From: Ben Warren <b...@skyportsystems.com> This adds a new linker-loader command to instruct the guest to allocate memory for a fw_cfg file and write the address back into another writeable fw_cfg file. Knowing this address, QEMU can then write into guest memory at runtime.
Signed-off-by: Ben Warren <b...@skyportsystems.com> --- hw/acpi/bios-linker-loader.c | 71 ++++++++++++++++++++++++++++++++++-- include/hw/acpi/bios-linker-loader.h | 7 ++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c index d963ebe..1d991ba 100644 --- a/hw/acpi/bios-linker-loader.c +++ b/hw/acpi/bios-linker-loader.c @@ -78,6 +78,22 @@ struct BiosLinkerLoaderEntry { uint32_t length; } cksum; + /* + * COMMAND_ALLOCATE_RETURN_ADDR - allocate a table from @alloc_ret_file + * subject to @alloc_ret_align alignment (must be power of 2) + * and @alloc_ret_zone (can be HIGH or FSEG) requirements. + * Additionally, return the address of the allocation in + * @addr_file. + * + * This may be used instead of COMMAND_ALLOCATE + */ + struct { + char alloc_ret_file[BIOS_LINKER_LOADER_FILESZ]; + uint32_t alloc_ret_align; + uint8_t alloc_ret_zone; + char alloc_ret_addr_file[BIOS_LINKER_LOADER_FILESZ]; + }; + /* padding */ char pad[124]; }; @@ -85,9 +101,10 @@ struct BiosLinkerLoaderEntry { 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, + BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1, + BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2, + BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3, + BIOS_LINKER_LOADER_COMMAND_ALLOCATE_RET_ADDR = 0x4, }; enum { @@ -278,3 +295,51 @@ void bios_linker_loader_add_pointer(BIOSLinker *linker, g_array_append_vals(linker->cmd_blob, &entry, sizeof entry); } + +/* + * bios_linker_loader_alloc_ret_addr: ask guest to load file into guest memory + * and patch the address in another file + * + * @linker: linker object instance + * @data_file: name of the file blob to be loaded + * @file_blob: pointer to blob corresponding to @file_name + * @alloc_align: required minimal alignment in bytes. Must be a power of 2. + * @alloc_fseg: request allocation in FSEG zone (useful for the RSDP ACPI table) + * @addr_file: destination file that will contain the address. + * This must already exist + * + * Note: this command must precede any other linker command that uses + * the data file. + */ +void bios_linker_loader_alloc_ret_addr(BIOSLinker *linker, + const char *data_file, + GArray *file_blob, + uint32_t alloc_align, + bool alloc_fseg, + const char *addr_file) +{ + BiosLinkerLoaderEntry entry; + BiosLinkerFileEntry d_file = { g_strdup(data_file), file_blob}; + + /* Address file is expected to already be loaded */ + const BiosLinkerFileEntry *a_file = + bios_linker_find_file(linker, addr_file); + + assert(!(alloc_align & (alloc_align - 1))); + assert(a_file); + + g_array_append_val(linker->file_list, d_file); + + memset(&entry, 0, sizeof entry); + strncpy(entry.alloc_ret_file, data_file, + sizeof entry.alloc_ret_file - 1); + strncpy(entry.alloc_ret_addr_file, addr_file, + sizeof entry.alloc_ret_addr_file - 1); + entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE_RET_ADDR); + entry.alloc.align = cpu_to_le32(alloc_align); + entry.alloc.zone = alloc_fseg ? BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG : + BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH; + + /* Alloc entries must come first, so prepend them */ + g_array_append_vals(linker->cmd_blob, &entry, sizeof entry); +} diff --git a/include/hw/acpi/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h index fa1e5d1..69953e6 100644 --- a/include/hw/acpi/bios-linker-loader.h +++ b/include/hw/acpi/bios-linker-loader.h @@ -26,5 +26,12 @@ void bios_linker_loader_add_pointer(BIOSLinker *linker, const char *src_file, uint32_t src_offset); +void bios_linker_loader_alloc_ret_addr(BIOSLinker *linker, + const char *data_file, + GArray *file_blob, + uint32_t alloc_align, + bool alloc_fseg, + const char *addr_file); + void bios_linker_loader_cleanup(BIOSLinker *linker); #endif -- 2.7.4