[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [RFC XEN PATCH v4 35/41] tools/libacpi: add a QEMU BIOSLinkLoader executor
The executor loads and executes the QEMU BIOSLinkerLoader ROM etc/table-loader. It currently supports three BIOSLinkerLoader commands ALLOCATE, POINTER and CHECKSUM, which are enough to load currently supported QEMU ROMs. Signed-off-by: Haozhong Zhang <haozhong.zhang@xxxxxxxxx> --- Cc: Jan Beulich <jbeulich@xxxxxxxx> Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> Cc: Wei Liu <wei.liu2@xxxxxxxxxx> --- tools/libacpi/build.c | 3 +- tools/libacpi/libacpi.h | 2 + tools/libacpi/qemu.h | 2 + tools/libacpi/qemu_fw_cfg.c | 6 + tools/libacpi/qemu_loader.c | 302 ++++++++++++++++++++++++++++++++++++++++++++ tools/libacpi/qemu_stub.c | 9 ++ 6 files changed, 322 insertions(+), 2 deletions(-) diff --git a/tools/libacpi/build.c b/tools/libacpi/build.c index 46051c46ac..f2d65574ff 100644 --- a/tools/libacpi/build.c +++ b/tools/libacpi/build.c @@ -105,8 +105,7 @@ static int dm_acpi_blacklist_signature(struct acpi_config *config, uint64_t sig) return 0; } -static void set_checksum( - void *table, uint32_t checksum_offset, uint32_t length) +void set_checksum(void *table, uint32_t checksum_offset, uint32_t length) { uint8_t *p, sum = 0; diff --git a/tools/libacpi/libacpi.h b/tools/libacpi/libacpi.h index 80403f04ab..c973311a15 100644 --- a/tools/libacpi/libacpi.h +++ b/tools/libacpi/libacpi.h @@ -108,6 +108,8 @@ int acpi_build_tables(struct acpi_ctxt *ctxt, struct acpi_config *config); bool fw_cfg_exists(void); +void set_checksum(void *table, uint32_t checksum_offset, uint32_t length); + #endif /* __LIBACPI_H__ */ /* diff --git a/tools/libacpi/qemu.h b/tools/libacpi/qemu.h index 940816bf27..224fc67e02 100644 --- a/tools/libacpi/qemu.h +++ b/tools/libacpi/qemu.h @@ -36,8 +36,10 @@ struct fw_cfg_file { }; int fw_cfg_probe_roms(struct acpi_ctxt *ctxt); +void fw_cfg_read_file(const struct fw_cfg_file *file, void *buf); int loader_add_rom(struct acpi_ctxt* ctxt, const struct fw_cfg_file *file); +int loader_exec(struct acpi_ctxt *ctxt); #endif /* !__QEMU_H__ */ diff --git a/tools/libacpi/qemu_fw_cfg.c b/tools/libacpi/qemu_fw_cfg.c index 458b6eabdc..260728ecb3 100644 --- a/tools/libacpi/qemu_fw_cfg.c +++ b/tools/libacpi/qemu_fw_cfg.c @@ -82,6 +82,12 @@ int fw_cfg_probe_roms(struct acpi_ctxt *ctxt) return rc; } +void fw_cfg_read_file(const struct fw_cfg_file *file, void *buf) +{ + fw_cfg_read_entry(be16_to_cpu(file->select), buf, + be32_to_cpu(file->size)); +} + /* * Local variables: * mode: C diff --git a/tools/libacpi/qemu_loader.c b/tools/libacpi/qemu_loader.c index c0ed3b0ad0..d041a37246 100644 --- a/tools/libacpi/qemu_loader.c +++ b/tools/libacpi/qemu_loader.c @@ -24,9 +24,71 @@ #include "libacpi.h" #include "qemu.h" +/* QEMU BIOSLinkerLoader interface. All fields in little-endian. */ +struct loader_entry { + 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[FW_CFG_FILE_PATH_MAX_LENGTH]; + 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[FW_CFG_FILE_PATH_MAX_LENGTH]; + char src_file[FW_CFG_FILE_PATH_MAX_LENGTH]; + 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[FW_CFG_FILE_PATH_MAX_LENGTH]; + uint32_t offset; + uint32_t start; + uint32_t length; + } cksum; + + /* padding */ + char pad[124]; + }; +} __attribute__ ((packed)); + +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, +}; + struct rom { struct fw_cfg_file file; struct rom *next; + void *data; }; static struct rom *roms = NULL; @@ -41,6 +103,174 @@ static bool rom_needed(const char *file_name) !strncmp(file_name, "etc/acpi/nvdimm-mem", FW_CFG_FILE_PATH_MAX_LENGTH); } +static int loader_load(struct acpi_ctxt *ctxt, struct rom *loader) +{ + struct fw_cfg_file *file = &loader->file; + uint32_t size = be32_to_cpu(file->size); + + loader->data = ctxt->mem_ops.alloc(ctxt, size, 0); + if ( !loader->data ) + return -ENOMEM; + + fw_cfg_read_file(file, loader->data); + + return 0; +} + +static struct rom *loader_find_rom(const char *file_name) +{ + struct rom *rom = roms; + + while ( rom ) + { + if ( !strncmp(rom->file.name, file_name, FW_CFG_FILE_PATH_MAX_LENGTH) ) + break; + rom = rom->next; + } + + if ( !rom ) + printf("ERROR: File %s not exist\n", file_name); + + return rom; +} + +static void loader_cmd_display(struct loader_entry *entry) +{ + switch ( entry->command ) + { + case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: + entry->alloc.file[FW_CFG_FILE_PATH_MAX_LENGTH - 1] = '\0'; + printf("COMMAND_ALLOCATE: file %s, align %u, zone %u\n", + entry->alloc.file, entry->alloc.align, entry->alloc.zone); + break; + + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + entry->pointer.dest_file[FW_CFG_FILE_PATH_MAX_LENGTH - 1] = '\0'; + entry->pointer.src_file[FW_CFG_FILE_PATH_MAX_LENGTH - 1] = '\0'; + printf("COMMAND_ADD_POINTER: dst %s, src %s, offset %u, size %u\n", + entry->pointer.dest_file, entry->pointer.src_file, + entry->pointer.offset, entry->pointer.size); + break; + + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + entry->cksum.file[FW_CFG_FILE_PATH_MAX_LENGTH - 1] = '\0'; + printf("COMMAND_ADD_CHECKSUM: file %s, offset %u, offset %u, len %u\n", + entry->cksum.file, entry->cksum.offset, + entry->cksum.start, entry->cksum.length); + break; + + default: + printf("Unsupported command %u\n", entry->command); + } +} + +static int loader_exec_allocate(struct acpi_ctxt *ctxt, + const struct loader_entry *entry) +{ + uint32_t align = entry->alloc.align; + uint8_t zone = entry->alloc.zone; + struct rom *rom; + struct fw_cfg_file *file; + + rom = loader_find_rom(entry->alloc.file); + if ( !rom ) + return -ENOENT; + file = &rom->file; + + if ( align & (align - 1) ) + { + printf("ERROR: Invalid alignment %u, not power of 2\n", align); + return -EINVAL; + } + + if ( zone != BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH && + zone != BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG ) + { + printf("ERROR: Unsupported zone type %u\n", zone); + return -EINVAL; + } + + rom->data = ctxt->mem_ops.alloc(ctxt, be32_to_cpu(file->size), align); + fw_cfg_read_file(file, rom->data); + + return 0; +} + +static int loader_exec_add_pointer(struct acpi_ctxt *ctxt, + const struct loader_entry *entry) +{ + uint32_t offset = entry->pointer.offset; + uint8_t size = entry->pointer.size; + struct rom *dst, *src; + uint64_t pointer, old_pointer; + + dst = loader_find_rom(entry->pointer.dest_file); + src = loader_find_rom(entry->pointer.src_file); + if ( !dst || !src ) + return -ENOENT; + + if ( !dst->data ) + { + printf("ERROR: No space allocated for file %s\n", + entry->pointer.dest_file); + return -ENOSPC; + } + if ( !src->data ) + { + printf("ERROR: No space allocated for file %s\n", + entry->pointer.src_file); + return -ENOSPC; + } + if ( offset + size < offset || + offset + size > be32_to_cpu(dst->file.size) ) + { + printf("ERROR: Invalid size\n"); + return -EINVAL; + } + if ( size != 1 && size != 2 && size != 4 && size != 8 ) + { + printf("ERROR: Invalid pointer size %u\n", size); + return -EINVAL; + } + + memcpy(&pointer, dst->data + offset, size); + old_pointer = pointer; + pointer += ctxt->mem_ops.v2p(ctxt, src->data); + memcpy(dst->data + offset, &pointer, size); + + return 0; +} + +static int loader_exec_add_checksum(const struct loader_entry *entry) +{ + uint32_t offset = entry->cksum.offset; + uint32_t start = entry->cksum.start; + uint32_t length = entry->cksum.length; + uint32_t size; + struct rom *rom; + + rom = loader_find_rom(entry->cksum.file); + if ( !rom ) + return -ENOENT; + + if ( !rom->data ) + { + printf("ERROR: No space allocated for file %s\n", entry->cksum.file); + return -ENOSPC; + } + + size = be32_to_cpu(rom->file.size); + if ( offset >= size || start + length < start || start + length > size ) + { + printf("ERROR: Invalid size\n"); + return -EINVAL; + } + + set_checksum(rom->data + start, offset - start, length); + + return 0; +} + int loader_add_rom(struct acpi_ctxt *ctxt, const struct fw_cfg_file *file) { const char *name = file->name; @@ -63,6 +293,7 @@ int loader_add_rom(struct acpi_ctxt *ctxt, const struct fw_cfg_file *file) memcpy(&rom->file, file, sizeof(*file)); rom->next = roms; + rom->data = NULL; roms = rom; if ( !strncmp(name, "etc/table-loader", FW_CFG_FILE_PATH_MAX_LENGTH) ) @@ -71,6 +302,77 @@ int loader_add_rom(struct acpi_ctxt *ctxt, const struct fw_cfg_file *file) return 0; } +int loader_exec(struct acpi_ctxt *ctxt) +{ + struct loader_entry *entry; + struct fw_cfg_file *file; + unsigned long size, offset = 0; + void *data; + int rc = 0; + + if ( !bios_loader ) + { + printf("ERROR: Cannot find BIOSLinkerLoader\n"); + return -ENODEV; + } + + file = &bios_loader->file; + size = be32_to_cpu(file->size); + + if ( size % sizeof(*entry) ) + { + printf("ERROR: Invalid BIOSLinkerLoader size %ld, " + "not multiples of entry size %ld\n", + size, (unsigned long)sizeof(*entry)); + return -EINVAL; + } + + rc = loader_load(ctxt, bios_loader); + if ( rc ) + { + printf("ERROR: Failed to load BIOSLinkerLoader, err %d\n", rc); + return rc; + } + + data = bios_loader->data; + + while ( offset < size ) + { + entry = data + offset; + + switch ( entry->command ) + { + case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: + rc = loader_exec_allocate(ctxt, entry); + break; + + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + rc = loader_exec_add_pointer(ctxt, entry); + break; + + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + rc = loader_exec_add_checksum(entry); + break; + + default: + /* Skip unsupported commands */ + break; + } + + if ( rc ) + { + printf("ERROR: Failed to execute BIOSLinkerLoader command:\n"); + loader_cmd_display(entry); + + break; + } + + offset += sizeof(*entry); + } + + return rc; +} + /* * Local variables: * mode: C diff --git a/tools/libacpi/qemu_stub.c b/tools/libacpi/qemu_stub.c index fdba5294e1..1eedf5466e 100644 --- a/tools/libacpi/qemu_stub.c +++ b/tools/libacpi/qemu_stub.c @@ -34,11 +34,20 @@ int fw_cfg_probe_roms(struct acpi_ctxt *ctxt) return -ENOSYS; } +void fw_cfg_read_file(const struct fw_cfg_file *file, void *buf) +{ +} + int loader_add_rom(struct acpi_ctxt* ctxt, const struct fw_cfg_file *file) { return -ENOSYS; } +int loader_exec(struct acpi_ctxt *ctxt) +{ + return -ENOSYS; +} + /* * Local variables: * mode: C -- 2.15.1 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |