Signed-off-by: Ivan Mironov <mironov.i...@gmail.com> --- hw/i386/pc.c | 12 +++++ hw/i386/smbios.c | 129 +++++++++++++++++++++++++++++++++++++++-------- include/hw/i386/smbios.h | 1 + qemu-options.hx | 8 ++- 4 files changed, 128 insertions(+), 22 deletions(-)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 12c436e..70d15b3 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -619,6 +619,14 @@ static unsigned int pc_apic_id_limit(unsigned int max_cpus) return x86_cpu_apic_id_from_index(max_cpus - 1) + 1; } +static void bochs_bios_machine_done(Notifier *notifier, void *data) +{ + (void) notifier; + (void) data; + + smbios_resolve_devices(); +} + static FWCfgState *bochs_bios_init(void) { FWCfgState *fw_cfg; @@ -654,6 +662,10 @@ static FWCfgState *bochs_bios_init(void) if (smbios_table) fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, smbios_table, smbios_len); + Notifier *smbios_machine_done = g_malloc0(sizeof(*smbios_machine_done)); + smbios_machine_done->notify = bochs_bios_machine_done; + qemu_add_machine_init_done_notifier(smbios_machine_done); + fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, &e820_reserve, sizeof(e820_reserve)); fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 355beb9..1be4e3a 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -109,6 +109,12 @@ static const struct { } }; +static struct unresolved_devs { + char *id; + ptrdiff_t bus_off, dev_func_off; +} *unresolved_devs; +static unsigned int unresolved_devs_count; + static QemuOptsList qemu_smbios_opts = { .name = "smbios", .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), @@ -259,7 +265,7 @@ static void smbios_check_collision(int type, int entry) } } -static void smbios_add_field(int type, int offset, const void *data, size_t len) +static void *smbios_alloc_field(int type, int offset, size_t len) { struct smbios_field *field; @@ -275,11 +281,24 @@ static void smbios_add_field(int type, int offset, const void *data, size_t len) field->type = type; field->offset = cpu_to_le16(offset); - memcpy(field->data, data, len); smbios_entries_len += sizeof(*field) + len; (*(uint16_t *)smbios_entries) = cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); + + return field->data; +} + +static void smbios_add_field(int type, int offset, const void *data, size_t len) +{ + void *field = smbios_alloc_field(type, offset, len); + memcpy(field, data, len); +} + +static ptrdiff_t smbios_alloc_field_off(int type, int offset, size_t len) +{ + void *field = smbios_alloc_field(type, offset, len); + return field - (void *)smbios_entries; } static void smbios_maybe_add_str(int type, int offset, const char *data) @@ -374,9 +393,7 @@ static void smbios_entry_add_type_41(QemuOpts *opts) const char *dev_type_str; unsigned long dev_type = 0x02 /* Unknown */; uint64_t dev_instance; - const char *address; - int seg, bus; - unsigned int dev, func; + const char *address, *id; /* Reference Designation */ designation = qemu_opt_get(opts, "designation"); @@ -443,30 +460,100 @@ static void smbios_entry_add_type_41(QemuOpts *opts) /* Segment Group Number, Bus Number and Device/Function Number */ address = qemu_opt_get(opts, "address"); - if (!address) { - error_report("You should specify address"); + id = qemu_opts_id(opts); + if (!address && !id) { + error_report("You should specify address or id"); exit(1); } - if (pci_parse_devaddr(address, &seg, &bus, &dev, &func) < 0) { - error_report("Invalid address"); + if (address && id) { + error_report("You should specify address OR id, not both"); exit(1); } - smbios_add_field(41, - offsetof(struct smbios_type_41, segment_group_number), - &(uint16_t){ cpu_to_le16(seg) }, - sizeof(uint16_t)); - smbios_add_field(41, - offsetof(struct smbios_type_41, bus_number), - &(uint8_t){ bus }, - sizeof(uint8_t)); - smbios_add_field(41, - offsetof(struct smbios_type_41, device_function_number), - &(uint8_t){ (dev << 3) | func }, - sizeof(uint8_t)); + if (address) { + int seg, bus; + unsigned int dev, func; + if (pci_parse_devaddr(address, &seg, &bus, &dev, &func) < 0) { + error_report("Invalid device address"); + exit(1); + } + + smbios_add_field(41, + offsetof(struct smbios_type_41, segment_group_number), + &(uint16_t){ cpu_to_le16(seg) }, + sizeof(uint16_t)); + smbios_add_field(41, + offsetof(struct smbios_type_41, bus_number), + &(uint8_t){ bus }, + sizeof(uint8_t)); + smbios_add_field(41, + offsetof(struct smbios_type_41, device_function_number), + &(uint8_t){ (dev << 3) | func }, + sizeof(uint8_t)); + } + + if (id) { + if (unresolved_devs) { + unresolved_devs_count++; + unresolved_devs = g_realloc(unresolved_devs, + sizeof(*unresolved_devs) * unresolved_devs_count); + } else { + unresolved_devs_count = 1; + unresolved_devs = g_malloc0(sizeof(*unresolved_devs)); + } + struct unresolved_devs *unresolved = + &unresolved_devs[unresolved_devs_count - 1]; + + unresolved->id = g_strdup(id); + smbios_add_field(41, + offsetof(struct smbios_type_41, segment_group_number), + &(uint16_t){ 0 }, /* QEMU supports only zero PCI domain. */ + sizeof(uint16_t)); + unresolved->bus_off = smbios_alloc_field_off(41, + offsetof(struct smbios_type_41, bus_number), + sizeof(uint8_t)); + unresolved->dev_func_off = smbios_alloc_field_off(41, + offsetof(struct smbios_type_41, device_function_number), + sizeof(uint8_t)); + } smbios_add_fields_set_end_marker(41); } +void smbios_resolve_devices(void) +{ + if (!unresolved_devs) { + return; + } + + unsigned int i; + struct unresolved_devs *unresolved = unresolved_devs; + for (i = 0; i < unresolved_devs_count; i++) { + DeviceState *qdev = qdev_find_recursive(sysbus_get_default(), + unresolved->id); + if (!qdev) { + error_report("smbios: Unable to find device \"%s\"", + unresolved->id); + exit(1); + } + + PCIDevice *dev = (PCIDevice *)object_dynamic_cast(OBJECT(qdev), + TYPE_PCI_DEVICE); + if (!dev) { + error_report("smbios: \"%s\" is not PCI device", unresolved->id); + exit(1); + } + + smbios_entries[unresolved->bus_off] = pci_bus_num(dev->bus); + smbios_entries[unresolved->dev_func_off] = dev->devfn; + + g_free(unresolved->id); + unresolved++; + } + + g_free(unresolved_devs); + unresolved_devs = NULL; +} + void smbios_entry_add(QemuOpts *opts) { Error *local_err = NULL; diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index a57ffc9..91a28a7 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -17,6 +17,7 @@ void smbios_entry_add(QemuOpts *opts); uint8_t *smbios_get_table(size_t *length); +void smbios_resolve_devices(void); /* * SMBIOS spec defined tables diff --git a/qemu-options.hx b/qemu-options.hx index a3b4b78..98298cf 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1323,6 +1323,8 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, " specify SMBIOS type 1 fields\n" "-smbios type=41,address=str,instance=n[,designation=str]\n" " [,status=on|off][,device-type=str|n]\n" + "-smbios type=41,id=str,instance=n[,designation=str]\n" + " [,status=on|off][,device-type=str|n]\n" " add SMBIOS type 41 fields (Onboard Devices Extended Information)\n" , QEMU_ARCH_I386) STEXI @item -smbios file=@var{binary} @@ -1336,9 +1338,10 @@ Specify SMBIOS type 0 fields Specify SMBIOS type 1 fields @item -smbios type=41,address=@var{str},instance=@var{n}[,designation=@var{str}][,status=on|off][,device-type=@var{str|n}] +@item -smbios type=41,id=@var{str},instance=@var{n}[,designation=@var{str}][,status=on|off][,device-type=@var{str|n}] Add SMBIOS type 41 fields (Onboard Devices Extended Information). Could be specified multiple times for different devices. Mandatory options are -@option{address} in form "[[<domain>:]<bus>:]<slot>.<func>" and +@option{address} in form "[[<domain>:]<bus>:]<slot>.<func>" or @option{id}, and @option{instance} number. @option{instance} shoud be in range [1, 255] and should be unique within specified @option{device-type}. @option{designation} is an optional string that somehow designates device. @option{status} is a device @@ -1365,6 +1368,9 @@ qemu-i386 \ -netdev user,id=hostnet0 \ -device e1000,netdev=hostnet0,id=net0,bus=pci.0,addr=0x1f \ -smbios type=41,address=00:1f.0,instance=1,designation="NIC 1",device-type=ethernet \ +-netdev user,id=hostnet1 \ +-device e1000,netdev=hostnet1,id=net1 \ +-smbios type=41,id=net1,instance=2,designation="NIC 2",device-type=ethernet \ < ... other qemu options ... > @end example ETEXI -- 1.8.4.1