diff options
-rw-r--r-- | docs/specs/fw_cfg.txt | 21 | ||||
-rw-r--r-- | hw/nvram/fw_cfg.c | 55 | ||||
-rw-r--r-- | hw/ppc/mac_newworld.c | 2 | ||||
-rw-r--r-- | hw/ppc/mac_oldworld.c | 2 | ||||
-rw-r--r-- | hw/sparc/sun4m.c | 2 | ||||
-rw-r--r-- | hw/sparc64/sun4u.c | 2 | ||||
-rw-r--r-- | include/hw/nvram/fw_cfg.h | 3 | ||||
-rw-r--r-- | qemu-options.hx | 11 | ||||
-rw-r--r-- | tests/bios-tables-test.c | 76 | ||||
-rw-r--r-- | trace-events | 2 | ||||
-rw-r--r-- | util/qemu-config.c | 2 | ||||
-rw-r--r-- | vl.c | 63 |
12 files changed, 163 insertions, 78 deletions
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=]<item_name>,file=<path> + +where <item_name> is the fw_cfg item name, and <path> 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/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 68eff77983..88481b78c4 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) @@ -436,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; @@ -458,7 +446,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; } @@ -484,6 +471,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; @@ -502,23 +499,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) @@ -535,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/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, diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 6d8a8ac564..e60d3ca212 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -67,10 +67,9 @@ 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, - 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/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=]<name>,file=<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/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,35 +599,15 @@ static void test_acpi_asl(test_data *data) free_test_data(&exp_data); } -static void test_smbios_ep_address(test_data *data) -{ - uint32_t off; - - /* find smbios entry point structure */ - for (off = 0xf0000; off < 0x100000; off += 0x10) { - uint8_t sig[] = "_SM_"; - int i; - - for (i = 0; i < sizeof sig - 1; ++i) { - sig[i] = readb(off + i); - } - - if (!memcmp(sig, "_SM_", sizeof sig)) { - break; - } - } - - g_assert_cmphex(off, <, 0x100000); - data->smbios_ep_addr = off; -} - -static void test_smbios_ep_table(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); - g_assert(!memcmp(ep_table->anchor_string, "_SM_", 4)); + 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); @@ -636,17 +616,50 @@ static void test_smbios_ep_table(test_data *data) 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)); + 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); - g_assert_cmpuint(ep_table->structure_table_length, >, 0); + 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); - g_assert_cmpuint(ep_table->number_of_structures, >, 0); + if (ep_table->number_of_structures == 0) { + return false; + } 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)); + 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; + + /* find smbios entry point structure */ + for (off = 0xf0000; off < 0x100000; off += 0x10) { + uint8_t sig[] = "_SM_"; + int i; + + for (i = 0; i < sizeof sig - 1; ++i) { + sig[i] = readb(off + i); + } + + if (!memcmp(sig, "_SM_", sizeof sig)) { + /* 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); } 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); diff --git a/trace-events b/trace-events index 7af9f19a33..09d94ea980 100644 --- a/trace-events +++ b/trace-events @@ -193,10 +193,8 @@ 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" fw_cfg_add_file(void *s, int index, char *name, size_t len) "%p #%d: %s (%zd bytes)" # hw/block/hd-geometry.c 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, @@ -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) |