[coreboot-gerrit] New patch to review for coreboot: 85b3fc7 qemu: load acpi tables from fw_cfg.

Gerd Hoffmann (kraxel@redhat.com) gerrit at coreboot.org
Tue Nov 12 10:22:43 CET 2013


Gerd Hoffmann (kraxel at redhat.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/4040

-gerrit

commit 85b3fc7ce5419c3eae86537609c56f89c656db8e
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Jul 25 15:59:07 2013 +0200

    qemu: load acpi tables from fw_cfg.
    
    Starting with release 1.7 qemu provides acpi tables via fw_cfg.  Main
    advantage is that new (virtual) hardware which needs acpi support
    JustWorks[tm] without having to patch & update the firmware (seabios,
    coreboot, ...) accordingly.
    
    So if we find acpi tables in fw_cfg try loading them, otherwise fallback
    to the builtin acpi tables.
    
    Change-Id: I792232829b870ff6ed8414a3007e0af17f6c4223
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
---
 src/mainboard/emulation/qemu-i440fx/acpi_tables.c |   6 +
 src/mainboard/emulation/qemu-i440fx/fw_cfg.c      | 187 ++++++++++++++++++++++
 src/mainboard/emulation/qemu-i440fx/fw_cfg.h      |   1 +
 src/mainboard/emulation/qemu-q35/acpi_tables.c    |   6 +
 4 files changed, 200 insertions(+)

diff --git a/src/mainboard/emulation/qemu-i440fx/acpi_tables.c b/src/mainboard/emulation/qemu-i440fx/acpi_tables.c
index 904fe0f..8be187a 100644
--- a/src/mainboard/emulation/qemu-i440fx/acpi_tables.c
+++ b/src/mainboard/emulation/qemu-i440fx/acpi_tables.c
@@ -29,6 +29,8 @@
 #include <device/pci_ids.h>
 #include <cpu/x86/msr.h>
 
+#include "fw_cfg.h"
+
 extern const unsigned char AmlCode[];
 #if CONFIG_HAVE_ACPI_SLIC
 unsigned long acpi_create_slic(unsigned long current);
@@ -88,6 +90,10 @@ unsigned long write_acpi_tables(unsigned long start)
 	acpi_header_t *ssdt;
 	acpi_header_t *dsdt;
 
+	current = fw_cfg_acpi_tables(start);
+	if (current)
+		return current;
+
 	current = start;
 
 	/* Align ACPI tables to 16byte */
diff --git a/src/mainboard/emulation/qemu-i440fx/fw_cfg.c b/src/mainboard/emulation/qemu-i440fx/fw_cfg.c
index b85a163..03b8dfa 100644
--- a/src/mainboard/emulation/qemu-i440fx/fw_cfg.c
+++ b/src/mainboard/emulation/qemu-i440fx/fw_cfg.c
@@ -19,6 +19,7 @@
 #include <swab.h>
 #include <console/console.h>
 #include <arch/io.h>
+#include <arch/acpigen.h>
 
 #include "fw_cfg.h"
 #include "fw_cfg_if.h"
@@ -116,3 +117,189 @@ int fw_cfg_max_cpus(void)
 	fw_cfg_get(FW_CFG_MAX_CPUS, &max_cpus, sizeof(max_cpus));
 	return max_cpus;
 }
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Starting with release 1.7 qemu provides acpi tables via fw_cfg.
+ * Main advantage is that new (virtual) hardware which needs acpi
+ * support JustWorks[tm] without having to patch & update the firmware
+ * (seabios, coreboot, ...) accordingly.
+ *
+ * Qemu provides a etc/table-loader file with instructions for the
+ * firmware:
+ *   - A "load" instruction to fetch acpi data from fw_cfg.
+ *   - A "pointer" instruction to patch a pointer.  This is needed to
+ *     get table-to-table references right, it is basically a
+ *     primitive dynamic linker for acpi tables.
+ *   - A "checksum" instruction to generate acpi table checksums.
+ *
+ * If a etc/table-loader file is found we'll go try loading the acpi
+ * tables from fw_cfg, otherwise we'll fallback to the acpi tables
+ * compiled in.
+ */
+
+#define BIOS_LINKER_LOADER_FILESZ 56
+
+struct BiosLinkerLoaderEntry {
+    uint32_t command;
+    union {
+        /*
+         * COMMAND_ALLOCATE - allocate a table from @alloc.file
+         * subject to @alloc.align alignment (must be power of 2)
+         * and @alloc.zone (can be HIGH or FSEG) requirements.
+         *
+         * Must appear exactly once for each file, and before
+         * this file is referenced by any other command.
+         */
+        struct {
+            char file[BIOS_LINKER_LOADER_FILESZ];
+            uint32_t align;
+            uint8_t zone;
+        } alloc;
+
+        /*
+         * COMMAND_ADD_POINTER - patch the table (originating from
+         * @dest_file) at @pointer.offset, by adding a pointer to the table
+         * originating from @src_file. 1,2,4 or 8 byte unsigned
+         * addition is used depending on @pointer.size.
+         */
+        struct {
+            char dest_file[BIOS_LINKER_LOADER_FILESZ];
+            char src_file[BIOS_LINKER_LOADER_FILESZ];
+            uint32_t offset;
+            uint8_t size;
+        } pointer;
+
+        /*
+         * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by
+         * @cksum_start and @cksum_length fields,
+         * and then add the value at @cksum.offset.
+         * Checksum simply sums -X for each byte X in the range
+         * using 8-bit math.
+         */
+        struct {
+            char file[BIOS_LINKER_LOADER_FILESZ];
+            uint32_t offset;
+            uint32_t start;
+            uint32_t length;
+        } cksum;
+
+        /* padding */
+        char pad[124];
+    };
+} __attribute__((packed));
+typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
+
+enum {
+    BIOS_LINKER_LOADER_COMMAND_ALLOCATE     = 0x1,
+    BIOS_LINKER_LOADER_COMMAND_ADD_POINTER  = 0x2,
+    BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3,
+};
+
+enum {
+    BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1,
+    BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2,
+};
+
+unsigned long fw_cfg_acpi_tables(unsigned long start)
+{
+	BiosLinkerLoaderEntry *s;
+	unsigned long *addrs, current;
+	uint32_t *ptr4;
+	uint64_t *ptr8;
+	int rc, i, j, src, dst, max;
+
+	rc = fw_cfg_check_file("etc/table-loader");
+	if (rc < 0)
+		return 0;
+
+	printk(BIOS_DEBUG, "QEMU: found acpi tables in fw_cfg.\n");
+
+	max = rc / sizeof(*s);
+	s = malloc(rc);
+	addrs = malloc(max * sizeof(*addrs));
+	fw_cfg_load_file("etc/table-loader", s);
+
+	current = start;
+	for (i = 0; i < max && s[i].command != 0; i++) {
+		switch (s[i].command) {
+		case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
+			current += (s[i].alloc.align-1);
+			current &= ~(s[i].alloc.align-1);
+			printk(BIOS_DEBUG, "QEMU: loading \"%s\" to 0x%lx\n",
+			       s[i].alloc.file, current);
+
+			rc = fw_cfg_check_file(s[i].alloc.file);
+			if (rc < 0)
+				goto err;
+			fw_cfg_load_file(s[i].alloc.file, (void*)current);
+			addrs[i] = current;
+			current += rc;
+			break;
+
+		case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
+			src = -1; dst = -1;
+			for (j = 0; j < i; j++) {
+				if (s[j].command != BIOS_LINKER_LOADER_COMMAND_ALLOCATE)
+					continue;
+				if (strcmp(s[j].alloc.file, s[i].pointer.dest_file) == 0)
+					dst = j;
+				if (strcmp(s[j].alloc.file, s[i].pointer.src_file) == 0)
+					src = j;
+			}
+			if (src == -1 || dst == -1)
+				goto err;
+
+			switch (s[i].pointer.size) {
+			case 4:
+				ptr4 = (uint32_t*)(addrs[dst]+s[i].pointer.offset);
+				*ptr4 += addrs[src];
+				break;
+
+			case 8:
+				ptr8 = (uint64_t*)(addrs[dst]+s[i].pointer.offset);
+				*ptr8 += addrs[src];
+				break;
+
+			default:
+				printk(BIOS_DEBUG, "QEMU: acpi: FIXME: ptr size %d\n",
+				       s[i].pointer.size);
+				goto err;
+			}
+			break;
+
+		case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
+			dst = -1;
+			for (j = 0; j < i; j++) {
+				if (s[j].command != BIOS_LINKER_LOADER_COMMAND_ALLOCATE)
+					continue;
+				if (strcmp(s[j].alloc.file, s[i].cksum.file) == 0)
+					dst = j;
+			}
+			if (dst == -1)
+				goto err;
+
+			ptr4 = (uint32_t*)(addrs[dst]+s[i].cksum.offset);
+			*ptr4 = 0;
+			*ptr4 = acpi_checksum((void *)(addrs[dst]+s[i].cksum.start),
+					      s[i].cksum.length);
+			break;
+
+		default:
+			printk(BIOS_DEBUG, "QEMU: acpi: unknown script cmd 0x%x @ %p\n",
+			       s[i].command, s+i);
+			goto err;
+		};
+	}
+
+	printk(BIOS_DEBUG, "QEMU: loaded acpi tables from fw_cfg.\n");
+	free(s);
+	free(addrs);
+	return ALIGN(current, 16);
+
+err:
+	printk(BIOS_DEBUG, "QEMU: loading acpi tables from fw_cfg failed.\n");
+	free(s);
+	free(addrs);
+	return 0;
+}
diff --git a/src/mainboard/emulation/qemu-i440fx/fw_cfg.h b/src/mainboard/emulation/qemu-i440fx/fw_cfg.h
index 2a10d8b..5ab024f 100644
--- a/src/mainboard/emulation/qemu-i440fx/fw_cfg.h
+++ b/src/mainboard/emulation/qemu-i440fx/fw_cfg.h
@@ -19,3 +19,4 @@ void fw_cfg_get(int entry, void *dst, int dstlen);
 int fw_cfg_check_file(const char *name);
 void fw_cfg_load_file(const char *name, void *dst);
 int fw_cfg_max_cpus(void);
+unsigned long fw_cfg_acpi_tables(unsigned long start);
diff --git a/src/mainboard/emulation/qemu-q35/acpi_tables.c b/src/mainboard/emulation/qemu-q35/acpi_tables.c
index d894dc1..4e79b2c 100644
--- a/src/mainboard/emulation/qemu-q35/acpi_tables.c
+++ b/src/mainboard/emulation/qemu-q35/acpi_tables.c
@@ -29,6 +29,8 @@
 #include <device/pci_ids.h>
 #include <cpu/x86/msr.h>
 
+#include "../qemu-i440fx/fw_cfg.h"
+
 extern const unsigned char AmlCode[];
 #if CONFIG_HAVE_ACPI_SLIC
 unsigned long acpi_create_slic(unsigned long current);
@@ -238,6 +240,10 @@ unsigned long write_acpi_tables(unsigned long start)
 	acpi_header_t *ssdt;
 	acpi_header_t *dsdt;
 
+	current = fw_cfg_acpi_tables(start);
+	if (current)
+		return current;
+
 	current = start;
 
 	/* Align ACPI tables to 16byte */



More information about the coreboot-gerrit mailing list