Comments
Patch
@@ -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;
}
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(-)