This is the seabios code that adds support for loading acpi tables from QEMU.
Changes from v5: - address Kevin's comments: move code to find rsdp to biostables.c scan for RSDP at 0x10 intervals Changes from v4: - address Kevin's comments: move loader to src/fw/ simplify some code drop some unused code chunks load from ROM even if !ACPI rename option to make it non ACPI specific Changes from v3: - updated to latest bits - add option to disable loading from QEMU
Changes from v2: - addressed comments from Kevin: fixed coding style, minimized changes to existing code
Changes from v1: - simplified linker interfaces along the lines suggested by Kevin - fixed lots of bugs
Michael S. Tsirkin (3): biostables: support looking up RSDP romfile_loader: utility to patch in-memory ROM files acpi: load and link tables through romfile loader
Makefile | 2 +- src/fw/romfile_loader.h | 72 ++++++++++++++++++++ src/util.h | 1 + src/fw/acpi.c | 21 ++++++ src/fw/biostables.c | 40 +++++++++-- src/fw/romfile_loader.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++ src/Kconfig | 11 +++ 7 files changed, 316 insertions(+), 8 deletions(-) create mode 100644 src/fw/romfile_loader.h create mode 100644 src/fw/romfile_loader.c
Will be used when it's loaded from QEMU.
Signed-off-by: Michael S. Tsirkin mst@redhat.com --- src/util.h | 1 + src/fw/biostables.c | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/src/util.h b/src/util.h index 880c04a..bf983e5 100644 --- a/src/util.h +++ b/src/util.h @@ -72,6 +72,7 @@ void acpi_reboot(void); // fw/biostable.c void copy_smbios(void *pos); void copy_table(void *pos); +void *find_acpi_rsdp(void);
// fw/coreboot.c extern const char *CBvendor, *CBpart; diff --git a/src/fw/biostables.c b/src/fw/biostables.c index a3ee827..4c2f355 100644 --- a/src/fw/biostables.c +++ b/src/fw/biostables.c @@ -60,22 +60,34 @@ copy_mptable(void *pos) memcpy((void*)newpos + length, (void*)p->physaddr, mpclength); }
-static void -copy_acpi_rsdp(void *pos) +static bool +test_acpi_rsdp(void *pos, unsigned size) { - if (RsdpAddr) - return; struct rsdp_descriptor *p = pos; if (p->signature != RSDP_SIGNATURE) - return; + return false; u32 length = 20; + if (length > size) + return false; if (checksum(pos, length) != 0) - return; + return false; if (p->revision > 1) { length = p->length; + if (length > size) + return false; if (checksum(pos, length) != 0) - return; + return false; } + return true; +} + +static void +copy_acpi_rsdp(void *pos) +{ + if (RsdpAddr) + return; + if (!test_acpi_rsdp(pos, -1)) + return; void *newpos = malloc_fseg(length); if (!newpos) { warn_noalloc(); @@ -118,3 +130,17 @@ copy_table(void *pos) copy_acpi_rsdp(pos); copy_smbios(pos); } + +void *find_acpi_rsdp(void) +{ + extern u8 zonefseg_start[], zonefseg_end[]; + unsigned long start = (unsigned long)zonefseg_start; + unsigned long end = (unsigned long)zonefseg_end; + unsigned long pos; + + for (pos = ALIGN(start, 0x10); pos += 0x10; pos <= ALIGN_DOWN(end, 0x10)) + if (test_acpi_rsdp((void *)pos, end - pos)) + return (void *)pos; + + return NULL; +}
On So, 2013-09-29 at 08:57 +0300, Michael S. Tsirkin wrote:
Will be used when it's loaded from QEMU.
-static void -copy_acpi_rsdp(void *pos) +static bool +test_acpi_rsdp(void *pos, unsigned size)
Fails to build for me:
src/fw/biostables.c:64:1: error: unknown type name ‘bool’ test_acpi_rsdp(void *pos, unsigned size) ^
[ more errors follow ]
cheers, Gerd
On Mon, Sep 30, 2013 at 09:41:06AM +0200, Gerd Hoffmann wrote:
On So, 2013-09-29 at 08:57 +0300, Michael S. Tsirkin wrote:
Will be used when it's loaded from QEMU.
-static void -copy_acpi_rsdp(void *pos) +static bool +test_acpi_rsdp(void *pos, unsigned size)
Fails to build for me:
src/fw/biostables.c:64:1: error: unknown type name ‘bool’ test_acpi_rsdp(void *pos, unsigned size) ^
[ more errors follow ]
cheers, Gerd
Ouch, looks like a tested the wrong version. I'll fix that now.
Add ability for a ROM file to point to it's image in memory. When file is in memory, add utility that can patch it, storing pointers to one file within another file.
This is not a lot of code: together with the follow-up patch to load ACPI tables from ROM, it's about 1K extra.
Signed-off-by: Michael S. Tsirkin mst@redhat.com --- Makefile | 2 +- src/fw/romfile_loader.h | 72 ++++++++++++++++++++ src/fw/romfile_loader.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 src/fw/romfile_loader.h create mode 100644 src/fw/romfile_loader.c
diff --git a/Makefile b/Makefile index 3984d35..797c00f 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c pmm.c romfile.c optionroms.c \ hw/usb-hub.c \ fw/coreboot.c fw/lzmadecode.c fw/csm.c fw/biostables.c \ fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/mtrr.c fw/xen.c \ - fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c + fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c DIRS=src src/hw src/fw vgasrc
diff --git a/src/fw/romfile_loader.h b/src/fw/romfile_loader.h new file mode 100644 index 0000000..15eab2a --- /dev/null +++ b/src/fw/romfile_loader.h @@ -0,0 +1,72 @@ +#ifndef __ROMFILE_LOADER_H +#define __ROMFILE_LOADER_H + +#include "types.h" // u8 +#include "util.h" // romfile_s + +#define ROMFILE_LOADER_FILESZ 56 + +/* ROM file linker/loader interface. Linker uses little endian format */ +struct romfile_loader_entry_s { + u32 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 alloc_file[ROMFILE_LOADER_FILESZ]; + u32 alloc_align; + u8 alloc_zone; + }; + + /* + * 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 pointer_dest_file[ROMFILE_LOADER_FILESZ]; + char pointer_src_file[ROMFILE_LOADER_FILESZ]; + u32 pointer_offset; + u8 pointer_size; + }; + + /* + * 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 cksum_file[ROMFILE_LOADER_FILESZ]; + u32 cksum_offset; + u32 cksum_start; + u32 cksum_length; + }; + + /* padding */ + char pad[124]; + }; +}; + +enum { + ROMFILE_LOADER_COMMAND_ALLOCATE = 0x1, + ROMFILE_LOADER_COMMAND_ADD_POINTER = 0x2, + ROMFILE_LOADER_COMMAND_ADD_CHECKSUM = 0x3, +}; + +enum { + ROMFILE_LOADER_ALLOC_ZONE_HIGH = 0x1, + ROMFILE_LOADER_ALLOC_ZONE_FSEG = 0x2, +}; + +int romfile_loader_execute(const char *name); + +#endif diff --git a/src/fw/romfile_loader.c b/src/fw/romfile_loader.c new file mode 100644 index 0000000..325a0fa --- /dev/null +++ b/src/fw/romfile_loader.c @@ -0,0 +1,177 @@ +#include "romfile_loader.h" +#include "byteorder.h" // leXX_to_cpu/cpu_to_leXX +#include "util.h" // checksum +#include "string.h" // strcmp +#include "romfile.h" // struct romfile_s +#include "malloc.h" // Zone*, _malloc +#include "output.h" // warn_* + +struct romfile_loader_file { + struct romfile_s *file; + void *data; +}; +struct romfile_loader_files { + int nfiles; + struct romfile_loader_file files[]; +}; + +static struct romfile_loader_file * +romfile_loader_find(const char *name, + struct romfile_loader_files *files) +{ + int i; + if (name[ROMFILE_LOADER_FILESZ - 1]) + return NULL; + for (i = 0; i < files->nfiles; ++i) + if (!strcmp(files->files[i].file->name, name)) + return &files->files[i]; + return NULL; +} + +static void romfile_loader_allocate(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct zone_s *zone; + struct romfile_loader_file *file = &files->files[files->nfiles]; + void *data; + int ret; + unsigned alloc_align = le32_to_cpu(entry->alloc_align); + + if (alloc_align & (alloc_align - 1)) + goto err; + + switch (entry->alloc_zone) { + case ROMFILE_LOADER_ALLOC_ZONE_HIGH: + zone = &ZoneHigh; + break; + case ROMFILE_LOADER_ALLOC_ZONE_FSEG: + zone = &ZoneFSeg; + break; + default: + goto err; + } + if (alloc_align < MALLOC_MIN_ALIGN) + alloc_align = MALLOC_MIN_ALIGN; + if (entry->alloc_file[ROMFILE_LOADER_FILESZ - 1]) + goto err; + file->file = romfile_find(entry->alloc_file); + if (!file->file || !file->file->size) + return; + data = _malloc(zone, MALLOC_DEFAULT_HANDLE, file->file->size, alloc_align); + if (!data) { + warn_noalloc(); + return; + } + ret = file->file->copy(file->file, data, file->file->size); + if (ret != file->file->size) + goto file_err; + file->data = data; + files->nfiles++; + return; + +file_err: + free(data); +err: + warn_internalerror(); +} + +static void romfile_loader_add_pointer(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct romfile_loader_file *dest_file; + struct romfile_loader_file *src_file; + unsigned offset = le32_to_cpu(entry->pointer_offset); + u64 pointer = 0; + + dest_file = romfile_loader_find(entry->pointer_dest_file, files); + src_file = romfile_loader_find(entry->pointer_src_file, files); + + if (!dest_file || !src_file || !dest_file->data || !src_file->data || + offset + entry->pointer_size < offset || + offset + entry->pointer_size > dest_file->file->size || + entry->pointer_size < 1 || entry->pointer_size > 8 || + entry->pointer_size & (entry->pointer_size - 1)) + goto err; + + memcpy(&pointer, dest_file->data + offset, entry->pointer_size); + pointer = le64_to_cpu(pointer); + pointer += (unsigned long)src_file->data; + pointer = cpu_to_le64(pointer); + memcpy(dest_file->data + offset, &pointer, entry->pointer_size); + + return; +err: + warn_internalerror(); +} + +static void romfile_loader_add_checksum(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct romfile_loader_file *file; + unsigned offset = le32_to_cpu(entry->cksum_offset); + unsigned start = le32_to_cpu(entry->cksum_start); + unsigned len = le32_to_cpu(entry->cksum_length); + u8 *data; + + file = romfile_loader_find(entry->cksum_file, files); + + if (!file || !file->data || offset >= file->file->size || + start + len < start || start + len > file->file->size) + goto err; + + data = file->data + offset; + *data -= checksum(file->data + start, len); + + return; +err: + warn_internalerror(); +} + +int romfile_loader_execute(const char *name) +{ + struct romfile_loader_entry_s *entry; + int size, offset = 0, nfiles; + struct romfile_loader_files *files; + void *data = romfile_loadfile(name, &size); + if (!data) + return -1; + + if (size % sizeof(*entry)) { + warn_internalerror(); + goto err; + } + + /* (over)estimate the number of files to load. */ + nfiles = size / sizeof(*entry); + files = malloc_tmp(sizeof(*files) + nfiles * sizeof(files->files[0])); + if (!files) { + warn_noalloc(); + goto err; + } + files->nfiles = 0; + + for (offset = 0; offset < size; offset += sizeof(*entry)) { + entry = data + offset; + switch (le32_to_cpu(entry->command)) { + case ROMFILE_LOADER_COMMAND_ALLOCATE: + romfile_loader_allocate(entry, files); + break; + case ROMFILE_LOADER_COMMAND_ADD_POINTER: + romfile_loader_add_pointer(entry, files); + break; + case ROMFILE_LOADER_COMMAND_ADD_CHECKSUM: + romfile_loader_add_checksum(entry, files); + default: + /* Skip commands that we don't recognize. */ + break; + } + } + + free(files); + free(data); + return 0; + +err: + free(data); + return -1; +}
Load files through romfile loader and use for acpi tables. We need the RSDP pointer to hang the rest of the tables off it, to detect that we simply scan all memory in FSEG.
Add an option to disable this feature (useful for old QEMU versions). This saves about 1Kbytes.
enabled: Total size: 134932 Fixed: 61571 Free: 127212 (used 51.5% of 256KiB rom)
disabled: Total size: 133836 Fixed: 61563 Free: 128308 (used 51.1% of 256KiB rom)
Signed-off-by: Michael S. Tsirkin mst@redhat.com --- src/fw/acpi.c | 21 +++++++++++++++++++++ src/Kconfig | 11 +++++++++++ 2 files changed, 32 insertions(+)
diff --git a/src/fw/acpi.c b/src/fw/acpi.c index 0497d9b..c29425c 100644 --- a/src/fw/acpi.c +++ b/src/fw/acpi.c @@ -32,6 +32,7 @@ #include "string.h" // memset #include "util.h" // MaxCountCPUs #include "x86.h" // readl +#include "romfile_loader.h" // romfile_loader_execute
#include "src/fw/acpi-dsdt.hex"
@@ -610,6 +611,26 @@ struct rsdp_descriptor *RsdpAddr; void acpi_setup(void) { + if (CONFIG_FW_ROMFILE_LOAD) { + int loader_err; + + dprintf(3, "load ACPI tables\n"); + + loader_err = romfile_loader_execute("etc/table-loader"); + + RsdpAddr = find_acpi_rsdp(); + + if (RsdpAddr) + return; + + /* If present, loader should have installed an RSDP. + * Not installed? We might still be able to continue + * using the builtin RSDP. + */ + if (!loader_err) + warn_internalerror(); + } + if (! CONFIG_ACPI) return;
diff --git a/src/Kconfig b/src/Kconfig index c40cc61..5780885 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -424,6 +424,17 @@ menu "BIOS Tables" This option can be disabled for QEMU 1.4 and newer to save some space in the ROM file. If unsure, say Y. + config FW_ROMFILE_LOAD + bool "Load BIOS tables from ROM files" + depends on QEMU_HARDWARE + default y + help + Support loading BIOS firmware tables from ROM files. + At the moment, only ACPI tables can be loaded in this way. + Required for QEMU 1.7 and newer. + This option can be disabled for QEMU 1.6 and older + to save some space in the ROM file. + If unsure, say Y. endmenu
source vgasrc/Kconfig