cbtable.c: don't assume high addresses can fully map 1 MiB

Forward port the downstream `commit b17e9e41838`.

When using a forwarding table entry for finding the coreboot table
don't assume one has access to a full 1 MiB where the forwarding
table entry points to. The reason is that the 1 MiB may cover address
regions that have differing cacheability type. As such the kernel will
complain and the mapping will fail. Instead, check the header first then
map in the bytes that it indicates after sanity validation. That way
there is no attempt at requesting an invalid mapping that spans different
memory cacheability attributes.

V.2: Incorperate Nico's and Angels comments from upstream.

Tested: Can successfully run 'flashrom -p host --wp-status' on kahlee
     without generating PAT errors.

flashrom-stable:
* Squashed fixup
* Don't continue if re-mapping failed
* Restored still valid FIXME comment

Original-Change-Id: Ic6c5832b069300cced66e11f4ca4a0bbc6e496de
Original-Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/685608
Original-Reviewed-by: Martin Roth <martinroth@chromium.org>
Original-Reviewed-by: Justin TerAvest <teravest@chromium.org>

Change-Id: I43705c19dd7c816098d03f528bde6f180c4c8f24
Signed-off-by: Edward O'Callaghan <quasisec@chromium.org>
Original-Reviewed-on: https://review.coreboot.org/c/flashrom/+/37240
Original-Reviewed-by: Sam McNally <sammc@google.com>
Reviewed-on: https://review.coreboot.org/c/flashrom-stable/+/71339
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
diff --git a/cbtable.c b/cbtable.c
index 6185f12..5fd1ffe 100644
--- a/cbtable.c
+++ b/cbtable.c
@@ -210,6 +210,63 @@
 	return NULL;
 }
 
+static struct lb_header *find_lb_table_remap(unsigned long start_addr,
+					     uint8_t **table_area)
+{
+	size_t offset;
+	unsigned long end;
+	size_t mapping_size;
+	void *base;
+
+	mapping_size = getpagesize();
+	offset = start_addr % getpagesize();
+	start_addr -= offset;
+
+	base = physmap_ro("high tables", start_addr, mapping_size);
+	if (ERROR_PTR == base) {
+		msg_perr("Failed getting access to coreboot high tables.\n");
+		return NULL;
+	}
+
+	for (end = getpagesize(); offset < end; offset += 16) {
+		struct lb_record *recs;
+		struct lb_header *head;
+
+		/* No more headers to check. */
+		if (end - offset < sizeof(*head))
+			return NULL;
+
+		head = (struct lb_header *)(((char *)base) + offset);
+
+		if (!lb_header_valid(head, offset))
+			continue;
+
+		if (mapping_size - offset < head->table_bytes + sizeof(*head)) {
+			size_t prev_mapping_size = mapping_size;
+			mapping_size = head->table_bytes + sizeof(*head);
+			mapping_size += offset;
+			mapping_size += getpagesize() - (mapping_size % getpagesize());
+			physunmap(base, prev_mapping_size);
+			base = physmap_ro("high tables", start_addr, mapping_size);
+			if (ERROR_PTR == base) {
+				msg_perr("Failed getting access to coreboot high tables.\n");
+				return NULL;
+			}
+			head = (struct lb_header *)(((char *)base) + offset);
+		}
+
+		recs = (struct lb_record *)(((char *)base) + offset + sizeof(*head));
+		if (!lb_table_valid(head, recs))
+			continue;
+		msg_pdbg("Found coreboot table at 0x%08zx.\n", offset);
+		*table_area = base;
+		return head;
+	}
+
+	physunmap(base, mapping_size);
+	return NULL;
+}
+
 static void find_mainboard(struct lb_record *ptr, unsigned long addr)
 {
 	struct lb_mainboard *rec;
@@ -283,15 +340,9 @@
 			(((char *)lb_table) + lb_table->header_bytes);
 		if (forward->tag == LB_TAG_FORWARD) {
 			start = forward->forward;
-			start &= ~(getpagesize() - 1);
 			physunmap_unaligned(table_area, BYTES_TO_MAP);
 			// FIXME: table_area is never unmapped below, nor is it unmapped above in the no-forward case
-			table_area = physmap_ro_unaligned("high tables", start, BYTES_TO_MAP);
-			if (ERROR_PTR == table_area) {
-				msg_perr("Failed getting access to coreboot high tables.\n");
-				return -1;
-			}
-			lb_table = find_lb_table(table_area, 0x00000, 0x1000);
+			lb_table = find_lb_table_remap(start, &table_area);
 		}
 	}