[PATCH 4/5] ichspi: warn if regions defined by FREGs do not cover the whole flash space

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; } -- 1.7.1
participants (1)
-
Stefan Tauner