the chipset will deny any access to addresses outside the defined regions and these registers are RO even if the configuration is not locked down. this usually indicates a broken BIOS/flash descriptor and can only mitigated by not accessing addresses outside the regions with a layout file.
the implementation is not for merge. most of the routines should probably be moved to layout.c or similar and integrated with a generic layout entry data structure (think of a struct that is able to hold all information needed to define one line of a (future) layout file i.e. address range, name, access rights, lock status etc.).
Signed-off-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at --- ichspi.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 136 insertions(+), 0 deletions(-)
diff --git a/ichspi.c b/ichspi.c index e296551..63c6f17 100644 --- a/ichspi.c +++ b/ichspi.c @@ -1397,6 +1397,118 @@ static int ich_spi_send_multicommand(struct spi_command *cmds) #define ICH_BRWA(x) ((x >> 8) & 0xff) #define ICH_BRRA(x) ((x >> 0) & 0xff)
+struct range { + uint32_t offset; + uint32_t size; + struct range *next; +}; + +static void prettyprint_range_list(struct range *cur) +{ + unsigned int i = 0; + while (cur != NULL) { + msg_pspew("#%d: offset=0x%08x, size=0x%08x\n", + i++, cur->offset, cur->size); + cur = cur->next; + } +} + +/* Frees every element in the list */ +static void free_range_list(struct range *cur) +{ + struct range *next; + while (cur != NULL) { + next = cur->next; + free(cur); + cur = next; + } +} + +/* Unifies a *sorted* list of ranges. This means that overlapping regions are + * combined to a single region. The given list r is modifed and returned. + */ +static struct range *unify_range_list(struct range *const r) +{ + struct range *cur = r; + struct range *next; + while (cur != NULL && cur->next != NULL) { + next = cur->next; + if (cur->offset + cur->size >= next->offset) { + cur->size = next->offset + next->size - cur->offset; + cur->next = next->next; + free(next); + } else + cur = cur->next; + } + return r; +} + +/* Unifies two *sorted* lists of ranges into a third one. + * Returns the newly allocated third list, or NULL if inputs are empty lists or + * an error occurred. + */ +static struct range *unify_range_lists(struct range *const r1, struct range *const r2) +{ + struct range *cur = NULL; + struct range *new = NULL; + struct range *cur1 = r1; + struct range *cur2 = r2; + + /* first, iterate over all entries from the first range. + * compare them to the lowest, unprocessed element of the second one + * and copy the one with the smaller offset over to the new list. + * this ensures that the new list is sorted all the time. + */ + while (cur1 != NULL) { + /* first element of the new list handled as special case */ + if (cur == NULL) { + cur = malloc(sizeof(struct range)); + new = cur; + } else { + cur->next = malloc(sizeof(struct range)); + cur = cur->next; + } + if (cur == NULL) { + free_range_list(new); + return NULL; + } + cur->next = NULL; + if (cur2 == NULL || cur1->offset < cur2->offset) { + cur->offset = cur1->offset; + cur->size = cur1->size; + cur1 = cur1->next; + } else { + cur->offset = cur2->offset; + cur->size = cur2->size; + cur2 = cur2->next; + } + } + + /* then copy over the remaining elements from the second list */ + while (cur2 != NULL) { + /* first element of the new list handled as special case */ + if (cur == NULL) { + cur = malloc(sizeof(struct range)); + new = cur; + } else { + cur->next = malloc(sizeof(struct range)); + cur = cur->next; + } + if (cur == NULL) { + free_range_list(new); + return NULL; + } + cur->next = NULL; + cur->offset = cur2->offset; + cur->size = cur2->size; + cur2 = cur2->next; + } + + return unify_range_list(new); +} + +static struct range *ich_ranges = NULL; + static void do_ich9_spi_frap(uint32_t frap, int i) { static const char *const access_names[4] = { @@ -1407,6 +1519,7 @@ static void do_ich9_spi_frap(uint32_t frap, int i) "Gigabit Ethernet", "Platform Data" }; uint32_t base, limit; + struct range cur; int rwperms = (((ICH_BRWA(frap) >> i) & 1) << 1) | (((ICH_BRRA(frap) >> i) & 1) << 0); int offset = ICH9_REG_FREG0 + i * 4; @@ -1423,6 +1536,11 @@ static void do_ich9_spi_frap(uint32_t frap, int i) return; }
+ cur.size = (limit | 0x0fff) + 1 - base; + cur.offset = base; + cur.next = NULL; + + ich_ranges = unify_range_lists(ich_ranges, &cur); msg_pdbg("0x%08x-0x%08x is %s\n", base, (limit | 0x0fff), access_names[rwperms]); } @@ -1515,6 +1633,7 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, int desc_valid = 0; int hwseq_en = 0; struct ich_descriptors desc = {{ 0 }}; + uint32_t flash_size;
switch (ich_generation) { case 7: @@ -1625,6 +1744,7 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, /* Decode and print FREGx and FRAP registers */ for (i = 0; i < 5; i++) do_ich9_spi_frap(tmp, i); + prettyprint_range_list(ich_ranges); }
/* try to disable PR locks before printing them */ @@ -1705,11 +1825,27 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, } hwseq.size_comp0 = getFCBA_component_density(&desc, 0); hwseq.size_comp1 = getFCBA_component_density(&desc, 1); + flash_size = hwseq.size_comp0 + hwseq.size_comp1; register_opaque_programmer(&opaque_programmer_ich_hwseq); } else { + flash_size = getFCBA_component_density(&desc, 0); register_spi_programmer(&spi_programmer_ich9); ich_init_opcodes(); } + if (ich_ranges != NULL) + if (ich_ranges->next != NULL || + ich_ranges->offset != 0 || + ich_ranges->size != flash_size) + msg_pinfo("WARNING: The regions defined by the " + "FREG registers do not cover the " + "whole flash\naddress space. The " + "chipset will deny access to any " + "address outside the defined\n" + "regions. Please use a layout file " + "to mitigate this and send us a log " + "with maximum\nverbosity (-VVV) to " + "flashrom@flashrom.org, thanks!\n\n"); + break; }