[U-Boot] [PATCH v2 0/4] add support for loading ACPI tables from QEMU

Currently, if CONFIG_GENERATE_ACPI_TABLE is defined, U-Boot will generate ACPI tables itlself, this patchset adds the ability to load the ACPI tables generated by QEMU.
Changes in v2: - Drop [PATCH 4/4] x86: qemu: loading ACPI table from QEMU, add a config option CONFIG_QEMU_ACPI_TABLE - various cleanups
Miao Yan (4): x86: qemu: re-structure qemu_fwcfg_list_firmware() x86: qemu: setup PM IO base for ACPI in southbridge x86: config option for loading ACPI table from QEMU x86: qemu: add the ability to load and link ACPI tables from QEMU
arch/x86/Kconfig | 9 + arch/x86/cpu/qemu/Kconfig | 7 + arch/x86/cpu/qemu/Makefile | 2 + arch/x86/cpu/qemu/fw_cfg.c | 316 ++++++++++++++++++++++++++++++-- arch/x86/cpu/qemu/qemu.c | 29 +++ arch/x86/include/asm/arch-qemu/device.h | 2 + arch/x86/include/asm/arch-qemu/qemu.h | 5 + arch/x86/include/asm/fw_cfg.h | 70 ++++++- arch/x86/lib/Makefile | 2 + arch/x86/lib/acpi_table.c | 4 + 10 files changed, 429 insertions(+), 17 deletions(-)

Re-write the logic in qemu_fwcfg_list_firmware(), add a function qemu_fwcfg_read_firmware_list() to handle reading firmware list.
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- Changes in v2: - coding style fix - add comments in header file
arch/x86/cpu/qemu/fw_cfg.c | 63 +++++++++++++++++++++++++++++++++---------- arch/x86/include/asm/fw_cfg.h | 9 ++++--- 2 files changed, 55 insertions(+), 17 deletions(-)
diff --git a/arch/x86/cpu/qemu/fw_cfg.c b/arch/x86/cpu/qemu/fw_cfg.c index 0599214..bcd34af 100644 --- a/arch/x86/cpu/qemu/fw_cfg.c +++ b/arch/x86/cpu/qemu/fw_cfg.c @@ -10,10 +10,13 @@ #include <malloc.h> #include <asm/io.h> #include <asm/fw_cfg.h> +#include <linux/list.h>
static bool fwcfg_present; static bool fwcfg_dma_present;
+static LIST_HEAD(fw_list); + /* Read configuration item using fw_cfg PIO interface */ static void qemu_fwcfg_read_entry_pio(uint16_t entry, uint32_t size, void *address) @@ -162,29 +165,61 @@ static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) return 0; }
-static int qemu_fwcfg_list_firmware(void) +static int qemu_fwcfg_read_firmware_list(void) { int i; uint32_t count; - struct fw_cfg_files *files; + struct fw_file *file; + struct list_head *entry; + + /* don't read it twice */ + if (!list_empty(&fw_list)) + return 0;
qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count); if (!count) return 0;
count = be32_to_cpu(count); - files = malloc(count * sizeof(struct fw_cfg_file)); - if (!files) - return -ENOMEM; - - files->count = count; - qemu_fwcfg_read_entry(FW_CFG_INVALID, - count * sizeof(struct fw_cfg_file), - files->files); - - for (i = 0; i < files->count; i++) - printf("%-56s\n", files->files[i].name); - free(files); + for (i = 0; i < count; i++) { + file = malloc(sizeof(*file)); + if (!file) { + printf("error: allocating resource\n"); + goto err; + } + qemu_fwcfg_read_entry(FW_CFG_INVALID, + sizeof(struct fw_cfg_file), &file->cfg); + file->addr = 0; + list_add_tail(&file->list, &fw_list); + } + + return 0; + +err: + list_for_each(entry, &fw_list) { + file = list_entry(entry, struct fw_file, list); + free(file); + } + + return -ENOMEM; +} + +static int qemu_fwcfg_list_firmware(void) +{ + int ret; + struct list_head *entry; + struct fw_file *file; + + /* make sure fw_list is loaded */ + ret = qemu_fwcfg_read_firmware_list(); + if (ret) + return ret; + + list_for_each(entry, &fw_list) { + file = list_entry(entry, struct fw_file, list); + printf("%-56s\n", file->cfg.name); + } + return 0; }
diff --git a/arch/x86/include/asm/fw_cfg.h b/arch/x86/include/asm/fw_cfg.h index fb110fa..2acf43e 100644 --- a/arch/x86/include/asm/fw_cfg.h +++ b/arch/x86/include/asm/fw_cfg.h @@ -12,6 +12,8 @@ #define FW_DMA_PORT_LOW 0x514 #define FW_DMA_PORT_HIGH 0x518
+#include <linux/list.h> + enum qemu_fwcfg_items { FW_CFG_SIGNATURE = 0x00, FW_CFG_ID = 0x01, @@ -67,9 +69,10 @@ struct fw_cfg_file { char name[FW_CFG_MAX_FILE_PATH]; };
-struct fw_cfg_files { - __be32 count; - struct fw_cfg_file files[]; +struct fw_file { + struct fw_cfg_file cfg; /* firmware file information */ + unsigned long addr; /* firmware file in-memory address */ + struct list_head list; /* list node to link to fw_list */ };
struct fw_cfg_dma_access {

On Wed, Jan 20, 2016 at 12:24 PM, Miao Yan yanmiaobest@gmail.com wrote:
Re-write the logic in qemu_fwcfg_list_firmware(), add a function qemu_fwcfg_read_firmware_list() to handle reading firmware list.
Signed-off-by: Miao Yan yanmiaobest@gmail.com
Changes in v2:
- coding style fix
- add comments in header file
arch/x86/cpu/qemu/fw_cfg.c | 63 +++++++++++++++++++++++++++++++++---------- arch/x86/include/asm/fw_cfg.h | 9 ++++--- 2 files changed, 55 insertions(+), 17 deletions(-)
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com

Enable ACPI IO space for piix4 (for pc board) and ich9 (for q35 board)
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- Changes in v2: - add ACPI_PM1_BASE in Kconfig - drop PCI device ID checks
arch/x86/cpu/qemu/Kconfig | 7 +++++++ arch/x86/cpu/qemu/qemu.c | 29 +++++++++++++++++++++++++++++ arch/x86/include/asm/arch-qemu/device.h | 2 ++ arch/x86/include/asm/arch-qemu/qemu.h | 5 +++++ 4 files changed, 43 insertions(+)
diff --git a/arch/x86/cpu/qemu/Kconfig b/arch/x86/cpu/qemu/Kconfig index 4f98621..6808c9a 100644 --- a/arch/x86/cpu/qemu/Kconfig +++ b/arch/x86/cpu/qemu/Kconfig @@ -17,4 +17,11 @@ config SYS_CAR_SIZE hex default 0x10000
+config ACPI_PM1_BASE + hex + default 0xe400 + help + ACPI Power Managment 1 (PM1) i/o-mapped base address. + This device is defined in ACPI specification, with 16 bytes in size. + endif diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index 46111c9..b794cfe 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -15,6 +15,31 @@
static bool i440fx;
+static void enable_pm_piix(void) +{ + u8 en; + u16 cmd; + + /* Set the PM I/O base */ + x86_pci_write_config32(PIIX_PM, PMBA, CONFIG_ACPI_PM1_BASE | 1); + + /* Enable access to the PM I/O space */ + cmd = x86_pci_read_config16(PIIX_PM, PCI_COMMAND); + cmd |= PCI_COMMAND_IO; + x86_pci_write_config16(PIIX_PM, PCI_COMMAND, cmd); + + /* PM I/O Space Enable (PMIOSE) */ + en = x86_pci_read_config8(PIIX_PM, PMREGMISC); + en |= PMIOSE; + x86_pci_write_config8(PIIX_PM, PMREGMISC, en); +} + +static void enable_pm_ich9(void) +{ + /* Set the PM I/O base */ + x86_pci_write_config32(ICH9_PM, PMBA, CONFIG_ACPI_PM1_BASE | 1); +} + static void qemu_chipset_init(void) { u16 device, xbcs; @@ -53,10 +78,14 @@ static void qemu_chipset_init(void) xbcs = x86_pci_read_config16(PIIX_ISA, XBCS); xbcs |= APIC_EN; x86_pci_write_config16(PIIX_ISA, XBCS, xbcs); + + enable_pm_piix(); } else { /* Configure PCIe ECAM base address */ x86_pci_write_config32(PCI_BDF(0, 0, 0), PCIEX_BAR, CONFIG_PCIE_ECAM_BASE | BAR_EN); + + enable_pm_ich9(); }
qemu_fwcfg_init(); diff --git a/arch/x86/include/asm/arch-qemu/device.h b/arch/x86/include/asm/arch-qemu/device.h index 75a435e..38ab798 100644 --- a/arch/x86/include/asm/arch-qemu/device.h +++ b/arch/x86/include/asm/arch-qemu/device.h @@ -13,6 +13,8 @@ #define PIIX_ISA PCI_BDF(0, 1, 0) #define PIIX_IDE PCI_BDF(0, 1, 1) #define PIIX_USB PCI_BDF(0, 1, 2) +#define PIIX_PM PCI_BDF(0, 1, 3) +#define ICH9_PM PCI_BDF(0, 0x1f, 0) #define I440FX_VGA PCI_BDF(0, 2, 0)
#define QEMU_Q35 PCI_BDF(0, 0, 0) diff --git a/arch/x86/include/asm/arch-qemu/qemu.h b/arch/x86/include/asm/arch-qemu/qemu.h index b67d342..a85eee8 100644 --- a/arch/x86/include/asm/arch-qemu/qemu.h +++ b/arch/x86/include/asm/arch-qemu/qemu.h @@ -33,4 +33,9 @@ #define LOW_RAM_ADDR 0x34 #define HIGH_RAM_ADDR 0x35
+/* PM registers */ +#define PMBA 0x40 +#define PMREGMISC 0x80 +#define PMIOSE (1 << 0) + #endif /* _ARCH_QEMU_H_ */

On Wed, Jan 20, 2016 at 12:24 PM, Miao Yan yanmiaobest@gmail.com wrote:
Enable ACPI IO space for piix4 (for pc board) and ich9 (for q35 board)
Signed-off-by: Miao Yan yanmiaobest@gmail.com
Changes in v2:
- add ACPI_PM1_BASE in Kconfig
- drop PCI device ID checks
arch/x86/cpu/qemu/Kconfig | 7 +++++++ arch/x86/cpu/qemu/qemu.c | 29 +++++++++++++++++++++++++++++ arch/x86/include/asm/arch-qemu/device.h | 2 ++ arch/x86/include/asm/arch-qemu/qemu.h | 5 +++++ 4 files changed, 43 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com

This patch adds a config option for loading ACPI table from QEMU. When enabled, U-Boot won't generate ACPI tables, but use those provided by QEMU.
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- arch/x86/Kconfig | 9 +++++++++ arch/x86/cpu/qemu/Makefile | 2 ++ arch/x86/lib/Makefile | 2 ++ 3 files changed, 13 insertions(+)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f07567c..26c8d83 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -358,6 +358,15 @@ config GENERATE_ACPI_TABLE by the operating system. It defines platform-independent interfaces for configuration and power management monitoring.
+config QEMU_ACPI_TABLE + bool "load ACPI table from QEMU fw_cfg interface" + depends on GENERATE_ACPI_TABLE && QEMU + default y + help + By default, U-Boot generates its own ACPI tables. This option, if + enabled, disables U-Boot's version and loads ACPI tables generated + by QEMU. + config GENERATE_SMBIOS_TABLE bool "Generate an SMBIOS (System Management BIOS) table" default y diff --git a/arch/x86/cpu/qemu/Makefile b/arch/x86/cpu/qemu/Makefile index 176ea54..801413a 100644 --- a/arch/x86/cpu/qemu/Makefile +++ b/arch/x86/cpu/qemu/Makefile @@ -8,4 +8,6 @@ ifndef CONFIG_EFI_STUB obj-y += car.o dram.o endif obj-y += cpu.o fw_cfg.o qemu.o +ifndef CONFIG_QEMU_ACPI_TABLE obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o dsdt.o +endif diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index cd5ecb6..75719e3 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -32,7 +32,9 @@ obj-$(CONFIG_X86_RAMTEST) += ramtest.o obj-y += sfi.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += smbios.o obj-y += string.o +ifndef CONFIG_QEMU_ACPI_TABLE obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi_table.o +endif obj-y += tables.o obj-$(CONFIG_CMD_ZBOOT) += zimage.o obj-$(CONFIG_HAVE_FSP) += fsp/

On Wed, Jan 20, 2016 at 12:24 PM, Miao Yan yanmiaobest@gmail.com wrote:
This patch adds a config option for loading ACPI table from QEMU. When enabled, U-Boot won't generate ACPI tables, but use those provided by QEMU.
Signed-off-by: Miao Yan yanmiaobest@gmail.com
arch/x86/Kconfig | 9 +++++++++ arch/x86/cpu/qemu/Makefile | 2 ++ arch/x86/lib/Makefile | 2 ++ 3 files changed, 13 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
But please see one nits below:
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f07567c..26c8d83 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -358,6 +358,15 @@ config GENERATE_ACPI_TABLE by the operating system. It defines platform-independent interfaces for configuration and power management monitoring.
+config QEMU_ACPI_TABLE
bool "load ACPI table from QEMU fw_cfg interface"
nits: load -> Load
I can fix this when applying.
depends on GENERATE_ACPI_TABLE && QEMU
default y
help
By default, U-Boot generates its own ACPI tables. This option, if
enabled, disables U-Boot's version and loads ACPI tables generated
by QEMU.
[snip]
Regards, Bin

Hi Bin,
2016-01-20 16:46 GMT+08:00 Bin Meng bmeng.cn@gmail.com:
On Wed, Jan 20, 2016 at 12:24 PM, Miao Yan yanmiaobest@gmail.com wrote:
This patch adds a config option for loading ACPI table from QEMU. When enabled, U-Boot won't generate ACPI tables, but use those provided by QEMU.
Signed-off-by: Miao Yan yanmiaobest@gmail.com
arch/x86/Kconfig | 9 +++++++++ arch/x86/cpu/qemu/Makefile | 2 ++ arch/x86/lib/Makefile | 2 ++ 3 files changed, 13 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
But please see one nits below:
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f07567c..26c8d83 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -358,6 +358,15 @@ config GENERATE_ACPI_TABLE by the operating system. It defines platform-independent interfaces for configuration and power management monitoring.
+config QEMU_ACPI_TABLE
bool "load ACPI table from QEMU fw_cfg interface"
nits: load -> Load
I can fix this when applying.
Thanks, I just found this patch breaks git bisect, I'll submit v3 to fix this as well as the typo.
depends on GENERATE_ACPI_TABLE && QEMU
default y
help
By default, U-Boot generates its own ACPI tables. This option, if
enabled, disables U-Boot's version and loads ACPI tables generated
by QEMU.
[snip]
Regards, Bin

Hi Miao,
On Wed, Jan 20, 2016 at 5:15 PM, Miao Yan yanmiaobest@gmail.com wrote:
Hi Bin,
2016-01-20 16:46 GMT+08:00 Bin Meng bmeng.cn@gmail.com:
On Wed, Jan 20, 2016 at 12:24 PM, Miao Yan yanmiaobest@gmail.com wrote:
This patch adds a config option for loading ACPI table from QEMU. When enabled, U-Boot won't generate ACPI tables, but use those provided by QEMU.
Signed-off-by: Miao Yan yanmiaobest@gmail.com
arch/x86/Kconfig | 9 +++++++++ arch/x86/cpu/qemu/Makefile | 2 ++ arch/x86/lib/Makefile | 2 ++ 3 files changed, 13 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
But please see one nits below:
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f07567c..26c8d83 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -358,6 +358,15 @@ config GENERATE_ACPI_TABLE by the operating system. It defines platform-independent interfaces for configuration and power management monitoring.
+config QEMU_ACPI_TABLE
bool "load ACPI table from QEMU fw_cfg interface"
nits: load -> Load
I can fix this when applying.
Thanks, I just found this patch breaks git bisect, I'll submit v3 to fix this as well as the typo.
Uh, I've tested building your patches via buildman, no errors. What do you see?
depends on GENERATE_ACPI_TABLE && QEMU
default y
help
By default, U-Boot generates its own ACPI tables. This option, if
enabled, disables U-Boot's version and loads ACPI tables generated
by QEMU.
[snip]
Regards, Bin

Hi Bin,
2016-01-20 17:18 GMT+08:00 Bin Meng bmeng.cn@gmail.com:
Hi Miao,
On Wed, Jan 20, 2016 at 5:15 PM, Miao Yan yanmiaobest@gmail.com wrote:
Hi Bin,
2016-01-20 16:46 GMT+08:00 Bin Meng bmeng.cn@gmail.com:
On Wed, Jan 20, 2016 at 12:24 PM, Miao Yan yanmiaobest@gmail.com wrote:
This patch adds a config option for loading ACPI table from QEMU. When enabled, U-Boot won't generate ACPI tables, but use those provided by QEMU.
Signed-off-by: Miao Yan yanmiaobest@gmail.com
arch/x86/Kconfig | 9 +++++++++ arch/x86/cpu/qemu/Makefile | 2 ++ arch/x86/lib/Makefile | 2 ++ 3 files changed, 13 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
But please see one nits below:
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f07567c..26c8d83 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -358,6 +358,15 @@ config GENERATE_ACPI_TABLE by the operating system. It defines platform-independent interfaces for configuration and power management monitoring.
+config QEMU_ACPI_TABLE
bool "load ACPI table from QEMU fw_cfg interface"
nits: load -> Load
I can fix this when applying.
Thanks, I just found this patch breaks git bisect, I'll submit v3 to fix this as well as the typo.
Uh, I've tested building your patches via buildman, no errors. What do you see?
If you enable CONFIG_GENERATE_ACPI_TABLE and QEMU_ACPI_TABLE, you'll see:
arch/x86/lib/built-in.o: In function `write_tables': /home/myan/work/u-boot-x86/arch/x86/lib/tables.c:57: undefined reference to `write_acpi_tables' make: *** [u-boot] Error
Because write_acpi_tables() are defined in [PATCH 0/4]. With default config you won't see it. Is this OK ?
depends on GENERATE_ACPI_TABLE && QEMU
default y
help
By default, U-Boot generates its own ACPI tables. This option, if
enabled, disables U-Boot's version and loads ACPI tables generated
by QEMU.
[snip]
Regards, Bin

Hi Miao,
On Wed, Jan 20, 2016 at 5:26 PM, Miao Yan yanmiaobest@gmail.com wrote:
Hi Bin,
2016-01-20 17:18 GMT+08:00 Bin Meng bmeng.cn@gmail.com:
Hi Miao,
On Wed, Jan 20, 2016 at 5:15 PM, Miao Yan yanmiaobest@gmail.com wrote:
Hi Bin,
2016-01-20 16:46 GMT+08:00 Bin Meng bmeng.cn@gmail.com:
On Wed, Jan 20, 2016 at 12:24 PM, Miao Yan yanmiaobest@gmail.com wrote:
This patch adds a config option for loading ACPI table from QEMU. When enabled, U-Boot won't generate ACPI tables, but use those provided by QEMU.
Signed-off-by: Miao Yan yanmiaobest@gmail.com
arch/x86/Kconfig | 9 +++++++++ arch/x86/cpu/qemu/Makefile | 2 ++ arch/x86/lib/Makefile | 2 ++ 3 files changed, 13 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
But please see one nits below:
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f07567c..26c8d83 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -358,6 +358,15 @@ config GENERATE_ACPI_TABLE by the operating system. It defines platform-independent interfaces for configuration and power management monitoring.
+config QEMU_ACPI_TABLE
bool "load ACPI table from QEMU fw_cfg interface"
nits: load -> Load
I can fix this when applying.
Thanks, I just found this patch breaks git bisect, I'll submit v3 to fix this as well as the typo.
Uh, I've tested building your patches via buildman, no errors. What do you see?
If you enable CONFIG_GENERATE_ACPI_TABLE and QEMU_ACPI_TABLE, you'll see:
arch/x86/lib/built-in.o: In function `write_tables': /home/myan/work/u-boot-x86/arch/x86/lib/tables.c:57: undefined reference to `write_acpi_tables' make: *** [u-boot] Error
Because write_acpi_tables() are defined in [PATCH 0/4]. With default config you won't see it. Is this OK ?
Ah, yes! buildman only builds with default configuration so it is not exposed. So I assume you may defer the Makefile changes to patch#4. Thanks for pointing this out!
Regards, Bin

This patch adds the ability to load and link ACPI tables provided by QEMU. QEMU tells guests how to load and patch ACPI tables through its fw_cfg interface, by adding a firmware file 'etc/table-loader'. Guests are supposed to parse this file and execute corresponding QEMU commands.
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- Changes in v2: - add function comment - improve error handling
arch/x86/cpu/qemu/fw_cfg.c | 253 ++++++++++++++++++++++++++++++++++++++++++ arch/x86/include/asm/fw_cfg.h | 61 ++++++++++ arch/x86/lib/acpi_table.c | 4 + 3 files changed, 318 insertions(+)
diff --git a/arch/x86/cpu/qemu/fw_cfg.c b/arch/x86/cpu/qemu/fw_cfg.c index bcd34af..5ea7a6e 100644 --- a/arch/x86/cpu/qemu/fw_cfg.c +++ b/arch/x86/cpu/qemu/fw_cfg.c @@ -10,7 +10,10 @@ #include <malloc.h> #include <asm/io.h> #include <asm/fw_cfg.h> +#include <asm/tables.h> +#include <asm/e820.h> #include <linux/list.h> +#include <memalign.h>
static bool fwcfg_present; static bool fwcfg_dma_present; @@ -204,6 +207,256 @@ err: return -ENOMEM; }
+#ifdef CONFIG_QEMU_ACPI_TABLE +static struct fw_file *qemu_fwcfg_find_file(const char *name) +{ + struct list_head *entry; + struct fw_file *file; + + list_for_each(entry, &fw_list) { + file = list_entry(entry, struct fw_file, list); + if (!strcmp(file->cfg.name, name)) + return file; + } + + return NULL; +} + +/* + * This function allocates memory for ACPI tables + * + * @entry : BIOS linker command entry which tells where to allocate memory + * (either high memory or low memory) + * @addr : The address that should be used for low memory allcation. If the + * memory allocation request is 'ZONE_HIGH' then this parameter will + * be ignored. + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_allocate(struct bios_linker_entry *entry, + unsigned long *addr) +{ + uint32_t size, align; + struct fw_file *file; + unsigned long aligned_addr; + + align = le32_to_cpu(entry->alloc.align); + /* align must be power of 2 */ + if (align & (align - 1)) { + printf("error: wrong alignment %u\n", align); + return -EINVAL; + } + + file = qemu_fwcfg_find_file(entry->alloc.file); + if (!file) { + printf("error: can't find file %s\n", entry->alloc.file); + return -ENOENT; + } + + size = be32_to_cpu(file->cfg.size); + + /* + * ZONE_HIGH means we need to allocate from high memory, since + * malloc space is already at the end of RAM, so we directly use it. + * If allocation zone is ZONE_FSEG, then we use the 'addr' passed + * in which is low memory + */ + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { + aligned_addr = (unsigned long)memalign(align, size); + if (!aligned_addr) { + printf("error: allocating resource\n"); + return -ENOMEM; + } + } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) { + aligned_addr = ALIGN(*addr, align); + } else { + printf("error: invalid allocation zone\n"); + return -EINVAL; + } + + debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n", + file->cfg.name, size, entry->alloc.zone, align, aligned_addr); + + qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), + size, (void *)aligned_addr); + file->addr = aligned_addr; + + /* adjust address for low memory allocation */ + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) + *addr = (aligned_addr + size); + + return 0; +} + +/* + * This function patches ACPI tables previously loaded + * by bios_linker_allocate() + * + * @entry : BIOS linker command entry which tells how to patch + * ACPI tables + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_add_pointer(struct bios_linker_entry *entry) +{ + struct fw_file *dest, *src; + uint32_t offset = le32_to_cpu(entry->pointer.offset); + uint64_t pointer = 0; + + dest = qemu_fwcfg_find_file(entry->pointer.dest_file); + if (!dest || !dest->addr) + return -ENOENT; + src = qemu_fwcfg_find_file(entry->pointer.src_file); + if (!src || !src->addr) + return -ENOENT; + + debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n", + dest->addr, src->addr, offset, entry->pointer.size, pointer); + + memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size); + pointer = le64_to_cpu(pointer); + pointer += (unsigned long)src->addr; + pointer = cpu_to_le64(pointer); + memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size); + + return 0; +} + +/* + * This function updates checksum fields of ACPI tables previously loaded + * by bios_linker_allocate() + * + * @entry : BIOS linker command entry which tells where to update ACPI table + * checksums + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_add_checksum(struct bios_linker_entry *entry) +{ + struct fw_file *file; + uint8_t *data, cksum = 0; + uint8_t *cksum_start; + + file = qemu_fwcfg_find_file(entry->cksum.file); + if (!file || !file->addr) + return -ENOENT; + + data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset)); + cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start)); + cksum = table_compute_checksum(cksum_start, + le32_to_cpu(entry->cksum.length)); + *data = cksum; + + return 0; +} + +unsigned install_e820_map(unsigned max_entries, struct e820entry *entries) +{ + entries[0].addr = 0; + entries[0].size = ISA_START_ADDRESS; + entries[0].type = E820_RAM; + + entries[1].addr = ISA_START_ADDRESS; + entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS; + entries[1].type = E820_RESERVED; + + /* + * since we use memalign(malloc) to allocate high memory for + * storing ACPI tables, we need to reserve them in e820 tables, + * otherwise kernel will reclaim them and data will be corrupted + */ + entries[2].addr = ISA_END_ADDRESS; + entries[2].size = gd->relocaddr - TOTAL_MALLOC_LEN - ISA_END_ADDRESS; + entries[2].type = E820_RAM; + + /* for simplicity, reserve entire malloc space */ + entries[3].addr = gd->relocaddr - TOTAL_MALLOC_LEN; + entries[3].size = TOTAL_MALLOC_LEN; + entries[3].type = E820_RESERVED; + + entries[4].addr = gd->relocaddr; + entries[4].size = gd->ram_size - gd->relocaddr; + entries[4].type = E820_RESERVED; + + entries[5].addr = CONFIG_PCIE_ECAM_BASE; + entries[5].size = CONFIG_PCIE_ECAM_SIZE; + entries[5].type = E820_RESERVED; + + return 6; +} + +/* This function loads and patches ACPI tables provided by QEMU */ +unsigned long write_acpi_tables(unsigned long addr) +{ + int i, ret = 0; + struct fw_file *file; + struct bios_linker_entry *table_loader; + struct bios_linker_entry *entry; + uint32_t size; + struct list_head *list; + + /* make sure fw_list is loaded */ + ret = qemu_fwcfg_read_firmware_list(); + if (ret) { + printf("error: can't read firmware file list\n"); + return addr; + } + + file = qemu_fwcfg_find_file("etc/table-loader"); + if (!file) { + printf("error: can't find etc/table-loader\n"); + return addr; + } + + size = be32_to_cpu(file->cfg.size); + if ((size % sizeof(*entry)) != 0) { + printf("error: table-loader maybe corrupted\n"); + return addr; + } + + table_loader = malloc(size); + if (!table_loader) { + printf("error: no memory for table-loader\n"); + return addr; + } + + qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), + size, table_loader); + + for (i = 0; i < (size / sizeof(*entry)); i++) { + entry = table_loader + i; + switch (le32_to_cpu(entry->command)) { + case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: + ret = bios_linker_allocate(entry, &addr); + if (ret) + goto out; + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + ret = bios_linker_add_pointer(entry); + if (ret) + goto out; + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + ret = bios_linker_add_checksum(entry); + if (ret) + goto out; + break; + default: + break; + } + } + +out: + if (ret) { + list_for_each(list, &fw_list) { + file = list_entry(list, struct fw_file, list); + if (file->addr) + free((void *)file->addr); + } + } + + free(table_loader); + return addr; +} +#endif + static int qemu_fwcfg_list_firmware(void) { int ret; diff --git a/arch/x86/include/asm/fw_cfg.h b/arch/x86/include/asm/fw_cfg.h index 2acf43e..e9450c6 100644 --- a/arch/x86/include/asm/fw_cfg.h +++ b/arch/x86/include/asm/fw_cfg.h @@ -47,11 +47,23 @@ enum qemu_fwcfg_items { FW_CFG_INVALID = 0xffff, };
+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, +}; + #define FW_CFG_FILE_SLOTS 0x10 #define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS) #define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
#define FW_CFG_MAX_FILE_PATH 56 +#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH
#define QEMU_FW_CFG_SIGNATURE (('Q' << 24) | ('E' << 16) | ('M' << 8) | 'U')
@@ -81,6 +93,55 @@ struct fw_cfg_dma_access { __be64 address; };
+struct bios_linker_entry { + __le32 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]; + __le32 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]; + __le32 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]; + __le32 offset; + __le32 start; + __le32 length; + } cksum; + + /* padding */ + char pad[124]; + }; +} __packed; + /** * Initialize QEMU fw_cfg interface */ diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index ab1db7e..2ec5ad2 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -331,6 +331,10 @@ static void acpi_create_ssdt_generator(acpi_header_t *ssdt, ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length); }
+/* + * QEMU's version of write_acpi_tables is defined in + * arch/x86/cpu/qemu/fw_cfg.c + */ unsigned long write_acpi_tables(unsigned long start) { unsigned long current;

On Wed, Jan 20, 2016 at 12:24 PM, Miao Yan yanmiaobest@gmail.com wrote:
This patch adds the ability to load and link ACPI tables provided by QEMU. QEMU tells guests how to load and patch ACPI tables through its fw_cfg interface, by adding a firmware file 'etc/table-loader'. Guests are supposed to parse this file and execute corresponding QEMU commands.
Signed-off-by: Miao Yan yanmiaobest@gmail.com
Changes in v2:
- add function comment
- improve error handling
arch/x86/cpu/qemu/fw_cfg.c | 253 ++++++++++++++++++++++++++++++++++++++++++ arch/x86/include/asm/fw_cfg.h | 61 ++++++++++ arch/x86/lib/acpi_table.c | 4 + 3 files changed, 318 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
participants (2)
-
Bin Meng
-
Miao Yan