Commit fa287b158099b5b2159ecbbcc4acb2d5dc6714cf

Authored by Miao Yan
Committed by Bin Meng
1 parent a3b15a0556

x86: qemu: add the ability to load and link ACPI tables from QEMU

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>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Tested-by: Bin Meng <bmeng.cn@gmail.com>

Showing 5 changed files with 322 additions and 0 deletions Side-by-side Diff

arch/x86/cpu/qemu/Makefile
... ... @@ -8,5 +8,7 @@
8 8 obj-y += car.o dram.o
9 9 endif
10 10 obj-y += cpu.o fw_cfg.o qemu.o
  11 +ifndef CONFIG_QEMU_ACPI_TABLE
11 12 obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o dsdt.o
  13 +endif
arch/x86/cpu/qemu/fw_cfg.c
... ... @@ -10,7 +10,10 @@
10 10 #include <malloc.h>
11 11 #include <asm/io.h>
12 12 #include <asm/fw_cfg.h>
  13 +#include <asm/tables.h>
  14 +#include <asm/e820.h>
13 15 #include <linux/list.h>
  16 +#include <memalign.h>
14 17  
15 18 static bool fwcfg_present;
16 19 static bool fwcfg_dma_present;
... ... @@ -203,6 +206,256 @@
203 206  
204 207 return -ENOMEM;
205 208 }
  209 +
  210 +#ifdef CONFIG_QEMU_ACPI_TABLE
  211 +static struct fw_file *qemu_fwcfg_find_file(const char *name)
  212 +{
  213 + struct list_head *entry;
  214 + struct fw_file *file;
  215 +
  216 + list_for_each(entry, &fw_list) {
  217 + file = list_entry(entry, struct fw_file, list);
  218 + if (!strcmp(file->cfg.name, name))
  219 + return file;
  220 + }
  221 +
  222 + return NULL;
  223 +}
  224 +
  225 +/*
  226 + * This function allocates memory for ACPI tables
  227 + *
  228 + * @entry : BIOS linker command entry which tells where to allocate memory
  229 + * (either high memory or low memory)
  230 + * @addr : The address that should be used for low memory allcation. If the
  231 + * memory allocation request is 'ZONE_HIGH' then this parameter will
  232 + * be ignored.
  233 + * @return: 0 on success, or negative value on failure
  234 + */
  235 +static int bios_linker_allocate(struct bios_linker_entry *entry,
  236 + unsigned long *addr)
  237 +{
  238 + uint32_t size, align;
  239 + struct fw_file *file;
  240 + unsigned long aligned_addr;
  241 +
  242 + align = le32_to_cpu(entry->alloc.align);
  243 + /* align must be power of 2 */
  244 + if (align & (align - 1)) {
  245 + printf("error: wrong alignment %u\n", align);
  246 + return -EINVAL;
  247 + }
  248 +
  249 + file = qemu_fwcfg_find_file(entry->alloc.file);
  250 + if (!file) {
  251 + printf("error: can't find file %s\n", entry->alloc.file);
  252 + return -ENOENT;
  253 + }
  254 +
  255 + size = be32_to_cpu(file->cfg.size);
  256 +
  257 + /*
  258 + * ZONE_HIGH means we need to allocate from high memory, since
  259 + * malloc space is already at the end of RAM, so we directly use it.
  260 + * If allocation zone is ZONE_FSEG, then we use the 'addr' passed
  261 + * in which is low memory
  262 + */
  263 + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) {
  264 + aligned_addr = (unsigned long)memalign(align, size);
  265 + if (!aligned_addr) {
  266 + printf("error: allocating resource\n");
  267 + return -ENOMEM;
  268 + }
  269 + } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) {
  270 + aligned_addr = ALIGN(*addr, align);
  271 + } else {
  272 + printf("error: invalid allocation zone\n");
  273 + return -EINVAL;
  274 + }
  275 +
  276 + debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n",
  277 + file->cfg.name, size, entry->alloc.zone, align, aligned_addr);
  278 +
  279 + qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select),
  280 + size, (void *)aligned_addr);
  281 + file->addr = aligned_addr;
  282 +
  283 + /* adjust address for low memory allocation */
  284 + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG)
  285 + *addr = (aligned_addr + size);
  286 +
  287 + return 0;
  288 +}
  289 +
  290 +/*
  291 + * This function patches ACPI tables previously loaded
  292 + * by bios_linker_allocate()
  293 + *
  294 + * @entry : BIOS linker command entry which tells how to patch
  295 + * ACPI tables
  296 + * @return: 0 on success, or negative value on failure
  297 + */
  298 +static int bios_linker_add_pointer(struct bios_linker_entry *entry)
  299 +{
  300 + struct fw_file *dest, *src;
  301 + uint32_t offset = le32_to_cpu(entry->pointer.offset);
  302 + uint64_t pointer = 0;
  303 +
  304 + dest = qemu_fwcfg_find_file(entry->pointer.dest_file);
  305 + if (!dest || !dest->addr)
  306 + return -ENOENT;
  307 + src = qemu_fwcfg_find_file(entry->pointer.src_file);
  308 + if (!src || !src->addr)
  309 + return -ENOENT;
  310 +
  311 + debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n",
  312 + dest->addr, src->addr, offset, entry->pointer.size, pointer);
  313 +
  314 + memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size);
  315 + pointer = le64_to_cpu(pointer);
  316 + pointer += (unsigned long)src->addr;
  317 + pointer = cpu_to_le64(pointer);
  318 + memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size);
  319 +
  320 + return 0;
  321 +}
  322 +
  323 +/*
  324 + * This function updates checksum fields of ACPI tables previously loaded
  325 + * by bios_linker_allocate()
  326 + *
  327 + * @entry : BIOS linker command entry which tells where to update ACPI table
  328 + * checksums
  329 + * @return: 0 on success, or negative value on failure
  330 + */
  331 +static int bios_linker_add_checksum(struct bios_linker_entry *entry)
  332 +{
  333 + struct fw_file *file;
  334 + uint8_t *data, cksum = 0;
  335 + uint8_t *cksum_start;
  336 +
  337 + file = qemu_fwcfg_find_file(entry->cksum.file);
  338 + if (!file || !file->addr)
  339 + return -ENOENT;
  340 +
  341 + data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset));
  342 + cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start));
  343 + cksum = table_compute_checksum(cksum_start,
  344 + le32_to_cpu(entry->cksum.length));
  345 + *data = cksum;
  346 +
  347 + return 0;
  348 +}
  349 +
  350 +unsigned install_e820_map(unsigned max_entries, struct e820entry *entries)
  351 +{
  352 + entries[0].addr = 0;
  353 + entries[0].size = ISA_START_ADDRESS;
  354 + entries[0].type = E820_RAM;
  355 +
  356 + entries[1].addr = ISA_START_ADDRESS;
  357 + entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS;
  358 + entries[1].type = E820_RESERVED;
  359 +
  360 + /*
  361 + * since we use memalign(malloc) to allocate high memory for
  362 + * storing ACPI tables, we need to reserve them in e820 tables,
  363 + * otherwise kernel will reclaim them and data will be corrupted
  364 + */
  365 + entries[2].addr = ISA_END_ADDRESS;
  366 + entries[2].size = gd->relocaddr - TOTAL_MALLOC_LEN - ISA_END_ADDRESS;
  367 + entries[2].type = E820_RAM;
  368 +
  369 + /* for simplicity, reserve entire malloc space */
  370 + entries[3].addr = gd->relocaddr - TOTAL_MALLOC_LEN;
  371 + entries[3].size = TOTAL_MALLOC_LEN;
  372 + entries[3].type = E820_RESERVED;
  373 +
  374 + entries[4].addr = gd->relocaddr;
  375 + entries[4].size = gd->ram_size - gd->relocaddr;
  376 + entries[4].type = E820_RESERVED;
  377 +
  378 + entries[5].addr = CONFIG_PCIE_ECAM_BASE;
  379 + entries[5].size = CONFIG_PCIE_ECAM_SIZE;
  380 + entries[5].type = E820_RESERVED;
  381 +
  382 + return 6;
  383 +}
  384 +
  385 +/* This function loads and patches ACPI tables provided by QEMU */
  386 +unsigned long write_acpi_tables(unsigned long addr)
  387 +{
  388 + int i, ret = 0;
  389 + struct fw_file *file;
  390 + struct bios_linker_entry *table_loader;
  391 + struct bios_linker_entry *entry;
  392 + uint32_t size;
  393 + struct list_head *list;
  394 +
  395 + /* make sure fw_list is loaded */
  396 + ret = qemu_fwcfg_read_firmware_list();
  397 + if (ret) {
  398 + printf("error: can't read firmware file list\n");
  399 + return addr;
  400 + }
  401 +
  402 + file = qemu_fwcfg_find_file("etc/table-loader");
  403 + if (!file) {
  404 + printf("error: can't find etc/table-loader\n");
  405 + return addr;
  406 + }
  407 +
  408 + size = be32_to_cpu(file->cfg.size);
  409 + if ((size % sizeof(*entry)) != 0) {
  410 + printf("error: table-loader maybe corrupted\n");
  411 + return addr;
  412 + }
  413 +
  414 + table_loader = malloc(size);
  415 + if (!table_loader) {
  416 + printf("error: no memory for table-loader\n");
  417 + return addr;
  418 + }
  419 +
  420 + qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select),
  421 + size, table_loader);
  422 +
  423 + for (i = 0; i < (size / sizeof(*entry)); i++) {
  424 + entry = table_loader + i;
  425 + switch (le32_to_cpu(entry->command)) {
  426 + case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
  427 + ret = bios_linker_allocate(entry, &addr);
  428 + if (ret)
  429 + goto out;
  430 + break;
  431 + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
  432 + ret = bios_linker_add_pointer(entry);
  433 + if (ret)
  434 + goto out;
  435 + break;
  436 + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
  437 + ret = bios_linker_add_checksum(entry);
  438 + if (ret)
  439 + goto out;
  440 + break;
  441 + default:
  442 + break;
  443 + }
  444 + }
  445 +
  446 +out:
  447 + if (ret) {
  448 + list_for_each(list, &fw_list) {
  449 + file = list_entry(list, struct fw_file, list);
  450 + if (file->addr)
  451 + free((void *)file->addr);
  452 + }
  453 + }
  454 +
  455 + free(table_loader);
  456 + return addr;
  457 +}
  458 +#endif
206 459  
207 460 static int qemu_fwcfg_list_firmware(void)
208 461 {
arch/x86/include/asm/fw_cfg.h
... ... @@ -47,11 +47,23 @@
47 47 FW_CFG_INVALID = 0xffff,
48 48 };
49 49  
  50 +enum {
  51 + BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1,
  52 + BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2,
  53 + BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3,
  54 +};
  55 +
  56 +enum {
  57 + BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1,
  58 + BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2,
  59 +};
  60 +
50 61 #define FW_CFG_FILE_SLOTS 0x10
51 62 #define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS)
52 63 #define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
53 64  
54 65 #define FW_CFG_MAX_FILE_PATH 56
  66 +#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH
55 67  
56 68 #define QEMU_FW_CFG_SIGNATURE (('Q' << 24) | ('E' << 16) | ('M' << 8) | 'U')
57 69  
... ... @@ -80,6 +92,55 @@
80 92 __be32 length;
81 93 __be64 address;
82 94 };
  95 +
  96 +struct bios_linker_entry {
  97 + __le32 command;
  98 + union {
  99 + /*
  100 + * COMMAND_ALLOCATE - allocate a table from @alloc.file
  101 + * subject to @alloc.align alignment (must be power of 2)
  102 + * and @alloc.zone (can be HIGH or FSEG) requirements.
  103 + *
  104 + * Must appear exactly once for each file, and before
  105 + * this file is referenced by any other command.
  106 + */
  107 + struct {
  108 + char file[BIOS_LINKER_LOADER_FILESZ];
  109 + __le32 align;
  110 + uint8_t zone;
  111 + } alloc;
  112 +
  113 + /*
  114 + * COMMAND_ADD_POINTER - patch the table (originating from
  115 + * @dest_file) at @pointer.offset, by adding a pointer to the
  116 + * table originating from @src_file. 1,2,4 or 8 byte unsigned
  117 + * addition is used depending on @pointer.size.
  118 + */
  119 + struct {
  120 + char dest_file[BIOS_LINKER_LOADER_FILESZ];
  121 + char src_file[BIOS_LINKER_LOADER_FILESZ];
  122 + __le32 offset;
  123 + uint8_t size;
  124 + } pointer;
  125 +
  126 + /*
  127 + * COMMAND_ADD_CHECKSUM - calculate checksum of the range
  128 + * specified by @cksum_start and @cksum_length fields,
  129 + * and then add the value at @cksum.offset.
  130 + * Checksum simply sums -X for each byte X in the range
  131 + * using 8-bit math.
  132 + */
  133 + struct {
  134 + char file[BIOS_LINKER_LOADER_FILESZ];
  135 + __le32 offset;
  136 + __le32 start;
  137 + __le32 length;
  138 + } cksum;
  139 +
  140 + /* padding */
  141 + char pad[124];
  142 + };
  143 +} __packed;
83 144  
84 145 /**
85 146 * Initialize QEMU fw_cfg interface
arch/x86/lib/Makefile
... ... @@ -32,7 +32,9 @@
32 32 obj-y += sfi.o
33 33 obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += smbios.o
34 34 obj-y += string.o
  35 +ifndef CONFIG_QEMU_ACPI_TABLE
35 36 obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi_table.o
  37 +endif
36 38 obj-y += tables.o
37 39 obj-$(CONFIG_CMD_ZBOOT) += zimage.o
38 40 obj-$(CONFIG_HAVE_FSP) += fsp/
arch/x86/lib/acpi_table.c
... ... @@ -331,6 +331,10 @@
331 331 ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length);
332 332 }
333 333  
  334 +/*
  335 + * QEMU's version of write_acpi_tables is defined in
  336 + * arch/x86/cpu/qemu/fw_cfg.c
  337 + */
334 338 unsigned long write_acpi_tables(unsigned long start)
335 339 {
336 340 unsigned long current;