This is the seabios code that adds support for loading acpi tables from QEMU.
Kevin, I think I have addresses all your comments. In particular, in the end I dropped the new data field from the romfile structure.
Please review and consider for merging.
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 (5): romfile_loader: utility to patch in-memory ROM files pmm: support looking up a given pattern in FSEG acpi: pack rsdp acpi: load and link tables through romfile loader acpi: add an option to disable builtin tables
Makefile | 2 +- src/acpi.h | 2 +- src/romfile_loader.h | 72 +++++++++++++++++++++ src/util.h | 2 + src/acpi.c | 29 +++++++++ src/paravirt.c | 2 + src/pmm.c | 18 ++++++ src/romfile_loader.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/Kconfig | 12 +++- 9 files changed, 308 insertions(+), 3 deletions(-) create mode 100644 src/romfile_loader.h create mode 100644 src/romfile_loader.c
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/romfile_loader.h | 72 +++++++++++++++++++++ src/paravirt.c | 2 + src/romfile_loader.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 src/romfile_loader.h create mode 100644 src/romfile_loader.c
diff --git a/Makefile b/Makefile index 00ef346..405f8f9 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c pmm.c coreboot.c boot.c \ acpi.c smm.c mptable.c pirtable.c smbios.c pciinit.c optionroms.c mtrr.c \ lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c \ - biostables.c xen.c bmp.c romfile.c csm.c + biostables.c xen.c bmp.c romfile.c csm.c romfile_loader.c SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
# Default compiler flags diff --git a/src/romfile_loader.h b/src/romfile_loader.h new file mode 100644 index 0000000..15eab2a --- /dev/null +++ b/src/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/paravirt.c b/src/paravirt.c index d1a5d3e..9dd229d 100644 --- a/src/paravirt.c +++ b/src/paravirt.c @@ -327,6 +327,8 @@ void qemu_cfg_init(void) for (e = 0; e < count; e++) { struct QemuCfgFile qfile; qemu_cfg_read(&qfile, sizeof(qfile)); + if (!*qfile.name) + return; qemu_romfile_add(qfile.name, be16_to_cpu(qfile.select) , 0, be32_to_cpu(qfile.size)); } diff --git a/src/romfile_loader.c b/src/romfile_loader.c new file mode 100644 index 0000000..5e98810 --- /dev/null +++ b/src/romfile_loader.c @@ -0,0 +1,172 @@ +#include "romfile_loader.h" +#include "byteorder.h" // leXX_to_cpu/cpu_to_leXX +#include "util.h" // checksum + +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 = pmm_malloc(zone, PMM_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 = 0; + struct romfile_loader_files *files; + void *data = romfile_loadfile(name, &size); + if (!data) + return -1; + + /* Validate and count files */ + for (offset = 0; offset < size; offset += sizeof(*entry)) { + entry = data + offset; + /* Check that entry fits in buffer. */ + if (offset + sizeof(*entry) > size) { + warn_internalerror(); + return -1; + } + + if (le32_to_cpu(entry->command) == ROMFILE_LOADER_COMMAND_ALLOCATE) { + nfiles++; + } + } + + files = malloc_tmp(sizeof(*files) + nfiles * sizeof(files->files[0])); + 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; +}
Will be used to find RSDP there.
Signed-off-by: Michael S. Tsirkin mst@redhat.com --- src/util.h | 2 ++ src/pmm.c | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+)
diff --git a/src/util.h b/src/util.h index 6944cfb..f66e65a 100644 --- a/src/util.h +++ b/src/util.h @@ -431,6 +431,8 @@ static inline void free(void *data) { pmm_free(data); }
+void *pmm_find_fseg_pattern(void *pattern, unsigned pattern_size); + // mtrr.c void mtrr_setup(void);
diff --git a/src/pmm.c b/src/pmm.c index 8f993fd..86cf86f 100644 --- a/src/pmm.c +++ b/src/pmm.c @@ -374,6 +374,24 @@ calcRamSize(void) LegacyRamSize = rs >= 1024*1024 ? rs : 1024*1024; }
+// Find the data block in FSEG matching a given pattern. +void *pmm_find_fseg_pattern(void *pattern, unsigned pattern_size) +{ + struct allocinfo_s *info; + hlist_for_each_entry(info, &ZoneFSeg.head, node) { + unsigned space = info->dataend - info->data; + int off; + + if (space < pattern_size) + continue; + for (off = 0; off < space - pattern_size; ++off) { + if (!memcmp(info->data + off, pattern, pattern_size)) + return info->data + off; + } + } + return NULL; +} + // Update pointers after code relocation. void malloc_init(void)
rsdp might not be aligned, so mark it packed.
Signed-off-by: Michael S. Tsirkin mst@redhat.com --- src/acpi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/acpi.h b/src/acpi.h index f872b39..eab982a 100644 --- a/src/acpi.h +++ b/src/acpi.h @@ -47,7 +47,7 @@ struct rsdp_descriptor { /* Root System Descriptor Pointer */ u64 xsdt_physical_address; /* 64-bit physical address of XSDT */ u8 extended_checksum; /* Checksum of entire table */ u8 reserved [3]; /* Reserved field must be 0 */ -}; +} PACKED;
extern struct rsdp_descriptor *RsdpAddr;
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.
Signed-off-by: Michael S. Tsirkin mst@redhat.com --- src/acpi.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/src/acpi.c b/src/acpi.c index f305fa3..8ec7d8d 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -27,6 +27,7 @@ #include "config.h" // CONFIG_* #include "paravirt.h" // RamSize #include "dev-q35.h" +#include "romfile_loader.h"
#include "acpi-dsdt.hex"
@@ -599,15 +600,40 @@ static const struct pci_device_id acpi_find_tbl[] = {
struct rsdp_descriptor *RsdpAddr;
+/* Look for RSDP signature in FSEG memory */ +struct rsdp_descriptor * +acpi_find_rsdp_rom(void) +{ + u64 rsdp = cpu_to_le64(RSDP_SIGNATURE); + return pmm_find_fseg_pattern(&rsdp, sizeof(rsdp)); +} + #define MAX_ACPI_TABLES 20 void acpi_setup(void) { + int loader_err; if (! CONFIG_ACPI) return;
dprintf(3, "init ACPI tables\n");
+ loader_err = romfile_loader_execute("etc/table-loader"); + + RsdpAddr = acpi_find_rsdp_rom(); + + 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(); + + dprintf(3, "generate ACPI tables\n"); + // This code is hardcoded for PIIX4 Power Management device. struct pci_device *pci = pci_find_init_device(acpi_find_tbl, NULL); if (!pci)
Serves to save a bit of memory, and is helpful for debugging (making sure tables come from qemu).
Memory stats: Enabled: Total size: 128776 Fixed: 59100 Free: 2296 (used 98.2% of 128KiB rom) Disabled: Total size: 119836 Fixed: 58996 Free: 11236 (used 91.4% of 128KiB rom)
Signed-off-by: Michael S. Tsirkin mst@redhat.com --- src/acpi.c | 3 +++ src/Kconfig | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/acpi.c b/src/acpi.c index 8ec7d8d..8b562f1 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -632,6 +632,9 @@ acpi_setup(void) if (!loader_err) warn_internalerror();
+ if (!CONFIG_ACPI_BUILTIN) + return; + dprintf(3, "generate ACPI tables\n");
// This code is hardcoded for PIIX4 Power Management device. diff --git a/src/Kconfig b/src/Kconfig index 3a4d580..e73eecc 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -399,10 +399,20 @@ menu "BIOS Tables" default y help Support generation of ACPI tables. + config ACPI_BUILTIN + bool "Include built-in ACPI tables" + default y + depends on ACPI + help + Include built-in ACPI tables in BIOS. + Required for QEMU 1.5 and older. + This option can be disabled for QEMU 1.6 and newer + to save some space in the ROM file. + If unsure, say Y. config ACPI_DSDT bool "Include default ACPI DSDT" default y - depends on ACPI + depends on ACPI && ACPI_BUILTIN help Include default DSDT ACPI table in BIOS. Required for QEMU 1.3 and older.
On So, 2013-09-22 at 16:17 +0300, Michael S. Tsirkin wrote:
This is the seabios code that adds support for loading acpi tables from QEMU.
Kevin, I think I have addresses all your comments. In particular, in the end I dropped the new data field from the romfile structure.
Series doesn't apply, needs a rebase.
cheers, Gerd
On Mon, Sep 23, 2013 at 05:42:36PM +0200, Gerd Hoffmann wrote:
On So, 2013-09-22 at 16:17 +0300, Michael S. Tsirkin wrote:
This is the seabios code that adds support for loading acpi tables from QEMU.
Kevin, I think I have addresses all your comments. In particular, in the end I dropped the new data field from the romfile structure.
Series doesn't apply, needs a rebase.
cheers, Gerd
Thanks, I rebased and sent v4.