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@redhat.com> Reviewed-on: http://review.coreboot.org/4040 Tested-by: build bot (Jenkins)
This commit is contained in:
parent
55b059385a
commit
d69da8475e
|
@ -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 */
|
||||
|
|
|
@ -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,193 @@ 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 = ALIGN(current, s[i].alloc.align);
|
||||
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:
|
||||
/*
|
||||
* Should not happen. acpi knows 1 and 2 byte ptrs
|
||||
* too, but we are operating with 32bit offsets which
|
||||
* would simply not fit in there ...
|
||||
*/
|
||||
printk(BIOS_DEBUG, "QEMU: acpi: unimplemented 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue