This patch adds "high coreboot table support" to coreboot version 2.

Some bootloaders seem to overwrite memory starting at 0x600, thus destroying
the coreboot table integrity, rendering the table useless.

By moving the table to the high tables area (if it's activated), this problem
is fixed.

In order to move the table, a 40 bytes mini coreboot table with a single sub
table is placed at 0x500/0x530 that points to the real coreboot table. This is
comparable to the ACPI RSDT or the MP floating table.

This patch also adds "table forward" support to flashrom and nvramtool.

Signed-off-by: Stefan Reinauer <stepan@coresystems.de>

Index: src/include/boot/coreboot_tables.h
===================================================================
--- src/include/boot/coreboot_tables.h	(revision 1304)
+++ src/include/boot/coreboot_tables.h	(working copy)
@@ -155,6 +155,13 @@
 	uint16_t type;
 };
 
+#define LB_TAG_FORWARD		0x0011
+struct lb_forward {
+	uint32_t tag;
+	uint32_t size;
+	uint64_t forward;
+};
+
 #define LB_TAG_CONSOLE_SERIAL8250	0
 #define LB_TAG_CONSOLE_VGA		1
 #define LB_TAG_CONSOLE_BTEXT		2
Index: src/arch/i386/boot/coreboot_table.c
===================================================================
--- src/arch/i386/boot/coreboot_table.c	(revision 1304)
+++ src/arch/i386/boot/coreboot_table.c	(working copy)
@@ -196,6 +196,18 @@
 
 }
 
+struct lb_forward *lb_forward(struct lb_header *header, struct lb_header *next_header)
+{
+	struct lb_record *rec;
+	struct lb_forward *forward;
+	rec = lb_new_record(header);
+	forward = (struct lb_forward *)rec;
+	forward->tag = LB_TAG_FORWARD;
+	forward->size = sizeof(*forward);
+	forward->forward = (uint64_t) next_header;
+	return forward;
+}
+
 void lb_memory_range(struct lb_memory *mem,
 	uint32_t type, uint64_t start, uint64_t size)
 {
@@ -239,14 +251,17 @@
 	}
 }
 
-unsigned long lb_table_fini(struct lb_header *head)
+static unsigned long lb_table_fini(struct lb_header *head, int fixup)
 {
 	struct lb_record *rec, *first_rec;
 	rec = lb_last_record(head);
 	if (head->table_entries) {
 		head->table_bytes += rec->size;
 	}
-	lb_reserve_table_memory(head);
+
+	if (fixup)
+		lb_reserve_table_memory(head);
+
 	first_rec = lb_first_record(head);
 	head->table_checksum = compute_ip_checksum(first_rec, head->table_bytes);
 	head->header_checksum = 0;
@@ -406,6 +421,22 @@
 	struct lb_header *head;
 	struct lb_memory *mem;
 
+#if HAVE_HIGH_TABLES == 1
+	printk_debug("Writing high table forward entry at 0x%08lx\n",
+			low_table_end);
+	head = lb_table_init(low_table_end);
+	lb_forward(head, rom_table_end);
+	lb_table_fini(head, 0);
+
+	low_table_end = (unsigned long)head;
+	printk_debug("New low_table_end: 0x%08lx\n", low_table_end);
+	printk_debug("Now going to write high coreboot table at 0x%08lx\n",
+			rom_table_end);
+	
+	head = lb_table_init(rom_table_end);
+	rom_table_end = (unsigned long)head;
+	printk_debug("rom_table_end = 0x%08lx\n", rom_table_end);
+#else
 	if(low_table_end > (0x1000 - sizeof(struct lb_header))) { /* after 4K */
 		/* We need to put lbtable on  to [0xf0000,0x100000) */
 		head = lb_table_init(rom_table_end);
@@ -414,6 +445,7 @@
 		head = lb_table_init(low_table_end);
 		low_table_end = (unsigned long)head;
 	}
+#endif
  
 	printk_debug("Adjust low_table_end from 0x%08lx to ", low_table_end);
 	low_table_end += 0xfff; // 4K aligned
@@ -469,6 +501,6 @@
 	lb_strings(head);
 
 	/* Remember where my valid memory ranges are */
-	return lb_table_fini(head);
+	return lb_table_fini(head, 1);
 	
 }
Index: src/arch/i386/boot/coreboot_table.h
===================================================================
--- src/arch/i386/boot/coreboot_table.h	(revision 1304)
+++ src/arch/i386/boot/coreboot_table.h	(working copy)
@@ -17,7 +17,6 @@
 void lb_memory_range(struct lb_memory *mem, 
 	uint32_t type, uint64_t start, uint64_t size);
 struct lb_mainboard *lb_mainboard(struct lb_header *header);
-unsigned long lb_table_fini(struct lb_header *header);
 
 /* Routines to extract part so the coreboot table or information
  * from the coreboot table.
Index: src/arch/i386/boot/tables.c
===================================================================
--- src/arch/i386/boot/tables.c	(revision 1304)
+++ src/arch/i386/boot/tables.c	(working copy)
@@ -75,7 +75,7 @@
 	unsigned long high_table_start, high_table_end=0;
 
 	if (high_tables_base) {
-		printk_debug("High Tables Base is %lx.\n", high_tables_base);
+		printk_debug("High Tables Base is %llx.\n", high_tables_base);
 		high_table_start = high_tables_base;
 		high_table_end = high_tables_base;
 	} else {
@@ -189,26 +189,19 @@
 				rom_table_start, rom_table_end);
 #endif
 
+#if HAVE_HIGH_TABLES == 1
+	if (high_tables_base) {
+		write_coreboot_table(low_table_start, low_table_end,
+				high_table_start, high_table_end);
+	} else {
+		printk_err("ERROR: No high_tables_base.\n");
+		write_coreboot_table(low_table_start, low_table_end,
+			      rom_table_start, rom_table_end);
+	}
+#else
 	/* The coreboot table must be in 0-4K or 960K-1M */
 	write_coreboot_table(low_table_start, low_table_end,
 			      rom_table_start, rom_table_end);
-
-#if 0 && HAVE_HIGH_TABLES == 1
-	/* This is currently broken and should be severely refactored. Ideally
-	 * we only have a pointer to the coreboot table in the low memory, so
-	 * anyone can find the real position.
-	 * write_coreboot_table does a lot more than just writing the coreboot
-	 * table. It magically decides where the table should go, and therefore
-	 * it consumes two base addresses. If we call write_coreboot_table like
-	 * below, we get weird effects.
-	 */
-	/* And we want another copy in high area because the low area might be
-	 * corrupted
-	 */
-	if (high_tables_base) {
-		write_coreboot_table(high_table_start, high_table_end,
-				      high_table_start, high_table_end);
-	}
 #endif
  
 	return get_lb_mem();
Index: util/nvramtool/lbtable.c
===================================================================
--- util/nvramtool/lbtable.c	(revision 1306)
+++ util/nvramtool/lbtable.c	(working copy)
@@ -149,6 +149,8 @@
 "%s: Item %s not found in coreboot table. Apparently, you are "
 "using coreboot v1.\n";
 
+int fd;
+
 /* This is the number of items from the coreboot table that may be displayed
  * using the -l option.
  */
@@ -240,6 +242,7 @@
  * /dev/mem.
  */
 static const void *low_phys_mem;
+static unsigned long low_phys_base = 0;
 
 /* Pointer to coreboot table. */
 static const struct lb_header *lbtable = NULL;
@@ -261,7 +264,7 @@
  * /dev/mem.  This macro converts 'vaddr' to a physical address.
  ****************************************************************************/
 #define vtophys(vaddr) (((unsigned long) vaddr) -       \
-                        ((unsigned long) low_phys_mem))
+                        ((unsigned long) low_phys_mem) + low_phys_base)
 
 /****************************************************************************
  * phystov
@@ -272,7 +275,7 @@
  * by calling mmap() on /dev/mem.
  ****************************************************************************/
 #define phystov(paddr) (((unsigned long) low_phys_mem) + \
-                        ((unsigned long) paddr))
+                        ((unsigned long) paddr) - low_phys_base)
 
 /****************************************************************************
  * get_lbtable
@@ -280,7 +283,7 @@
  * Find the coreboot table and set global variable lbtable to point to it.
  ****************************************************************************/
 void get_lbtable (void)
- { int fd, i, bad_header_count, bad_table_count, bad_headers, bad_tables;
+ { int i, bad_header_count, bad_table_count, bad_headers, bad_tables;
 
    if (lbtable != NULL)
       return;
@@ -494,6 +497,7 @@
                                               int *bad_table_count)
  { static const char signature[] = { 'L', 'B', 'I', 'O' };
    const struct lb_header *table;
+   const struct lb_forward *forward;
    const uint32_t *p;
    uint32_t sig;
 
@@ -534,6 +538,25 @@
        }
 
       /* checksums are ok: we found it! */
+      /* But it may just be a forwarding table, so look if there's a forwarder */
+      lbtable = table;
+      forward = (struct lb_forward *)find_lbrec(LB_TAG_FORWARD);
+      lbtable = NULL;
+
+      if (forward) {
+        uint64_t new_phys = forward->forward;
+
+	new_phys &= ~(getpagesize()-1);
+	
+        munmap((void *)low_phys_mem, BYTES_TO_MAP);
+        if ((low_phys_mem = mmap(NULL, BYTES_TO_MAP, PROT_READ, MAP_SHARED, fd, (off_t)new_phys)) == MAP_FAILED)
+        { fprintf(stderr, "%s: Failed to mmap /dev/mem: %s\n", prog_name,
+              strerror(errno));
+          exit(1);
+        }
+	low_phys_base = new_phys;
+	table = lbtable_scan(phystov(low_phys_base), phystov(low_phys_base + BYTES_TO_MAP), bad_header_count, bad_table_count);
+      }
       return table;
     }
 
@@ -865,6 +888,15 @@
       case LB_TAG_ASSEMBLER:
          return "ASSEMBLER";
 
+      case LB_TAG_SERIAL:
+         return "SERIAL";
+
+      case LB_TAG_CONSOLE:
+	 return "CONSOLE";
+
+      case LB_TAG_FORWARD:
+	 return "FORWARD";
+
       case LB_TAG_CMOS_OPTION_TABLE:
          return "CMOS_OPTION_TABLE";
 
Index: util/nvramtool/coreboot_tables.h
===================================================================
--- util/nvramtool/coreboot_tables.h	(revision 1306)
+++ util/nvramtool/coreboot_tables.h	(working copy)
@@ -146,6 +146,14 @@
         uint32_t size;
         uint8_t  string[0];
 };
+#define LB_TAG_SERIAL		0x000f
+#define LB_TAG_CONSOLE		0x0010
+#define LB_TAG_FORWARD		0x0011
+struct lb_forward {
+	uint32_t tag;
+	uint32_t size;
+	uint64_t forward;
+};
 
 /* The following structures are for the cmos definitions table */
 #define LB_TAG_CMOS_OPTION_TABLE 200
Index: util/flashrom/coreboot_tables.h
===================================================================
--- util/flashrom/coreboot_tables.h	(revision 1306)
+++ util/flashrom/coreboot_tables.h	(working copy)
@@ -135,6 +135,13 @@
 	uint8_t string[0];
 };
 
+#define LB_TAG_FORWARD		0x0011
+struct lb_forward {
+	uint32_t tag;
+	uint32_t size;
+	uint64_t forward;
+};
+
 /* The following structures are for the cmos definitions table */
 #define LB_TAG_CMOS_OPTION_TABLE 200
 /* cmos header record */
Index: util/flashrom/cbtable.c
===================================================================
--- util/flashrom/cbtable.c	(revision 1306)
+++ util/flashrom/cbtable.c	(working copy)
@@ -4,7 +4,7 @@
  * Copyright (C) 2002 Steven James <pyro@linuxlabs.com>
  * Copyright (C) 2002 Linux Networx
  * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx)
- * Copyright (C) 2006-2007 coresystems GmbH
+ * Copyright (C) 2006-2009 coresystems GmbH
  * (Written by Stefan Reinauer <stepan@coresystems.de> for coresystems GmbH)
  *
  * This program is free software; you can redistribute it and/or modify
@@ -124,7 +124,7 @@
 				head->table_checksum);
 			continue;
 		}
-		fprintf(stdout, "Found coreboot table at 0x%08lx.\n", addr);
+		printf_debug("Found coreboot table at 0x%08lx.\n", addr);
 		return head;
 
 	};
@@ -181,6 +181,7 @@
 	}
 }
 
+#define BYTES_TO_MAP (1024*1024)
 int coreboot_init(void)
 {
 	uint8_t *low_1MB;
@@ -196,18 +197,29 @@
 #else
 	start = 0x0;
 #endif
-	low_1MB = physmap("low megabyte", start, 1024*1024);
+	low_1MB = physmap("low megabyte", start, BYTES_TO_MAP);
 
 	lb_table = find_lb_table(low_1MB, 0x00000, 0x1000);
 	if (!lb_table)
 		lb_table = find_lb_table(low_1MB, 0xf0000, 1024*1024);
+	if (lb_table) {
+		struct lb_forward *forward = (struct lb_forward *)(((char *)lb_table) + lb_table->header_bytes);
+		if (forward->tag == LB_TAG_FORWARD) {
+			start = forward->forward;
+			start &= ~(getpagesize()-1);
+			physunmap(low_1MB, BYTES_TO_MAP);
+			low_1MB = physmap("high tables", start, BYTES_TO_MAP);
+			lb_table = find_lb_table(low_1MB, 0x00000, 0x1000);
+		}
+	}
+
 	if (!lb_table) {
 		printf("No coreboot table found.\n");
 		return -1;
 	}
 
 	addr = ((char *)lb_table) - ((char *)low_1MB) + start;
-	printf_debug("coreboot table found at %p.\n", lb_table + start);
+	fprintf(stdout, "coreboot table found at 0x%lx.\n", (unsigned long)lb_table - (unsigned long)low_1MB + start);
 	rec = (struct lb_record *)(((char *)lb_table) + lb_table->header_bytes);
 	last = (struct lb_record *)(((char *)rec) + lb_table->table_bytes);
 	printf_debug("coreboot header(%d) checksum: %04x table(%d) checksum: %04x entries: %d\n",
