virtio,pc,pci: bugfixes

Some minor fixes plus a big patchset from Igor fixing
 a regression with windows.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmX4NzsPHG1zdEByZWRo
 YXQuY29tAAoJECgfDbjSjVRpkp0H/1foAaDYrApMiIkji4aI94bq/fwTnu5CshNP
 +YEzwJCS4qbl67/Ix2Z+xVz7twjQbgGdLd6hb9ZypAQfclUk5tDoKyCmqHtQMakX
 T080FayOvWmUEostAw7MXvuz0HpJlgnJaJBn29l1hHjA/XXahKqcc705cup+W8hv
 F7xb6AoFcbdETMzNaoqekNaHiiYyQPITY9p/UYPLzj2zyLsspR9kBebIeA1yhtXw
 Tmc3+FMquoM2fMNxpwfhCBswg662MlOXhLN3dmyLqeJRl09x1GvaeJIGMY2MbefM
 RMMv0/jqwAyii5HXew2rPIbLdULGq+hSjZo2NOlx3EOjTCaOkXc=
 =XGMp
 -----END PGP SIGNATURE-----

Merge tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu into staging

virtio,pc,pci: bugfixes

Some minor fixes plus a big patchset from Igor fixing
a regression with windows.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

# -----BEGIN PGP SIGNATURE-----
#
# iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmX4NzsPHG1zdEByZWRo
# YXQuY29tAAoJECgfDbjSjVRpkp0H/1foAaDYrApMiIkji4aI94bq/fwTnu5CshNP
# +YEzwJCS4qbl67/Ix2Z+xVz7twjQbgGdLd6hb9ZypAQfclUk5tDoKyCmqHtQMakX
# T080FayOvWmUEostAw7MXvuz0HpJlgnJaJBn29l1hHjA/XXahKqcc705cup+W8hv
# F7xb6AoFcbdETMzNaoqekNaHiiYyQPITY9p/UYPLzj2zyLsspR9kBebIeA1yhtXw
# Tmc3+FMquoM2fMNxpwfhCBswg662MlOXhLN3dmyLqeJRl09x1GvaeJIGMY2MbefM
# RMMv0/jqwAyii5HXew2rPIbLdULGq+hSjZo2NOlx3EOjTCaOkXc=
# =XGMp
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 18 Mar 2024 12:44:43 GMT
# gpg:                using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469
# gpg:                issuer "mst@redhat.com"
# gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [full]
# gpg:                 aka "Michael S. Tsirkin <mst@redhat.com>" [full]
# Primary key fingerprint: 0270 606B 6F3C DF3D 0B17  0970 C350 3912 AFBE 8E67
#      Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA  8A0D 281F 0DB8 D28D 5469

* tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu: (24 commits)
  smbios: add extra comments to smbios_get_table_legacy()
  tests: acpi: update expected SSDT.dimmpxm blob
  pc/q35: set SMBIOS entry point type to 'auto' by default
  tests: acpi/smbios: whitelist expected blobs
  smbios: error out when building type 4 table is not possible
  smbios: in case of entry point is 'auto' try to build v2 tables 1st
  smbios: extend smbios-entry-point-type with 'auto' value
  smbios: clear smbios_type4_count before building tables
  smbios: get rid of global smbios_ep_type
  smbios: handle errors consistently
  smbios: build legacy mode code only for 'pc' machine
  smbios: rename/expose structures/bitmaps used by both legacy and modern code
  smbios: add smbios_add_usr_blob_size() helper
  smbios: don't check type4 structures in legacy mode
  smbios: avoid mangling user provided tables
  smbios: get rid of smbios_legacy global
  smbios: get rid of smbios_smp_sockets global
  smbios: cleanup smbios_get_tables() from legacy handling
  tests: smbios: add test for legacy mode CLI options
  tests: smbios: add test for -smbios type=11 option
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2024-03-19 10:25:15 +00:00
commit 9359197f0b
22 changed files with 558 additions and 331 deletions

View File

@ -29,7 +29,7 @@ bit 1
a guest panic has happened and will be handled by the guest; a guest panic has happened and will be handled by the guest;
the host should record it or report it, but should not affect the host should record it or report it, but should not affect
the execution of the guest. the execution of the guest.
bit 2 bit 2 (to be implemented)
a regular guest shutdown has happened and should be processed by the host a regular guest shutdown has happened and should be processed by the host
PCI Interface PCI Interface

View File

@ -1650,14 +1650,14 @@ static void virt_build_smbios(VirtMachineState *vms)
} }
smbios_set_defaults("QEMU", product, smbios_set_defaults("QEMU", product,
vmc->smbios_old_sys_ver ? "1.0" : mc->name, false, vmc->smbios_old_sys_ver ? "1.0" : mc->name,
true, SMBIOS_ENTRY_POINT_TYPE_64); true);
/* build the array of physical mem area from base_memmap */ /* build the array of physical mem area from base_memmap */
mem_array.address = vms->memmap[VIRT_MEM].base; mem_array.address = vms->memmap[VIRT_MEM].base;
mem_array.length = ms->ram_size; mem_array.length = ms->ram_size;
smbios_get_tables(ms, &mem_array, 1, smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64, &mem_array, 1,
&smbios_tables, &smbios_tables_len, &smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len, &smbios_anchor, &smbios_anchor_len,
&error_fatal); &error_fatal);

View File

@ -76,6 +76,7 @@ config I440FX
select PIIX select PIIX
select DIMM select DIMM
select SMBIOS select SMBIOS
select SMBIOS_LEGACY
select FW_CFG_DMA select FW_CFG_DMA
config ISAPC config ISAPC

View File

@ -48,7 +48,8 @@ const char *fw_cfg_arch_key_name(uint16_t key)
return NULL; return NULL;
} }
void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg) void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg,
SmbiosEntryPointType ep_type)
{ {
#ifdef CONFIG_SMBIOS #ifdef CONFIG_SMBIOS
uint8_t *smbios_tables, *smbios_anchor; uint8_t *smbios_tables, *smbios_anchor;
@ -63,17 +64,18 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
if (pcmc->smbios_defaults) { if (pcmc->smbios_defaults) {
/* These values are guest ABI, do not change */ /* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", mc->desc, mc->name, smbios_set_defaults("QEMU", mc->desc, mc->name,
pcmc->smbios_legacy_mode, pcmc->smbios_uuid_encoded, pcmc->smbios_uuid_encoded);
pcms->smbios_entry_point_type);
} }
/* tell smbios about cpuid version and features */ /* tell smbios about cpuid version and features */
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]); smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
smbios_tables = smbios_get_table_legacy(ms, &smbios_tables_len); if (pcmc->smbios_legacy_mode) {
if (smbios_tables) { smbios_tables = smbios_get_table_legacy(&smbios_tables_len,
&error_fatal);
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
smbios_tables, smbios_tables_len); smbios_tables, smbios_tables_len);
return;
} }
/* build the array of physical mem area from e820 table */ /* build the array of physical mem area from e820 table */
@ -87,7 +89,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
array_count++; array_count++;
} }
} }
smbios_get_tables(ms, mem_array, array_count, smbios_get_tables(ms, ep_type, mem_array, array_count,
&smbios_tables, &smbios_tables_len, &smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len, &smbios_anchor, &smbios_anchor_len,
&error_fatal); &error_fatal);

View File

@ -23,7 +23,8 @@
FWCfgState *fw_cfg_arch_create(MachineState *ms, FWCfgState *fw_cfg_arch_create(MachineState *ms,
uint16_t boot_cpus, uint16_t boot_cpus,
uint16_t apic_id_limit); uint16_t apic_id_limit);
void fw_cfg_build_smbios(PCMachineState *ms, FWCfgState *fw_cfg); void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg,
SmbiosEntryPointType ep_type);
void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg); void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg);
void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg); void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg);

View File

@ -672,7 +672,7 @@ void pc_machine_done(Notifier *notifier, void *data)
acpi_setup(); acpi_setup();
if (x86ms->fw_cfg) { if (x86ms->fw_cfg) {
fw_cfg_build_smbios(pcms, x86ms->fw_cfg); fw_cfg_build_smbios(pcms, x86ms->fw_cfg, pcms->smbios_entry_point_type);
fw_cfg_build_feature_control(MACHINE(pcms), x86ms->fw_cfg); fw_cfg_build_feature_control(MACHINE(pcms), x86ms->fw_cfg);
/* update FW_CFG_NB_CPUS to account for -device added CPUs */ /* update FW_CFG_NB_CPUS to account for -device added CPUs */
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
@ -1832,7 +1832,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
mc->nvdimm_supported = true; mc->nvdimm_supported = true;
mc->smp_props.dies_supported = true; mc->smp_props.dies_supported = true;
mc->default_ram_id = "pc.ram"; mc->default_ram_id = "pc.ram";
pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64; pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO;
object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size", object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g, pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g,

View File

@ -525,12 +525,16 @@ DEFINE_I440FX_MACHINE(v9_0, "pc-i440fx-9.0", NULL,
static void pc_i440fx_8_2_machine_options(MachineClass *m) static void pc_i440fx_8_2_machine_options(MachineClass *m)
{ {
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_9_0_machine_options(m); pc_i440fx_9_0_machine_options(m);
m->alias = NULL; m->alias = NULL;
m->is_default = false; m->is_default = false;
compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len); compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len);
compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len); compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len);
/* For pc-i44fx-8.2 and 8.1, use SMBIOS 3.X by default */
pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64;
} }
DEFINE_I440FX_MACHINE(v8_2, "pc-i440fx-8.2", NULL, DEFINE_I440FX_MACHINE(v8_2, "pc-i440fx-8.2", NULL,

View File

@ -376,11 +376,14 @@ DEFINE_Q35_MACHINE(v9_0, "pc-q35-9.0", NULL,
static void pc_q35_8_2_machine_options(MachineClass *m) static void pc_q35_8_2_machine_options(MachineClass *m)
{ {
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_q35_9_0_machine_options(m); pc_q35_9_0_machine_options(m);
m->alias = NULL; m->alias = NULL;
m->max_cpus = 1024; m->max_cpus = 1024;
compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len); compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len);
compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len); compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len);
/* For pc-q35-8.2 and 8.1, use SMBIOS 3.X by default */
pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64;
} }
DEFINE_Q35_MACHINE(v8_2, "pc-q35-8.2", NULL, DEFINE_Q35_MACHINE(v8_2, "pc-q35-8.2", NULL,

View File

@ -355,10 +355,11 @@ static void virt_build_smbios(LoongArchMachineState *lams)
return; return;
} }
smbios_set_defaults("QEMU", product, mc->name, false, smbios_set_defaults("QEMU", product, mc->name, true);
true, SMBIOS_ENTRY_POINT_TYPE_64);
smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len, smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64,
NULL, 0,
&smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len, &error_fatal); &smbios_anchor, &smbios_anchor_len, &error_fatal);
if (smbios_anchor) { if (smbios_anchor) {

View File

@ -1275,8 +1275,7 @@ static void virt_build_smbios(RISCVVirtState *s)
product = "KVM Virtual Machine"; product = "KVM Virtual Machine";
} }
smbios_set_defaults("QEMU", product, mc->name, false, smbios_set_defaults("QEMU", product, mc->name, true);
true, SMBIOS_ENTRY_POINT_TYPE_64);
if (riscv_is_32bit(&s->soc[0])) { if (riscv_is_32bit(&s->soc[0])) {
smbios_set_default_processor_family(0x200); smbios_set_default_processor_family(0x200);
@ -1288,7 +1287,8 @@ static void virt_build_smbios(RISCVVirtState *s)
mem_array.address = s->memmap[VIRT_DRAM].base; mem_array.address = s->memmap[VIRT_DRAM].base;
mem_array.length = ms->ram_size; mem_array.length = ms->ram_size;
smbios_get_tables(ms, &mem_array, 1, smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64,
&mem_array, 1,
&smbios_tables, &smbios_tables_len, &smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len, &smbios_anchor, &smbios_anchor_len,
&error_fatal); &error_fatal);

View File

@ -1,2 +1,4 @@
config SMBIOS config SMBIOS
bool bool
config SMBIOS_LEGACY
bool

View File

@ -4,5 +4,9 @@ smbios_ss.add(when: 'CONFIG_IPMI',
if_true: files('smbios_type_38.c'), if_true: files('smbios_type_38.c'),
if_false: files('smbios_type_38-stub.c')) if_false: files('smbios_type_38-stub.c'))
smbios_ss.add(when: 'CONFIG_SMBIOS_LEGACY',
if_true: files('smbios_legacy.c'),
if_false: files('smbios_legacy_stub.c'))
system_ss.add_all(when: 'CONFIG_SMBIOS', if_true: smbios_ss) system_ss.add_all(when: 'CONFIG_SMBIOS', if_true: smbios_ss)
system_ss.add(when: 'CONFIG_SMBIOS', if_false: files('smbios-stub.c')) system_ss.add(when: 'CONFIG_SMBIOS', if_false: files('smbios-stub.c'))

View File

@ -19,7 +19,6 @@
#include "qemu/units.h" #include "qemu/units.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/config-file.h" #include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
@ -31,60 +30,31 @@
#include "hw/pci/pci_device.h" #include "hw/pci/pci_device.h"
#include "smbios_build.h" #include "smbios_build.h"
/* legacy structures and constants for <= 2.0 machines */
struct smbios_header {
uint16_t length;
uint8_t type;
} QEMU_PACKED;
struct smbios_field {
struct smbios_header header;
uint8_t type;
uint16_t offset;
uint8_t data[];
} QEMU_PACKED;
struct smbios_table {
struct smbios_header header;
uint8_t data[];
} QEMU_PACKED;
#define SMBIOS_FIELD_ENTRY 0
#define SMBIOS_TABLE_ENTRY 1
static uint8_t *smbios_entries;
static size_t smbios_entries_len;
static bool smbios_legacy = true;
static bool smbios_uuid_encoded = true; static bool smbios_uuid_encoded = true;
/* end: legacy structures & constants for <= 2.0 machines */ /*
* SMBIOS tables provided by user with '-smbios file=<foo>' option
*/
uint8_t *usr_blobs;
size_t usr_blobs_len;
static unsigned usr_table_max;
static unsigned usr_table_cnt;
uint8_t *smbios_tables; uint8_t *smbios_tables;
size_t smbios_tables_len; size_t smbios_tables_len;
unsigned smbios_table_max; unsigned smbios_table_max;
unsigned smbios_table_cnt; unsigned smbios_table_cnt;
static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32;
static SmbiosEntryPoint ep; static SmbiosEntryPoint ep;
static int smbios_type4_count = 0; static int smbios_type4_count = 0;
static bool smbios_immutable;
static bool smbios_have_defaults; static bool smbios_have_defaults;
static uint32_t smbios_cpuid_version, smbios_cpuid_features, smbios_smp_sockets; static uint32_t smbios_cpuid_version, smbios_cpuid_features;
static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); DECLARE_BITMAP(smbios_have_binfile_bitmap, SMBIOS_MAX_TYPE + 1);
static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); DECLARE_BITMAP(smbios_have_fields_bitmap, SMBIOS_MAX_TYPE + 1);
static struct { smbios_type0_t smbios_type0;
const char *vendor, *version, *date; smbios_type1_t smbios_type1;
bool have_major_minor, uefi;
uint8_t major, minor;
} type0;
static struct {
const char *manufacturer, *product, *version, *serial, *sku, *family;
/* uuid is in qemu_uuid */
} type1;
static struct { static struct {
const char *manufacturer, *product, *version, *serial, *asset, *location; const char *manufacturer, *product, *version, *serial, *asset, *location;
@ -539,126 +509,33 @@ opts_init(smbios_register_config);
*/ */
#define SMBIOS_21_MAX_TABLES_LEN 0xffff #define SMBIOS_21_MAX_TABLES_LEN 0xffff
static void smbios_validate_table(MachineState *ms) static bool smbios_check_type4_count(uint32_t expected_t4_count, Error **errp)
{ {
uint32_t expect_t4_count = smbios_legacy ? if (smbios_type4_count && smbios_type4_count != expected_t4_count) {
ms->smp.cpus : smbios_smp_sockets; error_setg(errp, "Expected %d SMBIOS Type 4 tables, got %d instead",
expected_t4_count, smbios_type4_count);
if (smbios_type4_count && smbios_type4_count != expect_t4_count) { return false;
error_report("Expected %d SMBIOS Type 4 tables, got %d instead",
expect_t4_count, smbios_type4_count);
exit(1);
} }
return true;
}
if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp)
{
if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 &&
smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) { smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) {
error_report("SMBIOS 2.1 table length %zu exceeds %d", error_setg(errp, "SMBIOS 2.1 table length %zu exceeds %d",
smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN); smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN);
exit(1); return false;
} }
return true;
} }
/* legacy setup functions for <= 2.0 machines */
static void smbios_add_field(int type, int offset, const void *data, size_t len)
{
struct smbios_field *field;
if (!smbios_entries) {
smbios_entries_len = sizeof(uint16_t);
smbios_entries = g_malloc0(smbios_entries_len);
}
smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
sizeof(*field) + len);
field = (struct smbios_field *)(smbios_entries + smbios_entries_len);
field->header.type = SMBIOS_FIELD_ENTRY;
field->header.length = cpu_to_le16(sizeof(*field) + 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);
}
static void smbios_maybe_add_str(int type, int offset, const char *data)
{
if (data) {
smbios_add_field(type, offset, data, strlen(data) + 1);
}
}
static void smbios_build_type_0_fields(void)
{
smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str),
type0.vendor);
smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str),
type0.version);
smbios_maybe_add_str(0, offsetof(struct smbios_type_0,
bios_release_date_str),
type0.date);
if (type0.have_major_minor) {
smbios_add_field(0, offsetof(struct smbios_type_0,
system_bios_major_release),
&type0.major, 1);
smbios_add_field(0, offsetof(struct smbios_type_0,
system_bios_minor_release),
&type0.minor, 1);
}
}
static void smbios_build_type_1_fields(void)
{
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str),
type1.manufacturer);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str),
type1.product);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str),
type1.version);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str),
type1.serial);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str),
type1.sku);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str),
type1.family);
if (qemu_uuid_set) {
/* We don't encode the UUID in the "wire format" here because this
* function is for legacy mode and needs to keep the guest ABI, and
* because we don't know what's the SMBIOS version advertised by the
* BIOS.
*/
smbios_add_field(1, offsetof(struct smbios_type_1, uuid),
&qemu_uuid, 16);
}
}
uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length)
{
if (!smbios_legacy) {
*length = 0;
return NULL;
}
if (!smbios_immutable) {
smbios_build_type_0_fields();
smbios_build_type_1_fields();
smbios_validate_table(ms);
smbios_immutable = true;
}
*length = smbios_entries_len;
return smbios_entries;
}
/* end: legacy setup functions for <= 2.0 machines */
bool smbios_skip_table(uint8_t type, bool required_table) bool smbios_skip_table(uint8_t type, bool required_table)
{ {
if (test_bit(type, have_binfile_bitmap)) { if (test_bit(type, smbios_have_binfile_bitmap)) {
return true; /* user provided their own binary blob(s) */ return true; /* user provided their own binary blob(s) */
} }
if (test_bit(type, have_fields_bitmap)) { if (test_bit(type, smbios_have_fields_bitmap)) {
return false; /* user provided fields via command line */ return false; /* user provided fields via command line */
} }
if (smbios_have_defaults && required_table) { if (smbios_have_defaults && required_table) {
@ -686,25 +563,25 @@ static void smbios_build_type_0_table(void)
{ {
SMBIOS_BUILD_TABLE_PRE(0, T0_BASE, false); /* optional, leave up to BIOS */ SMBIOS_BUILD_TABLE_PRE(0, T0_BASE, false); /* optional, leave up to BIOS */
SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor); SMBIOS_TABLE_SET_STR(0, vendor_str, smbios_type0.vendor);
SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version); SMBIOS_TABLE_SET_STR(0, bios_version_str, smbios_type0.version);
t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */ t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */
SMBIOS_TABLE_SET_STR(0, bios_release_date_str, type0.date); SMBIOS_TABLE_SET_STR(0, bios_release_date_str, smbios_type0.date);
t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */ t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */
t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */ t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */
t->bios_characteristics_extension_bytes[0] = 0; t->bios_characteristics_extension_bytes[0] = 0;
t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */ t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */
if (type0.uefi) { if (smbios_type0.uefi) {
t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */ t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */
} }
if (type0.have_major_minor) { if (smbios_type0.have_major_minor) {
t->system_bios_major_release = type0.major; t->system_bios_major_release = smbios_type0.major;
t->system_bios_minor_release = type0.minor; t->system_bios_minor_release = smbios_type0.minor;
} else { } else {
t->system_bios_major_release = 0; t->system_bios_major_release = 0;
t->system_bios_minor_release = 0; t->system_bios_minor_release = 0;
@ -734,18 +611,18 @@ static void smbios_build_type_1_table(void)
{ {
SMBIOS_BUILD_TABLE_PRE(1, T1_BASE, true); /* required */ SMBIOS_BUILD_TABLE_PRE(1, T1_BASE, true); /* required */
SMBIOS_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer); SMBIOS_TABLE_SET_STR(1, manufacturer_str, smbios_type1.manufacturer);
SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product); SMBIOS_TABLE_SET_STR(1, product_name_str, smbios_type1.product);
SMBIOS_TABLE_SET_STR(1, version_str, type1.version); SMBIOS_TABLE_SET_STR(1, version_str, smbios_type1.version);
SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial); SMBIOS_TABLE_SET_STR(1, serial_number_str, smbios_type1.serial);
if (qemu_uuid_set) { if (qemu_uuid_set) {
smbios_encode_uuid(&t->uuid, &qemu_uuid); smbios_encode_uuid(&t->uuid, &qemu_uuid);
} else { } else {
memset(&t->uuid, 0, 16); memset(&t->uuid, 0, 16);
} }
t->wake_up_type = 0x06; /* power switch */ t->wake_up_type = 0x06; /* power switch */
SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku); SMBIOS_TABLE_SET_STR(1, sku_number_str, smbios_type1.sku);
SMBIOS_TABLE_SET_STR(1, family_str, type1.family); SMBIOS_TABLE_SET_STR(1, family_str, smbios_type1.family);
SMBIOS_BUILD_TABLE_POST; SMBIOS_BUILD_TABLE_POST;
} }
@ -791,14 +668,16 @@ static void smbios_build_type_3_table(void)
SMBIOS_BUILD_TABLE_POST; SMBIOS_BUILD_TABLE_POST;
} }
static void smbios_build_type_4_table(MachineState *ms, unsigned instance) static void smbios_build_type_4_table(MachineState *ms, unsigned instance,
SmbiosEntryPointType ep_type,
Error **errp)
{ {
char sock_str[128]; char sock_str[128];
size_t tbl_len = SMBIOS_TYPE_4_LEN_V28; size_t tbl_len = SMBIOS_TYPE_4_LEN_V28;
unsigned threads_per_socket; unsigned threads_per_socket;
unsigned cores_per_socket; unsigned cores_per_socket;
if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_64) { if (ep_type == SMBIOS_ENTRY_POINT_TYPE_64) {
tbl_len = SMBIOS_TYPE_4_LEN_V30; tbl_len = SMBIOS_TYPE_4_LEN_V30;
} }
@ -845,6 +724,12 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance)
if (tbl_len == SMBIOS_TYPE_4_LEN_V30) { if (tbl_len == SMBIOS_TYPE_4_LEN_V30) {
t->core_count2 = t->core_enabled2 = cpu_to_le16(cores_per_socket); t->core_count2 = t->core_enabled2 = cpu_to_le16(cores_per_socket);
t->thread_count2 = cpu_to_le16(threads_per_socket); t->thread_count2 = cpu_to_le16(threads_per_socket);
} else if (t->core_count == 0xFF || t->thread_count == 0xFF) {
error_setg(errp, "SMBIOS 2.0 doesn't support number of processor "
"cores/threads more than 255, use "
"-machine smbios-entry-point-type=64 option to enable "
"SMBIOS 3.0 support");
return;
} }
SMBIOS_BUILD_TABLE_POST; SMBIOS_BUILD_TABLE_POST;
@ -1132,31 +1017,15 @@ void smbios_set_default_processor_family(uint16_t processor_family)
} }
void smbios_set_defaults(const char *manufacturer, const char *product, void smbios_set_defaults(const char *manufacturer, const char *product,
const char *version, bool legacy_mode, const char *version,
bool uuid_encoded, SmbiosEntryPointType ep_type) bool uuid_encoded)
{ {
smbios_have_defaults = true; smbios_have_defaults = true;
smbios_legacy = legacy_mode;
smbios_uuid_encoded = uuid_encoded; smbios_uuid_encoded = uuid_encoded;
smbios_ep_type = ep_type;
/* drop unwanted version of command-line file blob(s) */ SMBIOS_SET_DEFAULT(smbios_type1.manufacturer, manufacturer);
if (smbios_legacy) { SMBIOS_SET_DEFAULT(smbios_type1.product, product);
g_free(smbios_tables); SMBIOS_SET_DEFAULT(smbios_type1.version, version);
/* in legacy mode, also complain if fields were given for types > 1 */
if (find_next_bit(have_fields_bitmap,
SMBIOS_MAX_TYPE+1, 2) < SMBIOS_MAX_TYPE+1) {
error_report("can't process fields for smbios "
"types > 1 on machine versions < 2.1!");
exit(1);
}
} else {
g_free(smbios_entries);
}
SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer);
SMBIOS_SET_DEFAULT(type1.product, product);
SMBIOS_SET_DEFAULT(type1.version, version);
SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer);
SMBIOS_SET_DEFAULT(type2.product, product); SMBIOS_SET_DEFAULT(type2.product, product);
SMBIOS_SET_DEFAULT(type2.version, version); SMBIOS_SET_DEFAULT(type2.version, version);
@ -1169,9 +1038,9 @@ void smbios_set_defaults(const char *manufacturer, const char *product,
SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer); SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer);
} }
static void smbios_entry_point_setup(void) static void smbios_entry_point_setup(SmbiosEntryPointType ep_type)
{ {
switch (smbios_ep_type) { switch (ep_type) {
case SMBIOS_ENTRY_POINT_TYPE_32: case SMBIOS_ENTRY_POINT_TYPE_32:
memcpy(ep.ep21.anchor_string, "_SM_", 4); memcpy(ep.ep21.anchor_string, "_SM_", 4);
memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5); memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5);
@ -1220,7 +1089,8 @@ static void smbios_entry_point_setup(void)
} }
} }
void smbios_get_tables(MachineState *ms, static bool smbios_get_tables_ep(MachineState *ms,
SmbiosEntryPointType ep_type,
const struct smbios_phys_mem_area *mem_array, const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size, const unsigned int mem_array_size,
uint8_t **tables, size_t *tables_len, uint8_t **tables, size_t *tables_len,
@ -1228,78 +1098,87 @@ void smbios_get_tables(MachineState *ms,
Error **errp) Error **errp)
{ {
unsigned i, dimm_cnt, offset; unsigned i, dimm_cnt, offset;
ERRP_GUARD();
if (smbios_legacy) { assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 ||
*tables = *anchor = NULL; ep_type == SMBIOS_ENTRY_POINT_TYPE_64);
*tables_len = *anchor_len = 0;
return; g_free(smbios_tables);
smbios_type4_count = 0;
smbios_tables = g_memdup2(usr_blobs, usr_blobs_len);
smbios_tables_len = usr_blobs_len;
smbios_table_max = usr_table_max;
smbios_table_cnt = usr_table_cnt;
smbios_build_type_0_table();
smbios_build_type_1_table();
smbios_build_type_2_table();
smbios_build_type_3_table();
assert(ms->smp.sockets >= 1);
for (i = 0; i < ms->smp.sockets; i++) {
smbios_build_type_4_table(ms, i, ep_type, errp);
if (*errp) {
goto err_exit;
}
} }
if (!smbios_immutable) { smbios_build_type_8_table();
smbios_build_type_0_table(); smbios_build_type_9_table(errp);
smbios_build_type_1_table(); smbios_build_type_11_table();
smbios_build_type_2_table();
smbios_build_type_3_table();
smbios_smp_sockets = ms->smp.sockets;
assert(smbios_smp_sockets >= 1);
for (i = 0; i < smbios_smp_sockets; i++) {
smbios_build_type_4_table(ms, i);
}
smbios_build_type_8_table();
smbios_build_type_9_table(errp);
smbios_build_type_11_table();
#define MAX_DIMM_SZ (16 * GiB) #define MAX_DIMM_SZ (16 * GiB)
#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ #define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \
: ((current_machine->ram_size - 1) % MAX_DIMM_SZ) + 1) : ((current_machine->ram_size - 1) % MAX_DIMM_SZ) + 1)
dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) /
MAX_DIMM_SZ;
/* /*
* The offset determines if we need to keep additional space between * The offset determines if we need to keep additional space between
* table 17 and table 19 header handle numbers so that they do * table 17 and table 19 header handle numbers so that they do
* not overlap. For example, for a VM with larger than 8 TB guest * not overlap. For example, for a VM with larger than 8 TB guest
* memory and DIMM like chunks of 16 GiB, the default space between * memory and DIMM like chunks of 16 GiB, the default space between
* the two tables (T19_BASE - T17_BASE = 512) is not enough. * the two tables (T19_BASE - T17_BASE = 512) is not enough.
*/ */
offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \ offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \
dimm_cnt - (T19_BASE - T17_BASE) : 0; dimm_cnt - (T19_BASE - T17_BASE) : 0;
smbios_build_type_16_table(dimm_cnt); smbios_build_type_16_table(dimm_cnt);
for (i = 0; i < dimm_cnt; i++) { for (i = 0; i < dimm_cnt; i++) {
smbios_build_type_17_table(i, GET_DIMM_SZ); smbios_build_type_17_table(i, GET_DIMM_SZ);
}
for (i = 0; i < mem_array_size; i++) {
smbios_build_type_19_table(i, offset, mem_array[i].address,
mem_array[i].length);
}
/*
* make sure 16 bit handle numbers in the headers of tables 19
* and 32 do not overlap.
*/
assert((mem_array_size + offset) < (T32_BASE - T19_BASE));
smbios_build_type_32_table();
smbios_build_type_38_table();
smbios_build_type_41_table(errp);
smbios_build_type_127_table();
smbios_validate_table(ms);
smbios_entry_point_setup();
smbios_immutable = true;
} }
for (i = 0; i < mem_array_size; i++) {
smbios_build_type_19_table(i, offset, mem_array[i].address,
mem_array[i].length);
}
/*
* make sure 16 bit handle numbers in the headers of tables 19
* and 32 do not overlap.
*/
assert((mem_array_size + offset) < (T32_BASE - T19_BASE));
smbios_build_type_32_table();
smbios_build_type_38_table();
smbios_build_type_41_table(errp);
smbios_build_type_127_table();
if (!smbios_check_type4_count(ms->smp.sockets, errp)) {
goto err_exit;
}
if (!smbios_validate_table(ep_type, errp)) {
goto err_exit;
}
smbios_entry_point_setup(ep_type);
/* return tables blob and entry point (anchor), and their sizes */ /* return tables blob and entry point (anchor), and their sizes */
*tables = smbios_tables; *tables = smbios_tables;
*tables_len = smbios_tables_len; *tables_len = smbios_tables_len;
*anchor = (uint8_t *)&ep; *anchor = (uint8_t *)&ep;
/* calculate length based on anchor string */ /* calculate length based on anchor string */
if (!strncmp((char *)&ep, "_SM_", 4)) { if (!strncmp((char *)&ep, "_SM_", 4)) {
*anchor_len = sizeof(struct smbios_21_entry_point); *anchor_len = sizeof(struct smbios_21_entry_point);
@ -1308,6 +1187,57 @@ void smbios_get_tables(MachineState *ms,
} else { } else {
abort(); abort();
} }
return true;
err_exit:
g_free(smbios_tables);
smbios_tables = NULL;
return false;
}
void smbios_get_tables(MachineState *ms,
SmbiosEntryPointType ep_type,
const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size,
uint8_t **tables, size_t *tables_len,
uint8_t **anchor, size_t *anchor_len,
Error **errp)
{
Error *local_err = NULL;
bool is_valid;
ERRP_GUARD();
switch (ep_type) {
case SMBIOS_ENTRY_POINT_TYPE_AUTO:
case SMBIOS_ENTRY_POINT_TYPE_32:
is_valid = smbios_get_tables_ep(ms, SMBIOS_ENTRY_POINT_TYPE_32,
mem_array, mem_array_size,
tables, tables_len,
anchor, anchor_len,
&local_err);
if (is_valid || ep_type != SMBIOS_ENTRY_POINT_TYPE_AUTO) {
break;
}
/*
* fall through in case AUTO endpoint is selected and
* SMBIOS 2.x tables can't be generated, to try if SMBIOS 3.x
* tables would work
*/
case SMBIOS_ENTRY_POINT_TYPE_64:
error_free(local_err);
local_err = NULL;
is_valid = smbios_get_tables_ep(ms, SMBIOS_ENTRY_POINT_TYPE_64,
mem_array, mem_array_size,
tables, tables_len,
anchor, anchor_len,
&local_err);
break;
default:
abort();
}
if (!is_valid) {
error_propagate(errp, local_err);
}
} }
static void save_opt(const char **dest, QemuOpts *opts, const char *name) static void save_opt(const char **dest, QemuOpts *opts, const char *name)
@ -1393,13 +1323,10 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
{ {
const char *val; const char *val;
assert(!smbios_immutable);
val = qemu_opt_get(opts, "file"); val = qemu_opt_get(opts, "file");
if (val) { if (val) {
struct smbios_structure_header *header; struct smbios_structure_header *header;
int size; size_t size;
struct smbios_table *table; /* legacy mode only */
if (!qemu_opts_validate(opts, qemu_smbios_file_opts, errp)) { if (!qemu_opts_validate(opts, qemu_smbios_file_opts, errp)) {
return; return;
@ -1416,9 +1343,9 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
* (except in legacy mode, where the second '\0' is implicit and * (except in legacy mode, where the second '\0' is implicit and
* will be inserted by the BIOS). * will be inserted by the BIOS).
*/ */
smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size); usr_blobs = g_realloc(usr_blobs, usr_blobs_len + size);
header = (struct smbios_structure_header *)(smbios_tables + header = (struct smbios_structure_header *)(usr_blobs +
smbios_tables_len); usr_blobs_len);
if (load_image_size(val, (uint8_t *)header, size) != size) { if (load_image_size(val, (uint8_t *)header, size) != size) {
error_setg(errp, "Failed to load SMBIOS file %s", val); error_setg(errp, "Failed to load SMBIOS file %s", val);
@ -1426,47 +1353,30 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
} }
if (header->type <= SMBIOS_MAX_TYPE) { if (header->type <= SMBIOS_MAX_TYPE) {
if (test_bit(header->type, have_fields_bitmap)) { if (test_bit(header->type, smbios_have_fields_bitmap)) {
error_setg(errp, error_setg(errp,
"can't load type %d struct, fields already specified!", "can't load type %d struct, fields already specified!",
header->type); header->type);
return; return;
} }
set_bit(header->type, have_binfile_bitmap); set_bit(header->type, smbios_have_binfile_bitmap);
} }
if (header->type == 4) { if (header->type == 4) {
smbios_type4_count++; smbios_type4_count++;
} }
smbios_tables_len += size; /*
if (size > smbios_table_max) { * preserve blob size for legacy mode so it could build its
smbios_table_max = size; * blobs flavor from 'usr_blobs'
}
smbios_table_cnt++;
/* add a copy of the newly loaded blob to legacy smbios_entries */
/* NOTE: This code runs before smbios_set_defaults(), so we don't
* yet know which mode (legacy vs. aggregate-table) will be
* required. We therefore add the binary blob to both legacy
* (smbios_entries) and aggregate (smbios_tables) tables, and
* delete the one we don't need from smbios_set_defaults(),
* once we know which machine version has been requested.
*/ */
if (!smbios_entries) { smbios_add_usr_blob_size(size);
smbios_entries_len = sizeof(uint16_t);
smbios_entries = g_malloc0(smbios_entries_len); usr_blobs_len += size;
if (size > usr_table_max) {
usr_table_max = size;
} }
smbios_entries = g_realloc(smbios_entries, smbios_entries_len + usr_table_cnt++;
size + sizeof(*table));
table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
table->header.type = SMBIOS_TABLE_ENTRY;
table->header.length = cpu_to_le16(sizeof(*table) + size);
memcpy(table->data, header, size);
smbios_entries_len += sizeof(*table) + size;
(*(uint16_t *)smbios_entries) =
cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
/* end: add a copy of the newly loaded blob to legacy smbios_entries */
return; return;
} }
@ -1480,41 +1390,42 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
return; return;
} }
if (test_bit(type, have_binfile_bitmap)) { if (test_bit(type, smbios_have_binfile_bitmap)) {
error_setg(errp, "can't add fields, binary file already loaded!"); error_setg(errp, "can't add fields, binary file already loaded!");
return; return;
} }
set_bit(type, have_fields_bitmap); set_bit(type, smbios_have_fields_bitmap);
switch (type) { switch (type) {
case 0: case 0:
if (!qemu_opts_validate(opts, qemu_smbios_type0_opts, errp)) { if (!qemu_opts_validate(opts, qemu_smbios_type0_opts, errp)) {
return; return;
} }
save_opt(&type0.vendor, opts, "vendor"); save_opt(&smbios_type0.vendor, opts, "vendor");
save_opt(&type0.version, opts, "version"); save_opt(&smbios_type0.version, opts, "version");
save_opt(&type0.date, opts, "date"); save_opt(&smbios_type0.date, opts, "date");
type0.uefi = qemu_opt_get_bool(opts, "uefi", false); smbios_type0.uefi = qemu_opt_get_bool(opts, "uefi", false);
val = qemu_opt_get(opts, "release"); val = qemu_opt_get(opts, "release");
if (val) { if (val) {
if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { if (sscanf(val, "%hhu.%hhu", &smbios_type0.major,
&smbios_type0.minor) != 2) {
error_setg(errp, "Invalid release"); error_setg(errp, "Invalid release");
return; return;
} }
type0.have_major_minor = true; smbios_type0.have_major_minor = true;
} }
return; return;
case 1: case 1:
if (!qemu_opts_validate(opts, qemu_smbios_type1_opts, errp)) { if (!qemu_opts_validate(opts, qemu_smbios_type1_opts, errp)) {
return; return;
} }
save_opt(&type1.manufacturer, opts, "manufacturer"); save_opt(&smbios_type1.manufacturer, opts, "manufacturer");
save_opt(&type1.product, opts, "product"); save_opt(&smbios_type1.product, opts, "product");
save_opt(&type1.version, opts, "version"); save_opt(&smbios_type1.version, opts, "version");
save_opt(&type1.serial, opts, "serial"); save_opt(&smbios_type1.serial, opts, "serial");
save_opt(&type1.sku, opts, "sku"); save_opt(&smbios_type1.sku, opts, "sku");
save_opt(&type1.family, opts, "family"); save_opt(&smbios_type1.family, opts, "family");
val = qemu_opt_get(opts, "uuid"); val = qemu_opt_get(opts, "uuid");
if (val) { if (val) {
@ -1592,12 +1503,15 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
t = g_new0(struct type9_instance, 1); t = g_new0(struct type9_instance, 1);
save_opt(&t->slot_designation, opts, "slot_designation"); save_opt(&t->slot_designation, opts, "slot_designation");
t->slot_type = qemu_opt_get_number(opts, "slot_type", 0); t->slot_type = qemu_opt_get_number(opts, "slot_type", 0);
t->slot_data_bus_width = qemu_opt_get_number(opts, "slot_data_bus_width", 0); t->slot_data_bus_width =
qemu_opt_get_number(opts, "slot_data_bus_width", 0);
t->current_usage = qemu_opt_get_number(opts, "current_usage", 0); t->current_usage = qemu_opt_get_number(opts, "current_usage", 0);
t->slot_length = qemu_opt_get_number(opts, "slot_length", 0); t->slot_length = qemu_opt_get_number(opts, "slot_length", 0);
t->slot_id = qemu_opt_get_number(opts, "slot_id", 0); t->slot_id = qemu_opt_get_number(opts, "slot_id", 0);
t->slot_characteristics1 = qemu_opt_get_number(opts, "slot_characteristics1", 0); t->slot_characteristics1 =
t->slot_characteristics2 = qemu_opt_get_number(opts, "slot_characteristics2", 0); qemu_opt_get_number(opts, "slot_characteristics1", 0);
t->slot_characteristics2 =
qemu_opt_get_number(opts, "slot_characteristics2", 0);
save_opt(&t->pcidev, opts, "pcidev"); save_opt(&t->pcidev, opts, "pcidev");
QTAILQ_INSERT_TAIL(&type9, t, next); QTAILQ_INSERT_TAIL(&type9, t, next);
return; return;

192
hw/smbios/smbios_legacy.c Normal file
View File

@ -0,0 +1,192 @@
/*
* SMBIOS legacy support
*
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2013 Red Hat, Inc.
*
* Authors:
* Alex Williamson <alex.williamson@hp.com>
* Markus Armbruster <armbru@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "qemu/bswap.h"
#include "hw/firmware/smbios.h"
#include "sysemu/sysemu.h"
#include "qapi/error.h"
struct smbios_header {
uint16_t length;
uint8_t type;
} QEMU_PACKED;
struct smbios_field {
struct smbios_header header;
uint8_t type;
uint16_t offset;
uint8_t data[];
} QEMU_PACKED;
struct smbios_table {
struct smbios_header header;
uint8_t data[];
} QEMU_PACKED;
#define SMBIOS_FIELD_ENTRY 0
#define SMBIOS_TABLE_ENTRY 1
static uint8_t *smbios_entries;
static size_t smbios_entries_len;
GArray *usr_blobs_sizes;
void smbios_add_usr_blob_size(size_t size)
{
if (!usr_blobs_sizes) {
usr_blobs_sizes = g_array_new(false, false, sizeof(size_t));
}
g_array_append_val(usr_blobs_sizes, size);
}
static void smbios_add_field(int type, int offset, const void *data, size_t len)
{
struct smbios_field *field;
if (!smbios_entries) {
smbios_entries_len = sizeof(uint16_t);
smbios_entries = g_malloc0(smbios_entries_len);
}
smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
sizeof(*field) + len);
field = (struct smbios_field *)(smbios_entries + smbios_entries_len);
field->header.type = SMBIOS_FIELD_ENTRY;
field->header.length = cpu_to_le16(sizeof(*field) + 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);
}
static void smbios_maybe_add_str(int type, int offset, const char *data)
{
if (data) {
smbios_add_field(type, offset, data, strlen(data) + 1);
}
}
static void smbios_build_type_0_fields(void)
{
smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str),
smbios_type0.vendor);
smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str),
smbios_type0.version);
smbios_maybe_add_str(0, offsetof(struct smbios_type_0,
bios_release_date_str),
smbios_type0.date);
if (smbios_type0.have_major_minor) {
smbios_add_field(0, offsetof(struct smbios_type_0,
system_bios_major_release),
&smbios_type0.major, 1);
smbios_add_field(0, offsetof(struct smbios_type_0,
system_bios_minor_release),
&smbios_type0.minor, 1);
}
}
static void smbios_build_type_1_fields(void)
{
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str),
smbios_type1.manufacturer);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str),
smbios_type1.product);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str),
smbios_type1.version);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str),
smbios_type1.serial);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str),
smbios_type1.sku);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str),
smbios_type1.family);
if (qemu_uuid_set) {
/*
* We don't encode the UUID in the "wire format" here because this
* function is for legacy mode and needs to keep the guest ABI, and
* because we don't know what's the SMBIOS version advertised by the
* BIOS.
*/
smbios_add_field(1, offsetof(struct smbios_type_1, uuid),
&qemu_uuid, 16);
}
}
uint8_t *smbios_get_table_legacy(size_t *length, Error **errp)
{
int i;
size_t usr_offset;
/* complain if fields were given for types > 1 */
if (find_next_bit(smbios_have_fields_bitmap,
SMBIOS_MAX_TYPE + 1, 2) < SMBIOS_MAX_TYPE + 1) {
error_setg(errp, "can't process fields for smbios "
"types > 1 on machine versions < 2.1!");
goto err_exit;
}
if (test_bit(4, smbios_have_binfile_bitmap)) {
error_setg(errp, "can't process table for smbios "
"type 4 on machine versions < 2.1!");
goto err_exit;
}
g_free(smbios_entries);
smbios_entries_len = sizeof(uint16_t);
smbios_entries = g_malloc0(smbios_entries_len);
/*
* build a set of legacy smbios_table entries using user provided blobs
*/
for (i = 0, usr_offset = 0; usr_blobs_sizes && i < usr_blobs_sizes->len;
i++)
{
struct smbios_table *table;
struct smbios_structure_header *header;
size_t size = g_array_index(usr_blobs_sizes, size_t, i);
header = (struct smbios_structure_header *)(usr_blobs + usr_offset);
smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
size + sizeof(*table));
table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
table->header.type = SMBIOS_TABLE_ENTRY;
table->header.length = cpu_to_le16(sizeof(*table) + size);
memcpy(table->data, header, size);
smbios_entries_len += sizeof(*table) + size;
/*
* update number of entries in the blob,
* see SeaBIOS: qemu_cfg_legacy():QEMU_CFG_SMBIOS_ENTRIES
*/
(*(uint16_t *)smbios_entries) =
cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
usr_offset += size;
}
smbios_build_type_0_fields();
smbios_build_type_1_fields();
if (!smbios_validate_table(SMBIOS_ENTRY_POINT_TYPE_32, errp)) {
goto err_exit;
}
*length = smbios_entries_len;
return smbios_entries;
err_exit:
g_free(smbios_entries);
return NULL;
}

View File

@ -0,0 +1,15 @@
/*
* IPMI SMBIOS firmware handling
*
* Copyright (c) 2024 Igor Mammedov, Red Hat, Inc.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "hw/firmware/smbios.h"
void smbios_add_usr_blob_size(size_t size)
{
}

View File

@ -2,6 +2,7 @@
#define QEMU_SMBIOS_H #define QEMU_SMBIOS_H
#include "qapi/qapi-types-machine.h" #include "qapi/qapi-types-machine.h"
#include "qemu/bitmap.h"
/* /*
* SMBIOS Support * SMBIOS Support
@ -16,8 +17,26 @@
* *
*/ */
extern uint8_t *usr_blobs;
extern GArray *usr_blobs_sizes;
typedef struct {
const char *vendor, *version, *date;
bool have_major_minor, uefi;
uint8_t major, minor;
} smbios_type0_t;
extern smbios_type0_t smbios_type0;
typedef struct {
const char *manufacturer, *product, *version, *serial, *sku, *family;
/* uuid is in qemu_uuid */
} smbios_type1_t;
extern smbios_type1_t smbios_type1;
#define SMBIOS_MAX_TYPE 127 #define SMBIOS_MAX_TYPE 127
extern DECLARE_BITMAP(smbios_have_binfile_bitmap, SMBIOS_MAX_TYPE + 1);
extern DECLARE_BITMAP(smbios_have_fields_bitmap, SMBIOS_MAX_TYPE + 1);
#define offsetofend(TYPE, MEMBER) \ #define offsetofend(TYPE, MEMBER) \
(offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
@ -307,14 +326,17 @@ struct smbios_type_127 {
struct smbios_structure_header header; struct smbios_structure_header header;
} QEMU_PACKED; } QEMU_PACKED;
bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp);
void smbios_add_usr_blob_size(size_t size);
void smbios_entry_add(QemuOpts *opts, Error **errp); void smbios_entry_add(QemuOpts *opts, Error **errp);
void smbios_set_cpuid(uint32_t version, uint32_t features); void smbios_set_cpuid(uint32_t version, uint32_t features);
void smbios_set_defaults(const char *manufacturer, const char *product, void smbios_set_defaults(const char *manufacturer, const char *product,
const char *version, bool legacy_mode, const char *version,
bool uuid_encoded, SmbiosEntryPointType ep_type); bool uuid_encoded);
void smbios_set_default_processor_family(uint16_t processor_family); void smbios_set_default_processor_family(uint16_t processor_family);
uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length); uint8_t *smbios_get_table_legacy(size_t *length, Error **errp);
void smbios_get_tables(MachineState *ms, void smbios_get_tables(MachineState *ms,
SmbiosEntryPointType ep_type,
const struct smbios_phys_mem_area *mem_array, const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size, const unsigned int mem_array_size,
uint8_t **tables, size_t *tables_len, uint8_t **tables, size_t *tables_len,

View File

@ -107,9 +107,9 @@
# #
# @16: 16.0GT/s # @16: 16.0GT/s
# #
# @32: 32.0GT/s # @32: 32.0GT/s (since 9.0)
# #
# @64: 64.0GT/s # @64: 64.0GT/s (since 9.0)
# #
# Since: 4.0 # Since: 4.0
## ##

View File

@ -1797,10 +1797,13 @@
# #
# @64: SMBIOS version 3.0 (64-bit) Entry Point # @64: SMBIOS version 3.0 (64-bit) Entry Point
# #
# @auto: Either 2.x or 3.x SMBIOS version, 2.x if configuration can be
# described by it and 3.x otherwise (since: 9.0)
#
# Since: 7.0 # Since: 7.0
## ##
{ 'enum': 'SmbiosEntryPointType', { 'enum': 'SmbiosEntryPointType',
'data': [ '32', '64' ] } 'data': [ '32', '64', 'auto' ] }
## ##
# @MemorySizeConfiguration: # @MemorySizeConfiguration:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -858,6 +858,27 @@ static void test_vm_prepare(const char *params, test_data *data)
g_free(args); g_free(args);
} }
static void process_smbios_tables_noexit(test_data *data)
{
/*
* TODO: make SMBIOS tests work with UEFI firmware,
* Bug on uefi-test-tools to provide entry point:
* https://bugs.launchpad.net/qemu/+bug/1821884
*/
if (!(data->uefi_fl1 && data->uefi_fl2)) {
SmbiosEntryPointType ep_type = test_smbios_entry_point(data);
test_smbios_structs(data, ep_type);
}
}
static void test_smbios(const char *params, test_data *data)
{
test_vm_prepare(params, data);
boot_sector_test(data->qts);
process_smbios_tables_noexit(data);
qtest_quit(data->qts);
}
static void process_acpi_tables_noexit(test_data *data) static void process_acpi_tables_noexit(test_data *data)
{ {
test_acpi_load_tables(data); test_acpi_load_tables(data);
@ -868,15 +889,7 @@ static void process_acpi_tables_noexit(test_data *data)
test_acpi_asl(data); test_acpi_asl(data);
} }
/* process_smbios_tables_noexit(data);
* TODO: make SMBIOS tests work with UEFI firmware,
* Bug on uefi-test-tools to provide entry point:
* https://bugs.launchpad.net/qemu/+bug/1821884
*/
if (!(data->uefi_fl1 && data->uefi_fl2)) {
SmbiosEntryPointType ep_type = test_smbios_entry_point(data);
test_smbios_structs(data, ep_type);
}
} }
static void process_acpi_tables(test_data *data) static void process_acpi_tables(test_data *data)
@ -2064,6 +2077,50 @@ static void test_acpi_q35_pvpanic_isa(void)
free_test_data(&data); free_test_data(&data);
} }
static void test_acpi_pc_smbios_options(void)
{
uint8_t req_type11[] = { 11 };
test_data data = {
.machine = MACHINE_PC,
.variant = ".pc_smbios_options",
.required_struct_types = req_type11,
.required_struct_types_len = ARRAY_SIZE(req_type11),
};
test_smbios("-smbios type=11,value=TEST", &data);
free_test_data(&data);
}
static void test_acpi_pc_smbios_blob(void)
{
uint8_t req_type11[] = { 11 };
test_data data = {
.machine = MACHINE_PC,
.variant = ".pc_smbios_blob",
.required_struct_types = req_type11,
.required_struct_types_len = ARRAY_SIZE(req_type11),
};
test_smbios("-machine smbios-entry-point-type=32 "
"-smbios file=tests/data/smbios/type11_blob", &data);
free_test_data(&data);
}
static void test_acpi_isapc_smbios_legacy(void)
{
uint8_t req_type11[] = { 1, 11 };
test_data data = {
.machine = "isapc",
.variant = ".pc_smbios_legacy",
.required_struct_types = req_type11,
.required_struct_types_len = ARRAY_SIZE(req_type11),
};
test_smbios("-smbios file=tests/data/smbios/type11_blob.legacy "
"-smbios type=1,family=TEST", &data);
free_test_data(&data);
}
static void test_oem_fields(test_data *data) static void test_oem_fields(test_data *data)
{ {
int i; int i;
@ -2215,6 +2272,12 @@ int main(int argc, char *argv[])
#ifdef CONFIG_POSIX #ifdef CONFIG_POSIX
qtest_add_func("acpi/piix4/acpierst", test_acpi_piix4_acpi_erst); qtest_add_func("acpi/piix4/acpierst", test_acpi_piix4_acpi_erst);
#endif #endif
qtest_add_func("acpi/piix4/smbios-options",
test_acpi_pc_smbios_options);
qtest_add_func("acpi/piix4/smbios-blob",
test_acpi_pc_smbios_blob);
qtest_add_func("acpi/piix4/smbios-legacy",
test_acpi_isapc_smbios_legacy);
} }
if (qtest_has_machine(MACHINE_Q35)) { if (qtest_has_machine(MACHINE_Q35)) {
qtest_add_func("acpi/q35", test_acpi_q35_tcg); qtest_add_func("acpi/q35", test_acpi_q35_tcg);