From 1ceaefbd0d09642fcff05c6b8da49ad8fbc050cb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 29 May 2015 14:37:54 +0200 Subject: [PATCH 1/8] QemuOpts: increase number of vm_config_groups Adding the fw_cfg cmd line support patch by Gabriel L. Somlo hits the limit. Fix this by making the array larger. Cc: Gabriel L. Somlo Signed-off-by: Gerd Hoffmann --- util/qemu-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/qemu-config.c b/util/qemu-config.c index 35adfda496..6cfdd7204a 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -6,7 +6,7 @@ #include "qapi/error.h" #include "qmp-commands.h" -static QemuOptsList *vm_config_groups[32]; +static QemuOptsList *vm_config_groups[48]; static QemuOptsList *drive_config_groups[4]; static QemuOptsList *find_list(QemuOptsList **lists, const char *group, From 1edd34b638f73d39a175fbc4f9ad5c97800d7470 Mon Sep 17 00:00:00 2001 From: "Gabriel L. Somlo" Date: Mon, 8 Jun 2015 14:10:44 -0400 Subject: [PATCH 2/8] fw_cfg: add fw_cfg_modify_i16 (update) method Allow the ability to modify the value of an existing 16-bit integer fw_cfg item. Signed-off-by: Gabriel Somlo Signed-off-by: Gerd Hoffmann --- hw/nvram/fw_cfg.c | 10 ++++++++++ include/hw/nvram/fw_cfg.h | 1 + 2 files changed, 11 insertions(+) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 68eff77983..08b5cc3ecc 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -484,6 +484,16 @@ void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value) fw_cfg_add_bytes(s, key, copy, sizeof(value)); } +void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value) +{ + uint16_t *copy, *old; + + copy = g_malloc(sizeof(value)); + *copy = cpu_to_le16(value); + old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value)); + g_free(old); +} + void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value) { uint32_t *copy; diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 6d8a8ac564..bc6c4a03c9 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -67,6 +67,7 @@ typedef void (*FWCfgReadCallback)(void *opaque, uint32_t offset); void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len); void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value); void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value); +void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value); void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value); void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value); void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, From 48779e501810c5046ff8af7b9cf9c99bec2928a1 Mon Sep 17 00:00:00 2001 From: "Gabriel L. Somlo" Date: Mon, 8 Jun 2015 14:10:45 -0400 Subject: [PATCH 3/8] fw_cfg: fix FW_CFG_BOOT_DEVICE update on ppc and sparc On ppc, sparc, and sparc64, the value of the FW_CFG_BOOT_DEVICE 16bit fw_cfg entry is repeatedly modified from a series of callbacks, which currently results in the previous value's dynamically allocated memory being leaked. This patch switches updating to the new fw_cfg_modify_i16() call, which does not cause memory leaks. Signed-off-by: Gabriel Somlo Signed-off-by: Gerd Hoffmann --- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/sparc/sun4m.c | 2 +- hw/sparc64/sun4u.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index a365bf9223..0f3e34122a 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -119,7 +119,7 @@ static const MemoryRegionOps unin_ops = { static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { - fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); + fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } static uint64_t translate_kernel_address(void *opaque, uint64_t addr) diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index f26133dedd..99879dd2d5 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -52,7 +52,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { - fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); + fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } static uint64_t translate_kernel_address(void *opaque, uint64_t addr) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 8a3599c403..68ac4d8bba 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -124,7 +124,7 @@ void DMA_register_channel (int nchan, static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { - fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); + fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } static void nvram_init(Nvram *nvram, uint8_t *macaddr, diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 6f34e87935..30cfa0e0a0 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -127,7 +127,7 @@ void DMA_register_channel (int nchan, static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { - fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); + fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size, From 023e3148567ac898c7258138f8e86c3c2bb40d07 Mon Sep 17 00:00:00 2001 From: "Gabriel L. Somlo" Date: Wed, 29 Apr 2015 11:21:50 -0400 Subject: [PATCH 4/8] fw_cfg: remove support for guest-side data writes From this point forward, any guest-side writes to the fw_cfg data register will be treated as no-ops. This patch also removes the unused host-side API function fw_cfg_add_callback(), which allowed the registration of a callback to be executed each time the guest completed a full overwrite of a given fw_cfg data item. Signed-off-by: Gabriel Somlo Reviewed-by: Laszlo Ersek Signed-off-by: Gerd Hoffmann --- hw/nvram/fw_cfg.c | 33 +-------------------------------- include/hw/nvram/fw_cfg.h | 2 -- trace-events | 1 - 3 files changed, 1 insertion(+), 35 deletions(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 08b5cc3ecc..d2a43470e0 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -46,7 +46,6 @@ typedef struct FWCfgEntry { uint32_t len; uint8_t *data; void *callback_opaque; - FWCfgCallback callback; FWCfgReadCallback read_callback; } FWCfgEntry; @@ -232,19 +231,7 @@ static void fw_cfg_reboot(FWCfgState *s) static void fw_cfg_write(FWCfgState *s, uint8_t value) { - int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); - FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; - - trace_fw_cfg_write(s, value); - - if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback && - s->cur_offset < e->len) { - e->data[s->cur_offset++] = value; - if (s->cur_offset == e->len) { - e->callback(e->callback_opaque, e->data); - s->cur_offset = 0; - } - } + /* nothing, write support removed in QEMU v2.4+ */ } static int fw_cfg_select(FWCfgState *s, uint16_t key) @@ -458,7 +445,6 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key, s->entries[arch][key].data = data; s->entries[arch][key].len = len; s->entries[arch][key].callback_opaque = NULL; - s->entries[arch][key].callback = NULL; return ptr; } @@ -512,23 +498,6 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value) fw_cfg_add_bytes(s, key, copy, sizeof(value)); } -void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, - void *callback_opaque, void *data, size_t len) -{ - int arch = !!(key & FW_CFG_ARCH_LOCAL); - - assert(key & FW_CFG_WRITE_CHANNEL); - - key &= FW_CFG_ENTRY_MASK; - - assert(key < FW_CFG_MAX_ENTRY && len <= UINT32_MAX); - - s->entries[arch][key].data = data; - s->entries[arch][key].len = (uint32_t)len; - s->entries[arch][key].callback_opaque = callback_opaque; - s->entries[arch][key].callback = callback; -} - void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgReadCallback callback, void *callback_opaque, void *data, size_t len) diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index bc6c4a03c9..e60d3ca212 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -70,8 +70,6 @@ void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value); void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value); void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value); void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value); -void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, - void *callback_opaque, void *data, size_t len); void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, size_t len); void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, diff --git a/trace-events b/trace-events index a589650597..27d4ba71b6 100644 --- a/trace-events +++ b/trace-events @@ -193,7 +193,6 @@ ecc_diag_mem_writeb(uint64_t addr, uint32_t val) "Write diagnostic %"PRId64" = % ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x" # hw/nvram/fw_cfg.c -fw_cfg_write(void *s, uint8_t value) "%p %d" fw_cfg_select(void *s, uint16_t key, int ret) "%p key %d = %d" fw_cfg_read(void *s, uint8_t ret) "%p = %d" fw_cfg_add_file_dupe(void *s, char *name) "%p %s" From 0f9b214139d11ef058fa0f1c11c89e94fa6ef95d Mon Sep 17 00:00:00 2001 From: "Gabriel L. Somlo" Date: Wed, 29 Apr 2015 11:21:51 -0400 Subject: [PATCH 5/8] fw_cfg: prevent selector key conflict Enforce a single assignment of data for each distinct selector key. Signed-off-by: Gabriel Somlo Reviewed-by: Laszlo Ersek Signed-off-by: Gerd Hoffmann --- hw/nvram/fw_cfg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index d2a43470e0..0c0e15ecf5 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -423,6 +423,7 @@ static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key, key &= FW_CFG_ENTRY_MASK; assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX); + assert(s->entries[arch][key].data == NULL); /* avoid key conflict */ s->entries[arch][key].data = data; s->entries[arch][key].len = (uint32_t)len; From 0eb973f91521c6bcb6399d25327711d083f6eb10 Mon Sep 17 00:00:00 2001 From: "Gabriel L. Somlo" Date: Wed, 29 Apr 2015 11:21:52 -0400 Subject: [PATCH 6/8] fw_cfg: prohibit insertion of duplicate fw_cfg file names Exit with an error (instead of simply logging a trace event) whenever the same fw_cfg file name is added multiple times via one of the fw_cfg_add_file[_callback]() host-side API calls. Signed-off-by: Gabriel Somlo Reviewed-by: Laszlo Ersek Signed-off-by: Gerd Hoffmann --- hw/nvram/fw_cfg.c | 11 ++++++----- trace-events | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 0c0e15ecf5..88481b78c4 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -515,18 +515,19 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, index = be32_to_cpu(s->files->count); assert(index < FW_CFG_FILE_SLOTS); - fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index, - callback, callback_opaque, data, len); - pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename); for (i = 0; i < index; i++) { if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) { - trace_fw_cfg_add_file_dupe(s, s->files->f[index].name); - return; + error_report("duplicate fw_cfg file name: %s", + s->files->f[index].name); + exit(1); } } + fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index, + callback, callback_opaque, data, len); + s->files->f[index].size = cpu_to_be32(len); s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index); trace_fw_cfg_add_file(s, index, s->files->f[index].name, len); diff --git a/trace-events b/trace-events index 27d4ba71b6..9a29df7bb6 100644 --- a/trace-events +++ b/trace-events @@ -195,7 +195,6 @@ ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x # hw/nvram/fw_cfg.c fw_cfg_select(void *s, uint16_t key, int ret) "%p key %d = %d" fw_cfg_read(void *s, uint8_t ret) "%p = %d" -fw_cfg_add_file_dupe(void *s, char *name) "%p %s" fw_cfg_add_file(void *s, int index, char *name, size_t len) "%p #%d: %s (%zd bytes)" # hw/block/hd-geometry.c From 81b2b81062612ebeac4cd5333a3b15c7d79a5a3d Mon Sep 17 00:00:00 2001 From: "Gabriel L. Somlo" Date: Wed, 29 Apr 2015 11:21:53 -0400 Subject: [PATCH 7/8] fw_cfg: insert fw_cfg file blobs via qemu cmdline Allow user supplied files to be inserted into the fw_cfg device before starting the guest. Since fw_cfg_add_file() already disallows duplicate fw_cfg file names, qemu will exit with an error message if the user supplies multiple blobs with the same fw_cfg file name, or if a blob name collides with a fw_cfg name programmatically added from within the QEMU source code. A warning message will be printed if the fw_cfg item name does not begin with the prefix "opt/", which is recommended for external, user provided blobs. Signed-off-by: Gabriel Somlo Reviewed-by: Laszlo Ersek Signed-off-by: Gerd Hoffmann --- docs/specs/fw_cfg.txt | 21 +++++++++++++++ qemu-options.hx | 11 ++++++++ vl.c | 63 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt index 6accd924bd..74351dd18f 100644 --- a/docs/specs/fw_cfg.txt +++ b/docs/specs/fw_cfg.txt @@ -203,3 +203,24 @@ completes fully overwriting the item's data. NOTE: This function is deprecated, and will be completely removed starting with QEMU v2.4. + +== Externally Provided Items == + +As of v2.4, "file" fw_cfg items (i.e., items with selector keys above +FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file +directory structure) may be inserted via the QEMU command line, using +the following syntax: + + -fw_cfg [name=],file= + +where is the fw_cfg item name, and is the location +on the host file system of a file containing the data to be inserted. + +NOTE: Users *SHOULD* choose item names beginning with the prefix "opt/" +when using the "-fw_cfg" command line option, to avoid conflicting with +item names used internally by QEMU. For instance: + + -fw_cfg name=opt/my_item_name,file=./my_blob.bin + +Similarly, QEMU developers *SHOULD NOT* use item names prefixed with +"opt/" when inserting items programmatically, e.g. via fw_cfg_add_file(). diff --git a/qemu-options.hx b/qemu-options.hx index 4be98f71df..1d281f6818 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2686,6 +2686,17 @@ STEXI @table @option ETEXI +DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg, + "-fw_cfg [name=],file=\n" + " add named fw_cfg entry from file\n", + QEMU_ARCH_ALL) +STEXI +@item -fw_cfg [name=]@var{name},file=@var{file} +@findex -fw_cfg +Add named fw_cfg entry from file. @var{name} determines the name of +the entry in the fw_cfg file directory exposed to the guest. +ETEXI + DEF("serial", HAS_ARG, QEMU_OPTION_serial, \ "-serial dev redirect the serial port to char device 'dev'\n", QEMU_ARCH_ALL) diff --git a/vl.c b/vl.c index d4b2d03e0e..9542095d95 100644 --- a/vl.c +++ b/vl.c @@ -492,6 +492,25 @@ static QemuOptsList qemu_semihosting_config_opts = { }, }; +static QemuOptsList qemu_fw_cfg_opts = { + .name = "fw_cfg", + .implied_opt_name = "name", + .head = QTAILQ_HEAD_INITIALIZER(qemu_fw_cfg_opts.head), + .desc = { + { + .name = "name", + .type = QEMU_OPT_STRING, + .help = "Sets the fw_cfg name of the blob to be inserted", + }, { + .name = "file", + .type = QEMU_OPT_STRING, + .help = "Sets the name of the file from which\n" + "the fw_cfg blob will be loaded", + }, + { /* end of list */ } + }, +}; + /** * Get machine options * @@ -2127,6 +2146,38 @@ char *qemu_find_file(int type, const char *name) return NULL; } +static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) +{ + gchar *buf; + size_t size; + const char *name, *file; + + if (opaque == NULL) { + error_report("fw_cfg device not available"); + return -1; + } + name = qemu_opt_get(opts, "name"); + file = qemu_opt_get(opts, "file"); + if (name == NULL || *name == '\0' || file == NULL || *file == '\0') { + error_report("invalid argument value"); + return -1; + } + if (strlen(name) > FW_CFG_MAX_FILE_PATH - 1) { + error_report("name too long (max. %d char)", FW_CFG_MAX_FILE_PATH - 1); + return -1; + } + if (strncmp(name, "opt/", 4) != 0) { + error_report("WARNING: externally provided fw_cfg item names " + "should be prefixed with \"opt/\"!"); + } + if (!g_file_get_contents(file, &buf, &size, NULL)) { + error_report("can't load %s", file); + return -1; + } + fw_cfg_add_file((FWCfgState *)opaque, name, buf, size); + return 0; +} + static int device_help_func(void *opaque, QemuOpts *opts, Error **errp) { return qdev_device_help(opts); @@ -2822,6 +2873,7 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_numa_opts); qemu_add_opts(&qemu_icount_opts); qemu_add_opts(&qemu_semihosting_config_opts); + qemu_add_opts(&qemu_fw_cfg_opts); runstate_init(); @@ -3438,6 +3490,12 @@ int main(int argc, char **argv, char **envp) } do_smbios_option(opts); break; + case QEMU_OPTION_fwcfg: + opts = qemu_opts_parse(qemu_find_opts("fw_cfg"), optarg, 1); + if (opts == NULL) { + exit(1); + } + break; case QEMU_OPTION_enable_kvm: olist = qemu_find_opts("machine"); qemu_opts_parse(olist, "accel=kvm", 0); @@ -4274,6 +4332,11 @@ int main(int argc, char **argv, char **envp) numa_post_machine_init(); + if (qemu_opts_foreach(qemu_find_opts("fw_cfg"), + parse_fw_cfg, fw_cfg_find(), NULL) != 0) { + exit(1); + } + /* init USB devices */ if (usb_enabled()) { if (foreach_device_config(DEV_USB, usb_parse) < 0) From 5efed5a172881f601ac3c57c22ec5c5721f895be Mon Sep 17 00:00:00 2001 From: "Gabriel L. Somlo" Date: Mon, 18 May 2015 08:47:24 -0400 Subject: [PATCH 8/8] bios-tables-test: handle false-positive smbios signature matches It has been reported that sometimes the .rodata section of SeaBIOS, containing the constant string against which the SMBIOS signature ends up being compared, also falls within the guest f-segment. In that case, the test obviously fails, unless we continue searching for the *real* SMBIOS entry point. Rather than stopping at the first match for the SMBIOS signature ("_SM_") in the f-segment (0xF0000-0xFFFFF), continue scanning until either a valid entry point table is found, or the f-segment has been exhausted. Reported-by: Bruce Rogers Signed-off-by: Gabriel Somlo Tested-by: Bruce Rogers Signed-off-by: Gerd Hoffmann --- tests/bios-tables-test.c | 78 +++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 7e85dc45e3..0de1742d7d 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -599,7 +599,45 @@ static void test_acpi_asl(test_data *data) free_test_data(&exp_data); } -static void test_smbios_ep_address(test_data *data) +static bool smbios_ep_table_ok(test_data *data) +{ + struct smbios_entry_point *ep_table = &data->smbios_ep_table; + uint32_t addr = data->smbios_ep_addr; + + ACPI_READ_ARRAY(ep_table->anchor_string, addr); + if (memcmp(ep_table->anchor_string, "_SM_", 4)) { + return false; + } + ACPI_READ_FIELD(ep_table->checksum, addr); + ACPI_READ_FIELD(ep_table->length, addr); + ACPI_READ_FIELD(ep_table->smbios_major_version, addr); + ACPI_READ_FIELD(ep_table->smbios_minor_version, addr); + ACPI_READ_FIELD(ep_table->max_structure_size, addr); + ACPI_READ_FIELD(ep_table->entry_point_revision, addr); + ACPI_READ_ARRAY(ep_table->formatted_area, addr); + ACPI_READ_ARRAY(ep_table->intermediate_anchor_string, addr); + if (memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)) { + return false; + } + ACPI_READ_FIELD(ep_table->intermediate_checksum, addr); + ACPI_READ_FIELD(ep_table->structure_table_length, addr); + if (ep_table->structure_table_length == 0) { + return false; + } + ACPI_READ_FIELD(ep_table->structure_table_address, addr); + ACPI_READ_FIELD(ep_table->number_of_structures, addr); + if (ep_table->number_of_structures == 0) { + return false; + } + ACPI_READ_FIELD(ep_table->smbios_bcd_revision, addr); + if (acpi_checksum((uint8_t *)ep_table, sizeof *ep_table) || + acpi_checksum((uint8_t *)ep_table + 0x10, sizeof *ep_table - 0x10)) { + return false; + } + return true; +} + +static void test_smbios_entry_point(test_data *data) { uint32_t off; @@ -613,40 +651,15 @@ static void test_smbios_ep_address(test_data *data) } if (!memcmp(sig, "_SM_", sizeof sig)) { - break; + /* signature match, but is this a valid entry point? */ + data->smbios_ep_addr = off; + if (smbios_ep_table_ok(data)) { + break; + } } } g_assert_cmphex(off, <, 0x100000); - data->smbios_ep_addr = off; -} - -static void test_smbios_ep_table(test_data *data) -{ - struct smbios_entry_point *ep_table = &data->smbios_ep_table; - uint32_t addr = data->smbios_ep_addr; - - ACPI_READ_ARRAY(ep_table->anchor_string, addr); - g_assert(!memcmp(ep_table->anchor_string, "_SM_", 4)); - ACPI_READ_FIELD(ep_table->checksum, addr); - ACPI_READ_FIELD(ep_table->length, addr); - ACPI_READ_FIELD(ep_table->smbios_major_version, addr); - ACPI_READ_FIELD(ep_table->smbios_minor_version, addr); - ACPI_READ_FIELD(ep_table->max_structure_size, addr); - ACPI_READ_FIELD(ep_table->entry_point_revision, addr); - ACPI_READ_ARRAY(ep_table->formatted_area, addr); - ACPI_READ_ARRAY(ep_table->intermediate_anchor_string, addr); - g_assert(!memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)); - ACPI_READ_FIELD(ep_table->intermediate_checksum, addr); - ACPI_READ_FIELD(ep_table->structure_table_length, addr); - g_assert_cmpuint(ep_table->structure_table_length, >, 0); - ACPI_READ_FIELD(ep_table->structure_table_address, addr); - ACPI_READ_FIELD(ep_table->number_of_structures, addr); - g_assert_cmpuint(ep_table->number_of_structures, >, 0); - ACPI_READ_FIELD(ep_table->smbios_bcd_revision, addr); - g_assert(!acpi_checksum((uint8_t *)ep_table, sizeof *ep_table)); - g_assert(!acpi_checksum((uint8_t *)ep_table + 0x10, - sizeof *ep_table - 0x10)); } static inline bool smbios_single_instance(uint8_t type) @@ -767,8 +780,7 @@ static void test_acpi_one(const char *params, test_data *data) } } - test_smbios_ep_address(data); - test_smbios_ep_table(data); + test_smbios_entry_point(data); test_smbios_structs(data); qtest_quit(global_qtest);