aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-03-19 10:25:15 +0000
committerPeter Maydell <peter.maydell@linaro.org>2024-03-19 10:25:15 +0000
commit9359197f0b460134c6bd8ce9842ba103b83be1b1 (patch)
tree970da9784ea07c2d41782e246e743d1d85d18861
parent4511400fb78e72d4d9916ed60e04f4e99e594f65 (diff)
parentbb949df637bdb6136a9acca55a2371fe1721e109 (diff)
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>
-rw-r--r--docs/specs/pvpanic.rst2
-rw-r--r--hw/arm/virt.c6
-rw-r--r--hw/i386/Kconfig1
-rw-r--r--hw/i386/fw_cfg.c14
-rw-r--r--hw/i386/fw_cfg.h3
-rw-r--r--hw/i386/pc.c4
-rw-r--r--hw/i386/pc_piix.c4
-rw-r--r--hw/i386/pc_q35.c3
-rw-r--r--hw/loongarch/virt.c7
-rw-r--r--hw/riscv/virt.c6
-rw-r--r--hw/smbios/Kconfig2
-rw-r--r--hw/smbios/meson.build4
-rw-r--r--hw/smbios/smbios.c492
-rw-r--r--hw/smbios/smbios_legacy.c192
-rw-r--r--hw/smbios/smbios_legacy_stub.c15
-rw-r--r--include/hw/firmware/smbios.h28
-rw-r--r--qapi/common.json4
-rw-r--r--qapi/machine.json5
-rw-r--r--tests/data/acpi/q35/SSDT.dimmpxmbin1815 -> 1815 bytes
-rw-r--r--tests/data/smbios/type11_blobbin0 -> 11 bytes
-rw-r--r--tests/data/smbios/type11_blob.legacybin0 -> 10 bytes
-rw-r--r--tests/qtest/bios-tables-test.c81
22 files changed, 550 insertions, 323 deletions
diff --git a/docs/specs/pvpanic.rst b/docs/specs/pvpanic.rst
index 61a80480ed..b0f27860ec 100644
--- a/docs/specs/pvpanic.rst
+++ b/docs/specs/pvpanic.rst
@@ -29,7 +29,7 @@ bit 1
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 execution of the guest.
-bit 2
+bit 2 (to be implemented)
a regular guest shutdown has happened and should be processed by the host
PCI Interface
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index e5cd935232..a9a913aead 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1650,14 +1650,14 @@ static void virt_build_smbios(VirtMachineState *vms)
}
smbios_set_defaults("QEMU", product,
- vmc->smbios_old_sys_ver ? "1.0" : mc->name, false,
- true, SMBIOS_ENTRY_POINT_TYPE_64);
+ vmc->smbios_old_sys_ver ? "1.0" : mc->name,
+ true);
/* build the array of physical mem area from base_memmap */
mem_array.address = vms->memmap[VIRT_MEM].base;
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_anchor, &smbios_anchor_len,
&error_fatal);
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index a1846be6f7..a6ee052f9a 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -76,6 +76,7 @@ config I440FX
select PIIX
select DIMM
select SMBIOS
+ select SMBIOS_LEGACY
select FW_CFG_DMA
config ISAPC
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index 98a478c276..d802d2787f 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -48,7 +48,8 @@ const char *fw_cfg_arch_key_name(uint16_t key)
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
uint8_t *smbios_tables, *smbios_anchor;
@@ -63,17 +64,18 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
if (pcmc->smbios_defaults) {
/* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", mc->desc, mc->name,
- pcmc->smbios_legacy_mode, pcmc->smbios_uuid_encoded,
- pcms->smbios_entry_point_type);
+ pcmc->smbios_uuid_encoded);
}
/* tell smbios about cpuid version and features */
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
- smbios_tables = smbios_get_table_legacy(ms, &smbios_tables_len);
- if (smbios_tables) {
+ if (pcmc->smbios_legacy_mode) {
+ smbios_tables = smbios_get_table_legacy(&smbios_tables_len,
+ &error_fatal);
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
smbios_tables, smbios_tables_len);
+ return;
}
/* 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++;
}
}
- smbios_get_tables(ms, mem_array, array_count,
+ smbios_get_tables(ms, ep_type, mem_array, array_count,
&smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len,
&error_fatal);
diff --git a/hw/i386/fw_cfg.h b/hw/i386/fw_cfg.h
index 1e1de6b4a3..92e310f5fd 100644
--- a/hw/i386/fw_cfg.h
+++ b/hw/i386/fw_cfg.h
@@ -23,7 +23,8 @@
FWCfgState *fw_cfg_arch_create(MachineState *ms,
uint16_t boot_cpus,
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_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg);
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index feb7a93083..e80f02bef4 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -672,7 +672,7 @@ void pc_machine_done(Notifier *notifier, void *data)
acpi_setup();
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);
/* 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);
@@ -1832,7 +1832,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
mc->nvdimm_supported = true;
mc->smp_props.dies_supported = true;
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",
pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g,
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index c9a6c0aa68..18ba076609 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -525,12 +525,16 @@ DEFINE_I440FX_MACHINE(v9_0, "pc-i440fx-9.0", NULL,
static void pc_i440fx_8_2_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+
pc_i440fx_9_0_machine_options(m);
m->alias = NULL;
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, 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,
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 8a427c4647..b5922b44af 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -376,11 +376,14 @@ DEFINE_Q35_MACHINE(v9_0, "pc-q35-9.0", NULL,
static void pc_q35_8_2_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_q35_9_0_machine_options(m);
m->alias = NULL;
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, 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,
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
index efce112310..441d764843 100644
--- a/hw/loongarch/virt.c
+++ b/hw/loongarch/virt.c
@@ -355,10 +355,11 @@ static void virt_build_smbios(LoongArchMachineState *lams)
return;
}
- smbios_set_defaults("QEMU", product, mc->name, false,
- true, SMBIOS_ENTRY_POINT_TYPE_64);
+ smbios_set_defaults("QEMU", product, mc->name, true);
- 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);
if (smbios_anchor) {
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index a094af97c3..72a55b8af1 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -1275,8 +1275,7 @@ static void virt_build_smbios(RISCVVirtState *s)
product = "KVM Virtual Machine";
}
- smbios_set_defaults("QEMU", product, mc->name, false,
- true, SMBIOS_ENTRY_POINT_TYPE_64);
+ smbios_set_defaults("QEMU", product, mc->name, true);
if (riscv_is_32bit(&s->soc[0])) {
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.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_anchor, &smbios_anchor_len,
&error_fatal);
diff --git a/hw/smbios/Kconfig b/hw/smbios/Kconfig
index 553adf4bfc..8d989a2f1b 100644
--- a/hw/smbios/Kconfig
+++ b/hw/smbios/Kconfig
@@ -1,2 +1,4 @@
config SMBIOS
bool
+config SMBIOS_LEGACY
+ bool
diff --git a/hw/smbios/meson.build b/hw/smbios/meson.build
index 7046967462..a59039f669 100644
--- a/hw/smbios/meson.build
+++ b/hw/smbios/meson.build
@@ -4,5 +4,9 @@ smbios_ss.add(when: 'CONFIG_IPMI',
if_true: files('smbios_type_38.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(when: 'CONFIG_SMBIOS', if_false: files('smbios-stub.c'))
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index e3d5d8f2e2..eed5787b15 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -19,7 +19,6 @@
#include "qemu/units.h"
#include "qapi/error.h"
#include "qemu/config-file.h"
-#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "sysemu/sysemu.h"
@@ -31,60 +30,31 @@
#include "hw/pci/pci_device.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;
-/* 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;
size_t smbios_tables_len;
unsigned smbios_table_max;
unsigned smbios_table_cnt;
-static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32;
static SmbiosEntryPoint ep;
static int smbios_type4_count = 0;
-static bool smbios_immutable;
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);
-static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1);
+DECLARE_BITMAP(smbios_have_binfile_bitmap, SMBIOS_MAX_TYPE + 1);
+DECLARE_BITMAP(smbios_have_fields_bitmap, SMBIOS_MAX_TYPE + 1);
-static struct {
- const char *vendor, *version, *date;
- 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;
+smbios_type0_t smbios_type0;
+smbios_type1_t smbios_type1;
static struct {
const char *manufacturer, *product, *version, *serial, *asset, *location;
@@ -539,126 +509,33 @@ opts_init(smbios_register_config);
*/
#define SMBIOS_21_MAX_TABLES_LEN 0xffff
-static void smbios_validate_table(MachineState *ms)
-{
- uint32_t expect_t4_count = smbios_legacy ?
- ms->smp.cpus : smbios_smp_sockets;
-
- if (smbios_type4_count && smbios_type4_count != expect_t4_count) {
- error_report("Expected %d SMBIOS Type 4 tables, got %d instead",
- expect_t4_count, smbios_type4_count);
- exit(1);
- }
-
- if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 &&
- smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) {
- error_report("SMBIOS 2.1 table length %zu exceeds %d",
- smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN);
- exit(1);
- }
-}
-
-
-/* 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)
+static bool smbios_check_type4_count(uint32_t expected_t4_count, Error **errp)
{
- 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);
+ if (smbios_type4_count && smbios_type4_count != expected_t4_count) {
+ error_setg(errp, "Expected %d SMBIOS Type 4 tables, got %d instead",
+ expected_t4_count, smbios_type4_count);
+ return false;
}
+ return true;
}
-uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length)
+bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp)
{
- 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;
+ if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 &&
+ smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) {
+ error_setg(errp, "SMBIOS 2.1 table length %zu exceeds %d",
+ smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN);
+ return false;
}
- *length = smbios_entries_len;
- return smbios_entries;
+ return true;
}
-/* end: legacy setup functions for <= 2.0 machines */
-
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) */
}
- if (test_bit(type, have_fields_bitmap)) {
+ if (test_bit(type, smbios_have_fields_bitmap)) {
return false; /* user provided fields via command line */
}
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_TABLE_SET_STR(0, vendor_str, type0.vendor);
- SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version);
+ SMBIOS_TABLE_SET_STR(0, vendor_str, smbios_type0.vendor);
+ SMBIOS_TABLE_SET_STR(0, bios_version_str, smbios_type0.version);
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_characteristics = cpu_to_le64(0x08); /* Not supported */
t->bios_characteristics_extension_bytes[0] = 0;
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 */
}
- if (type0.have_major_minor) {
- t->system_bios_major_release = type0.major;
- t->system_bios_minor_release = type0.minor;
+ if (smbios_type0.have_major_minor) {
+ t->system_bios_major_release = smbios_type0.major;
+ t->system_bios_minor_release = smbios_type0.minor;
} else {
t->system_bios_major_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_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer);
- SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product);
- SMBIOS_TABLE_SET_STR(1, version_str, type1.version);
- SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial);
+ SMBIOS_TABLE_SET_STR(1, manufacturer_str, smbios_type1.manufacturer);
+ SMBIOS_TABLE_SET_STR(1, product_name_str, smbios_type1.product);
+ SMBIOS_TABLE_SET_STR(1, version_str, smbios_type1.version);
+ SMBIOS_TABLE_SET_STR(1, serial_number_str, smbios_type1.serial);
if (qemu_uuid_set) {
smbios_encode_uuid(&t->uuid, &qemu_uuid);
} else {
memset(&t->uuid, 0, 16);
}
t->wake_up_type = 0x06; /* power switch */
- SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku);
- SMBIOS_TABLE_SET_STR(1, family_str, type1.family);
+ SMBIOS_TABLE_SET_STR(1, sku_number_str, smbios_type1.sku);
+ SMBIOS_TABLE_SET_STR(1, family_str, smbios_type1.family);
SMBIOS_BUILD_TABLE_POST;
}
@@ -791,14 +668,16 @@ static void smbios_build_type_3_table(void)
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];
size_t tbl_len = SMBIOS_TYPE_4_LEN_V28;
unsigned threads_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;
}
@@ -845,6 +724,12 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance)
if (tbl_len == SMBIOS_TYPE_4_LEN_V30) {
t->core_count2 = t->core_enabled2 = cpu_to_le16(cores_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;
@@ -1132,31 +1017,15 @@ void smbios_set_default_processor_family(uint16_t processor_family)
}
void smbios_set_defaults(const char *manufacturer, const char *product,
- const char *version, bool legacy_mode,
- bool uuid_encoded, SmbiosEntryPointType ep_type)
+ const char *version,
+ bool uuid_encoded)
{
smbios_have_defaults = true;
- smbios_legacy = legacy_mode;
smbios_uuid_encoded = uuid_encoded;
- smbios_ep_type = ep_type;
-
- /* drop unwanted version of command-line file blob(s) */
- if (smbios_legacy) {
- g_free(smbios_tables);
- /* 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(smbios_type1.manufacturer, manufacturer);
+ SMBIOS_SET_DEFAULT(smbios_type1.product, product);
+ SMBIOS_SET_DEFAULT(smbios_type1.version, version);
SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer);
SMBIOS_SET_DEFAULT(type2.product, product);
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);
}
-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:
memcpy(ep.ep21.anchor_string, "_SM_", 4);
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 unsigned int mem_array_size,
uint8_t **tables, size_t *tables_len,
@@ -1228,78 +1098,87 @@ void smbios_get_tables(MachineState *ms,
Error **errp)
{
unsigned i, dimm_cnt, offset;
+ ERRP_GUARD();
- if (smbios_legacy) {
- *tables = *anchor = NULL;
- *tables_len = *anchor_len = 0;
- return;
- }
+ assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 ||
+ ep_type == SMBIOS_ENTRY_POINT_TYPE_64);
- if (!smbios_immutable) {
- smbios_build_type_0_table();
- smbios_build_type_1_table();
- smbios_build_type_2_table();
- smbios_build_type_3_table();
+ 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_smp_sockets = ms->smp.sockets;
- assert(smbios_smp_sockets >= 1);
+ smbios_build_type_0_table();
+ smbios_build_type_1_table();
+ smbios_build_type_2_table();
+ smbios_build_type_3_table();
- for (i = 0; i < smbios_smp_sockets; i++) {
- smbios_build_type_4_table(ms, i);
+ 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;
}
+ }
- smbios_build_type_8_table();
- smbios_build_type_9_table(errp);
- smbios_build_type_11_table();
+ smbios_build_type_8_table();
+ smbios_build_type_9_table(errp);
+ smbios_build_type_11_table();
#define MAX_DIMM_SZ (16 * GiB)
#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \
: ((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
- * 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
- * memory and DIMM like chunks of 16 GiB, the default space between
- * the two tables (T19_BASE - T17_BASE = 512) is not enough.
- */
- offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \
- dimm_cnt - (T19_BASE - T17_BASE) : 0;
+ /*
+ * The offset determines if we need to keep additional space between
+ * 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
+ * memory and DIMM like chunks of 16 GiB, the default space between
+ * the two tables (T19_BASE - T17_BASE = 512) is not enough.
+ */
+ offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \
+ 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++) {
- smbios_build_type_17_table(i, GET_DIMM_SZ);
- }
+ for (i = 0; i < dimm_cnt; i++) {
+ 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);
- }
+ 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));
+ /*
+ * 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_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;
+ 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 */
*tables = smbios_tables;
*tables_len = smbios_tables_len;
*anchor = (uint8_t *)&ep;
-
/* calculate length based on anchor string */
if (!strncmp((char *)&ep, "_SM_", 4)) {
*anchor_len = sizeof(struct smbios_21_entry_point);
@@ -1308,6 +1187,57 @@ void smbios_get_tables(MachineState *ms,
} else {
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)
@@ -1393,13 +1323,10 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
{
const char *val;
- assert(!smbios_immutable);
-
val = qemu_opt_get(opts, "file");
if (val) {
struct smbios_structure_header *header;
- int size;
- struct smbios_table *table; /* legacy mode only */
+ size_t size;
if (!qemu_opts_validate(opts, qemu_smbios_file_opts, errp)) {
return;
@@ -1416,9 +1343,9 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
* (except in legacy mode, where the second '\0' is implicit and
* will be inserted by the BIOS).
*/
- smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size);
- header = (struct smbios_structure_header *)(smbios_tables +
- smbios_tables_len);
+ usr_blobs = g_realloc(usr_blobs, usr_blobs_len + size);
+ header = (struct smbios_structure_header *)(usr_blobs +
+ usr_blobs_len);
if (load_image_size(val, (uint8_t *)header, size) != size) {
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 (test_bit(header->type, have_fields_bitmap)) {
+ if (test_bit(header->type, smbios_have_fields_bitmap)) {
error_setg(errp,
"can't load type %d struct, fields already specified!",
header->type);
return;
}
- set_bit(header->type, have_binfile_bitmap);
+ set_bit(header->type, smbios_have_binfile_bitmap);
}
if (header->type == 4) {
smbios_type4_count++;
}
- smbios_tables_len += size;
- if (size > smbios_table_max) {
- smbios_table_max = size;
- }
- 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.
+ /*
+ * preserve blob size for legacy mode so it could build its
+ * blobs flavor from 'usr_blobs'
*/
- if (!smbios_entries) {
- smbios_entries_len = sizeof(uint16_t);
- smbios_entries = g_malloc0(smbios_entries_len);
+ smbios_add_usr_blob_size(size);
+
+ usr_blobs_len += size;
+ if (size > usr_table_max) {
+ usr_table_max = size;
}
- 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;
- (*(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 */
+ usr_table_cnt++;
return;
}
@@ -1480,41 +1390,42 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
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!");
return;
}
- set_bit(type, have_fields_bitmap);
+ set_bit(type, smbios_have_fields_bitmap);
switch (type) {
case 0:
if (!qemu_opts_validate(opts, qemu_smbios_type0_opts, errp)) {
return;
}
- save_opt(&type0.vendor, opts, "vendor");
- save_opt(&type0.version, opts, "version");
- save_opt(&type0.date, opts, "date");
- type0.uefi = qemu_opt_get_bool(opts, "uefi", false);
+ save_opt(&smbios_type0.vendor, opts, "vendor");
+ save_opt(&smbios_type0.version, opts, "version");
+ save_opt(&smbios_type0.date, opts, "date");
+ smbios_type0.uefi = qemu_opt_get_bool(opts, "uefi", false);
val = qemu_opt_get(opts, "release");
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");
return;
}
- type0.have_major_minor = true;
+ smbios_type0.have_major_minor = true;
}
return;
case 1:
if (!qemu_opts_validate(opts, qemu_smbios_type1_opts, errp)) {
return;
}
- save_opt(&type1.manufacturer, opts, "manufacturer");
- save_opt(&type1.product, opts, "product");
- save_opt(&type1.version, opts, "version");
- save_opt(&type1.serial, opts, "serial");
- save_opt(&type1.sku, opts, "sku");
- save_opt(&type1.family, opts, "family");
+ save_opt(&smbios_type1.manufacturer, opts, "manufacturer");
+ save_opt(&smbios_type1.product, opts, "product");
+ save_opt(&smbios_type1.version, opts, "version");
+ save_opt(&smbios_type1.serial, opts, "serial");
+ save_opt(&smbios_type1.sku, opts, "sku");
+ save_opt(&smbios_type1.family, opts, "family");
val = qemu_opt_get(opts, "uuid");
if (val) {
@@ -1592,12 +1503,15 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
t = g_new0(struct type9_instance, 1);
save_opt(&t->slot_designation, opts, "slot_designation");
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->slot_length = qemu_opt_get_number(opts, "slot_length", 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_characteristics2 = qemu_opt_get_number(opts, "slot_characteristics2", 0);
+ t->slot_characteristics1 =
+ 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");
QTAILQ_INSERT_TAIL(&type9, t, next);
return;
diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c
new file mode 100644
index 0000000000..c37a8ee821
--- /dev/null
+++ b/hw/smbios/smbios_legacy.c
@@ -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;
+}
diff --git a/hw/smbios/smbios_legacy_stub.c b/hw/smbios/smbios_legacy_stub.c
new file mode 100644
index 0000000000..f29b15316c
--- /dev/null
+++ b/hw/smbios/smbios_legacy_stub.c
@@ -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)
+{
+}
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index c21b8d3285..8d3fb2fb3b 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -2,6 +2,7 @@
#define QEMU_SMBIOS_H
#include "qapi/qapi-types-machine.h"
+#include "qemu/bitmap.h"
/*
* 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
+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) \
(offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
@@ -307,14 +326,17 @@ struct smbios_type_127 {
struct smbios_structure_header header;
} 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_set_cpuid(uint32_t version, uint32_t features);
void smbios_set_defaults(const char *manufacturer, const char *product,
- const char *version, bool legacy_mode,
- bool uuid_encoded, SmbiosEntryPointType ep_type);
+ const char *version,
+ bool uuid_encoded);
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,
+ SmbiosEntryPointType ep_type,
const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size,
uint8_t **tables, size_t *tables_len,
diff --git a/qapi/common.json b/qapi/common.json
index 867a9ad9b0..7558ce5430 100644
--- a/qapi/common.json
+++ b/qapi/common.json
@@ -107,9 +107,9 @@
#
# @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
##
diff --git a/qapi/machine.json b/qapi/machine.json
index bb5a178909..0840c91e70 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1797,10 +1797,13 @@
#
# @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
##
{ 'enum': 'SmbiosEntryPointType',
- 'data': [ '32', '64' ] }
+ 'data': [ '32', '64', 'auto' ] }
##
# @MemorySizeConfiguration:
diff --git a/tests/data/acpi/q35/SSDT.dimmpxm b/tests/data/acpi/q35/SSDT.dimmpxm
index 70f133412f..9ea4e0d0ce 100644
--- a/tests/data/acpi/q35/SSDT.dimmpxm
+++ b/tests/data/acpi/q35/SSDT.dimmpxm
Binary files differ
diff --git a/tests/data/smbios/type11_blob b/tests/data/smbios/type11_blob
new file mode 100644
index 0000000000..1d8fea4b0c
--- /dev/null
+++ b/tests/data/smbios/type11_blob
Binary files differ
diff --git a/tests/data/smbios/type11_blob.legacy b/tests/data/smbios/type11_blob.legacy
new file mode 100644
index 0000000000..aef463aab9
--- /dev/null
+++ b/tests/data/smbios/type11_blob.legacy
Binary files differ
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index 21811a1ab5..d1ff4db7a2 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -858,16 +858,8 @@ static void test_vm_prepare(const char *params, test_data *data)
g_free(args);
}
-static void process_acpi_tables_noexit(test_data *data)
+static void process_smbios_tables_noexit(test_data *data)
{
- test_acpi_load_tables(data);
-
- if (getenv(ACPI_REBUILD_EXPECTED_AML)) {
- dump_aml_files(data, true);
- } else {
- test_acpi_asl(data);
- }
-
/*
* TODO: make SMBIOS tests work with UEFI firmware,
* Bug on uefi-test-tools to provide entry point:
@@ -879,6 +871,27 @@ static void process_acpi_tables_noexit(test_data *data)
}
}
+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)
+{
+ test_acpi_load_tables(data);
+
+ if (getenv(ACPI_REBUILD_EXPECTED_AML)) {
+ dump_aml_files(data, true);
+ } else {
+ test_acpi_asl(data);
+ }
+
+ process_smbios_tables_noexit(data);
+}
+
static void process_acpi_tables(test_data *data)
{
process_acpi_tables_noexit(data);
@@ -2064,6 +2077,50 @@ static void test_acpi_q35_pvpanic_isa(void)
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)
{
int i;
@@ -2215,6 +2272,12 @@ int main(int argc, char *argv[])
#ifdef CONFIG_POSIX
qtest_add_func("acpi/piix4/acpierst", test_acpi_piix4_acpi_erst);
#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)) {
qtest_add_func("acpi/q35", test_acpi_q35_tcg);