From fdf1c98063eec7f7fb4dd019391739eaae1e165f Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 12 Mar 2024 18:28:12 -0400 Subject: [PATCH 01/24] SMBIOS: fix long lines Break up long lines to fit under 80/90 char limit. Fixes: 04f143d828 ("Implement SMBIOS type 9 v2.6") Fixes: 735eee07d1 ("Implement base of SMBIOS type 9 descriptor.") Cc: "Felix Wu" Cc: Nabih Estefan Reviewed-by: Ani Sinha Signed-off-by: Michael S. Tsirkin --- hw/smbios/smbios.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index e3d5d8f2e2..949c2d74a1 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -1592,12 +1592,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; From 1bdef7a6290b1a7ed820aa2e9c25fa85069b2b85 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 13 Mar 2024 02:43:46 -0400 Subject: [PATCH 02/24] qapi: document PCIe Gen5/Gen6 speeds since 9.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document that PCIe Gen5/Gen6 speeds are only in QAPI since 9.0 - the rest is since 4.0. Cc: Lukas Stockner Cc: Marcel Apfelbaum Fixes: c08da86dc4 ("pcie: Support PCIe Gen5/Gen6 link speeds") Suggested-by: Markus Armbruster Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- qapi/common.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ## From e739d1935c461d0668057e9dbba9d06f728d29ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Wed, 13 Mar 2024 19:30:31 +0100 Subject: [PATCH 03/24] docs/specs/pvpanic: mark shutdown event as not implemented MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mention the fact that this event is not yet implemented to avoid confusion. As requested by Michael. Signed-off-by: Thomas Weißschuh Message-Id: <20240313-pvpanic-note-v1-1-7f2571cdaedc@t-8ch.de> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/specs/pvpanic.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 53002d9028f9d8b3215dafd7c0a55047bb5c9804 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:42 +0100 Subject: [PATCH 04/24] tests: smbios: make it possible to write SMBIOS only test Cureently it not possible to run SMBIOS test without ACPI one, which gets into the way when testing ACPI-less configs. Extract SMBIOS testing into separate routines that could also be run without ACPI dependency and use that for testing SMBIOS. As the 1st user add "acpi/piix4/smbios-options" test case. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-2-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 47 +++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 21811a1ab5..b2992bafa8 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -858,6 +858,27 @@ static void test_vm_prepare(const char *params, test_data *data) 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) { test_acpi_load_tables(data); @@ -868,15 +889,7 @@ static void process_acpi_tables_noexit(test_data *data) test_acpi_asl(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); - } + process_smbios_tables_noexit(data); } static void process_acpi_tables(test_data *data) @@ -2064,6 +2077,20 @@ 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_oem_fields(test_data *data) { int i; @@ -2215,6 +2242,8 @@ 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); } if (qtest_has_machine(MACHINE_Q35)) { qtest_add_func("acpi/q35", test_acpi_q35_tcg); From ed75658af39ea29c5bf2d6fd2c645a54c95db78a Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:43 +0100 Subject: [PATCH 05/24] tests: smbios: add test for -smbios type=11 option Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-3-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/smbios/type11_blob | Bin 0 -> 11 bytes tests/qtest/bios-tables-test.c | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/data/smbios/type11_blob diff --git a/tests/data/smbios/type11_blob b/tests/data/smbios/type11_blob new file mode 100644 index 0000000000000000000000000000000000000000..1d8fea4b0c6f040a13ba99c3fad762538b795614 GIT binary patch literal 11 Scmd;PW!S(N;u;*nzyJUX)&c?m literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index b2992bafa8..a116f88e1d 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2091,6 +2091,21 @@ static void test_acpi_pc_smbios_options(void) 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_oem_fields(test_data *data) { int i; @@ -2244,6 +2259,8 @@ int main(int argc, char *argv[]) #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); } if (qtest_has_machine(MACHINE_Q35)) { qtest_add_func("acpi/q35", test_acpi_q35_tcg); From 579094cb995b605af07ba3ced3d0a5dc1545509c Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:44 +0100 Subject: [PATCH 06/24] tests: smbios: add test for legacy mode CLI options Unfortunately having 2.0 machine type deprecated is not enough to get rid of legacy SMBIOS handling since 'isapc' also uses that and it's staying around. Hence add test for CLI options handling to be sure that it ain't broken during SMBIOS code refactoring. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-4-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/smbios/type11_blob.legacy | Bin 0 -> 10 bytes tests/qtest/bios-tables-test.c | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/data/smbios/type11_blob.legacy diff --git a/tests/data/smbios/type11_blob.legacy b/tests/data/smbios/type11_blob.legacy new file mode 100644 index 0000000000000000000000000000000000000000..aef463aab903405958b0a85f85c5980671c08bee GIT binary patch literal 10 Rcmd;PW!S(N;u;*n000Tp0s;U4 literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index a116f88e1d..d1ff4db7a2 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2106,6 +2106,21 @@ static void test_acpi_pc_smbios_blob(void) 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; @@ -2261,6 +2276,8 @@ int main(int argc, char *argv[]) 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); From a7bdf7186f8ee5955874438ec2ddb1d28bc084b4 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:45 +0100 Subject: [PATCH 07/24] smbios: cleanup smbios_get_tables() from legacy handling smbios_get_tables() bails out right away if leagacy mode is enabled and won't generate any SMBIOS tables. At the same time x86 specific fw_cfg_build_smbios() will genarate legacy tables and then proceed to preparing temporary mem_array for useless call to smbios_get_tables() and then discard it. Drop legacy related check in smbios_get_tables() and return from fw_cfg_build_smbios() early if legacy tables where built without proceeding to non legacy part of the function. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-5-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/fw_cfg.c | 1 + hw/smbios/smbios.c | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index 98a478c276..a635234e68 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -74,6 +74,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg) if (smbios_tables) { 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 */ diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 949c2d74a1..a1741a64a6 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -1229,12 +1229,6 @@ void smbios_get_tables(MachineState *ms, { unsigned i, dimm_cnt, offset; - if (smbios_legacy) { - *tables = *anchor = NULL; - *tables_len = *anchor_len = 0; - return; - } - if (!smbios_immutable) { smbios_build_type_0_table(); smbios_build_type_1_table(); From e94e0a833b9d909e239829a2030dbb6e7c2db01d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:46 +0100 Subject: [PATCH 08/24] smbios: get rid of smbios_smp_sockets global it makes smbios_validate_table() independent from smbios_smp_sockets global, which in turn lets smbios_get_tables() avoid using not related legacy code. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-6-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/fw_cfg.c | 2 +- hw/smbios/smbios.c | 22 +++++++++------------- include/hw/firmware/smbios.h | 2 +- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index a635234e68..fcb4fb0769 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -70,7 +70,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg) /* 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); + smbios_tables = smbios_get_table_legacy(ms->smp.cpus, &smbios_tables_len); if (smbios_tables) { fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, smbios_tables, smbios_tables_len); diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index a1741a64a6..003c539d76 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -70,7 +70,7 @@ 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); @@ -539,14 +539,11 @@ opts_init(smbios_register_config); */ #define SMBIOS_21_MAX_TABLES_LEN 0xffff -static void smbios_validate_table(MachineState *ms) +static void smbios_validate_table(uint32_t expected_t4_count) { - uint32_t expect_t4_count = smbios_legacy ? - ms->smp.cpus : smbios_smp_sockets; - - if (smbios_type4_count && smbios_type4_count != expect_t4_count) { + if (smbios_type4_count && smbios_type4_count != expected_t4_count) { error_report("Expected %d SMBIOS Type 4 tables, got %d instead", - expect_t4_count, smbios_type4_count); + expected_t4_count, smbios_type4_count); exit(1); } @@ -634,7 +631,7 @@ static void smbios_build_type_1_fields(void) } } -uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length) +uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length) { if (!smbios_legacy) { *length = 0; @@ -644,7 +641,7 @@ uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length) if (!smbios_immutable) { smbios_build_type_0_fields(); smbios_build_type_1_fields(); - smbios_validate_table(ms); + smbios_validate_table(expected_t4_count); smbios_immutable = true; } *length = smbios_entries_len; @@ -1235,10 +1232,9 @@ void smbios_get_tables(MachineState *ms, smbios_build_type_2_table(); smbios_build_type_3_table(); - smbios_smp_sockets = ms->smp.sockets; - assert(smbios_smp_sockets >= 1); + assert(ms->smp.sockets >= 1); - for (i = 0; i < smbios_smp_sockets; i++) { + for (i = 0; i < ms->smp.sockets; i++) { smbios_build_type_4_table(ms, i); } @@ -1284,7 +1280,7 @@ void smbios_get_tables(MachineState *ms, smbios_build_type_41_table(errp); smbios_build_type_127_table(); - smbios_validate_table(ms); + smbios_validate_table(ms->smp.sockets); smbios_entry_point_setup(); smbios_immutable = true; } diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index c21b8d3285..36744b6cc9 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -313,7 +313,7 @@ void smbios_set_defaults(const char *manufacturer, const char *product, const char *version, bool legacy_mode, bool uuid_encoded, SmbiosEntryPointType ep_type); 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(uint32_t expected_t4_count, size_t *length); void smbios_get_tables(MachineState *ms, const struct smbios_phys_mem_area *mem_array, const unsigned int mem_array_size, From b3854ce8a77f14b40a59c4fcef234f4af04504d5 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:47 +0100 Subject: [PATCH 09/24] smbios: get rid of smbios_legacy global clean up smbios_set_defaults() which is reused by legacy and non legacy machines from being aware of 'legacy' notion and need to turn it off. And push legacy handling up to PC machine code where it's relevant. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Acked-by: Daniel Henrique Barboza Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-7-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt.c | 2 +- hw/i386/fw_cfg.c | 7 ++++--- hw/loongarch/virt.c | 2 +- hw/riscv/virt.c | 2 +- hw/smbios/smbios.c | 35 +++++++++++++++-------------------- include/hw/firmware/smbios.h | 2 +- 6 files changed, 23 insertions(+), 27 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e5cd935232..b634c908a7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1650,7 +1650,7 @@ static void virt_build_smbios(VirtMachineState *vms) } 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); /* build the array of physical mem area from base_memmap */ diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index fcb4fb0769..c1e9c0fd9c 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -63,15 +63,16 @@ 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, + pcmc->smbios_uuid_encoded, pcms->smbios_entry_point_type); } /* 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->smp.cpus, &smbios_tables_len); - if (smbios_tables) { + if (pcmc->smbios_legacy_mode) { + smbios_tables = smbios_get_table_legacy(ms->smp.cpus, + &smbios_tables_len); fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, smbios_tables, smbios_tables_len); return; diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index efce112310..53bfdcee61 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -355,7 +355,7 @@ static void virt_build_smbios(LoongArchMachineState *lams) return; } - smbios_set_defaults("QEMU", product, mc->name, false, + smbios_set_defaults("QEMU", product, mc->name, true, SMBIOS_ENTRY_POINT_TYPE_64); smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len, diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index a094af97c3..535fd047ba 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1275,7 +1275,7 @@ static void virt_build_smbios(RISCVVirtState *s) product = "KVM Virtual Machine"; } - smbios_set_defaults("QEMU", product, mc->name, false, + smbios_set_defaults("QEMU", product, mc->name, true, SMBIOS_ENTRY_POINT_TYPE_64); if (riscv_is_32bit(&s->soc[0])) { diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 003c539d76..9f9087601c 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -54,7 +54,6 @@ struct smbios_table { 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 */ @@ -633,9 +632,16 @@ static void smbios_build_type_1_fields(void) uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length) { - if (!smbios_legacy) { - *length = 0; - return NULL; + /* drop unwanted version of command-line file blob(s) */ + g_free(smbios_tables); + smbios_tables = NULL; + + /* 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); } if (!smbios_immutable) { @@ -1129,28 +1135,13 @@ 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, + const char *version, bool uuid_encoded, SmbiosEntryPointType ep_type) { 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); @@ -1226,6 +1217,10 @@ void smbios_get_tables(MachineState *ms, { unsigned i, dimm_cnt, offset; + /* drop unwanted (legacy) version of command-line file blob(s) */ + g_free(smbios_entries); + smbios_entries = NULL; + if (!smbios_immutable) { smbios_build_type_0_table(); smbios_build_type_1_table(); diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index 36744b6cc9..7b42e7b4ac 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -310,7 +310,7 @@ struct smbios_type_127 { 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, + const char *version, bool uuid_encoded, SmbiosEntryPointType ep_type); void smbios_set_default_processor_family(uint16_t processor_family); uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length); From cba59fe38a2bc2b1888892539d0c4688e07aa356 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:48 +0100 Subject: [PATCH 10/24] smbios: avoid mangling user provided tables currently smbios_entry_add() preserves internally '-smbios type=' options but tables provided with '-smbios file=' are stored directly into blob that eventually will be exposed to VM. And then later QEMU adds default/'-smbios type' entries on top into the same blob. It makes impossible to generate tables more than once, hence 'immutable' guard was used. Make it possible to regenerate final blob by storing user provided blobs into a dedicated area (usr_blobs) and then copy it when composing final blob. Which also makes handling of -smbios options consistent. As side effect of this and previous commits there is no need to generate legacy smbios_entries at the time options are parsed. Instead compose smbios_entries on demand from usr_blobs like it is done for non-legacy SMBIOS tables. Signed-off-by: Igor Mammedov Tested-by: Fiona Ebner Reviewed-by: Ani Sinha Message-Id: <20240314152302.2324164-8-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/smbios/smbios.c | 187 +++++++++++++++++++++++---------------------- 1 file changed, 96 insertions(+), 91 deletions(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 9f9087601c..aab4ffb4cb 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -57,6 +57,14 @@ static size_t smbios_entries_len; static bool smbios_uuid_encoded = true; /* end: legacy structures & constants for <= 2.0 machines */ +/* + * SMBIOS tables provided by user with '-smbios file=' option + */ +uint8_t *usr_blobs; +size_t usr_blobs_len; +static GArray *usr_blobs_sizes; +static unsigned usr_table_max; +static unsigned usr_table_cnt; uint8_t *smbios_tables; size_t smbios_tables_len; @@ -67,7 +75,6 @@ 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; @@ -632,9 +639,8 @@ static void smbios_build_type_1_fields(void) uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length) { - /* drop unwanted version of command-line file blob(s) */ - g_free(smbios_tables); - smbios_tables = NULL; + int i; + size_t usr_offset; /* also complain if fields were given for types > 1 */ if (find_next_bit(have_fields_bitmap, @@ -644,12 +650,33 @@ uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length) exit(1); } - if (!smbios_immutable) { - smbios_build_type_0_fields(); - smbios_build_type_1_fields(); - smbios_validate_table(expected_t4_count); - smbios_immutable = true; + g_free(smbios_entries); + smbios_entries_len = sizeof(uint16_t); + smbios_entries = g_malloc0(smbios_entries_len); + + 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; + (*(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(); + smbios_validate_table(expected_t4_count); *length = smbios_entries_len; return smbios_entries; } @@ -1217,69 +1244,69 @@ void smbios_get_tables(MachineState *ms, { unsigned i, dimm_cnt, offset; - /* drop unwanted (legacy) version of command-line file blob(s) */ - g_free(smbios_entries); - smbios_entries = NULL; + g_free(smbios_tables); + 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; - if (!smbios_immutable) { - smbios_build_type_0_table(); - smbios_build_type_1_table(); - smbios_build_type_2_table(); - smbios_build_type_3_table(); + 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); + assert(ms->smp.sockets >= 1); - for (i = 0; i < ms->smp.sockets; i++) { - smbios_build_type_4_table(ms, i); - } + for (i = 0; i < ms->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(); + 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 < 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->smp.sockets); - smbios_entry_point_setup(); - smbios_immutable = true; + 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); + } + + /* + * 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->smp.sockets); + smbios_entry_point_setup(); + /* return tables blob and entry point (anchor), and their sizes */ *tables = smbios_tables; *tables_len = smbios_tables_len; @@ -1378,13 +1405,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; @@ -1401,9 +1425,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); @@ -1424,34 +1448,15 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) smbios_type4_count++; } - smbios_tables_len += size; - if (size > smbios_table_max) { - smbios_table_max = size; + if (!usr_blobs_sizes) { + usr_blobs_sizes = g_array_new(false, false, sizeof(size_t)); } - 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_entries_len = sizeof(uint16_t); - smbios_entries = g_malloc0(smbios_entries_len); + g_array_append_val(usr_blobs_sizes, 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; } From 9cd7fd69cfad9180e22b9adb728fc7b596b4bc1e Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:49 +0100 Subject: [PATCH 11/24] smbios: don't check type4 structures in legacy mode legacy mode doesn't support structures of type 2 and more, and CLI has a check for '-smbios type' option, however it's still possible to sneak in type4 as a blob with '-smbios file' option. However doing the later makes SMBIOS tables broken since SeaBIOS doesn't expect that. Rather than trying to add support for type4 to legacy code (both QEMU and SeaBIOS), simplify smbios_get_table_legacy() by dropping not relevant check in legacy code and error out on type4 blob. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-9-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/fw_cfg.c | 3 +-- hw/smbios/smbios.c | 18 ++++++++++++++---- include/hw/firmware/smbios.h | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index c1e9c0fd9c..d1281066f4 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -71,8 +71,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg) smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]); if (pcmc->smbios_legacy_mode) { - smbios_tables = smbios_get_table_legacy(ms->smp.cpus, - &smbios_tables_len); + smbios_tables = smbios_get_table_legacy(&smbios_tables_len); fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, smbios_tables, smbios_tables_len); return; diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index aab4ffb4cb..30196d2911 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -545,14 +545,17 @@ opts_init(smbios_register_config); */ #define SMBIOS_21_MAX_TABLES_LEN 0xffff -static void smbios_validate_table(uint32_t expected_t4_count) +static void smbios_check_type4_count(uint32_t expected_t4_count) { if (smbios_type4_count && smbios_type4_count != expected_t4_count) { error_report("Expected %d SMBIOS Type 4 tables, got %d instead", expected_t4_count, smbios_type4_count); exit(1); } +} +static void smbios_validate_table(void) +{ 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", @@ -637,7 +640,7 @@ static void smbios_build_type_1_fields(void) } } -uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length) +uint8_t *smbios_get_table_legacy(size_t *length) { int i; size_t usr_offset; @@ -650,6 +653,12 @@ uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length) exit(1); } + if (test_bit(4, have_binfile_bitmap)) { + error_report("can't process table for smbios " + "type 4 on machine versions < 2.1!"); + exit(1); + } + g_free(smbios_entries); smbios_entries_len = sizeof(uint16_t); smbios_entries = g_malloc0(smbios_entries_len); @@ -676,7 +685,7 @@ uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length) smbios_build_type_0_fields(); smbios_build_type_1_fields(); - smbios_validate_table(expected_t4_count); + smbios_validate_table(); *length = smbios_entries_len; return smbios_entries; } @@ -1304,7 +1313,8 @@ void smbios_get_tables(MachineState *ms, smbios_build_type_41_table(errp); smbios_build_type_127_table(); - smbios_validate_table(ms->smp.sockets); + smbios_check_type4_count(ms->smp.sockets); + smbios_validate_table(); smbios_entry_point_setup(); /* return tables blob and entry point (anchor), and their sizes */ diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index 7b42e7b4ac..0f0dca8f83 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -313,7 +313,7 @@ void smbios_set_defaults(const char *manufacturer, const char *product, const char *version, bool uuid_encoded, SmbiosEntryPointType ep_type); void smbios_set_default_processor_family(uint16_t processor_family); -uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length); +uint8_t *smbios_get_table_legacy(size_t *length); void smbios_get_tables(MachineState *ms, const struct smbios_phys_mem_area *mem_array, const unsigned int mem_array_size, From 684b49fda6735fef7ee07bb2c628b37fa114ca1e Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:50 +0100 Subject: [PATCH 12/24] smbios: add smbios_add_usr_blob_size() helper it will be used by follow up patch when legacy handling is moved out into a separate file. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Message-Id: <20240314152302.2324164-10-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/smbios/smbios.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 30196d2911..090a6eb018 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -1411,6 +1411,14 @@ static bool save_opt_list(size_t *ndest, char ***dest, QemuOpts *opts, return true; } +static 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); +} + void smbios_entry_add(QemuOpts *opts, Error **errp) { const char *val; @@ -1458,10 +1466,12 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) smbios_type4_count++; } - if (!usr_blobs_sizes) { - usr_blobs_sizes = g_array_new(false, false, sizeof(size_t)); - } - g_array_append_val(usr_blobs_sizes, size); + /* + * preserve blob size for legacy mode so it could build its + * blobs flavor from 'usr_blobs' + */ + smbios_add_usr_blob_size(size); + usr_blobs_len += size; if (size > usr_table_max) { usr_table_max = size; From d638a8659b7e957dede0cdc5afa569b733a10da5 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:51 +0100 Subject: [PATCH 13/24] smbios: rename/expose structures/bitmaps used by both legacy and modern code As a preparation to move legacy handling into a separate file, add prefix 'smbios_' to type0/type1/have_binfile_bitmap/have_fields_bitmap and expose them in smbios.h so that they can be reused in legacy and modern code. Doing it as a separate patch to avoid rename cluttering follow-up patch which will move legacy code into a separate file. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Message-Id: <20240314152302.2324164-11-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/smbios/smbios.c | 113 ++++++++++++++++------------------- include/hw/firmware/smbios.h | 16 +++++ 2 files changed, 69 insertions(+), 60 deletions(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 090a6eb018..6c24fb75d3 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -78,19 +78,11 @@ static int smbios_type4_count = 0; static bool smbios_have_defaults; 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; @@ -599,36 +591,36 @@ static void smbios_maybe_add_str(int type, int offset, const char *data) static void smbios_build_type_0_fields(void) { smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str), - type0.vendor); + smbios_type0.vendor); smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str), - type0.version); + smbios_type0.version); smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_release_date_str), - type0.date); - if (type0.have_major_minor) { + smbios_type0.date); + if (smbios_type0.have_major_minor) { smbios_add_field(0, offsetof(struct smbios_type_0, system_bios_major_release), - &type0.major, 1); + &smbios_type0.major, 1); smbios_add_field(0, offsetof(struct smbios_type_0, system_bios_minor_release), - &type0.minor, 1); + &smbios_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_type1.manufacturer); smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str), - type1.product); + smbios_type1.product); smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str), - type1.version); + smbios_type1.version); smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str), - type1.serial); + smbios_type1.serial); smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str), - type1.sku); + smbios_type1.sku); smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), - type1.family); + 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 @@ -646,14 +638,14 @@ uint8_t *smbios_get_table_legacy(size_t *length) size_t usr_offset; /* also complain if fields were given for types > 1 */ - if (find_next_bit(have_fields_bitmap, + if (find_next_bit(smbios_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); } - if (test_bit(4, have_binfile_bitmap)) { + if (test_bit(4, smbios_have_binfile_bitmap)) { error_report("can't process table for smbios " "type 4 on machine versions < 2.1!"); exit(1); @@ -694,10 +686,10 @@ uint8_t *smbios_get_table_legacy(size_t *length) 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) { @@ -725,25 +717,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; @@ -773,18 +765,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; } @@ -1178,9 +1170,9 @@ void smbios_set_defaults(const char *manufacturer, const char *product, smbios_uuid_encoded = uuid_encoded; smbios_ep_type = ep_type; - 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); @@ -1453,13 +1445,13 @@ 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) { @@ -1490,41 +1482,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) { diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index 0f0dca8f83..05707c6341 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,23 @@ * */ +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)) From b42b0e4daaa543bc9f8e24662f94d65f6481c4a0 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:52 +0100 Subject: [PATCH 14/24] smbios: build legacy mode code only for 'pc' machine basically moving code around without functional change. And exposing some symbols so that they could be shared between smbbios.c and new smbios_legacy.c plus some meson magic to build smbios_legacy.c only for 'pc' machine and otherwise replace it with stub if not selected. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Message-Id: <20240314152302.2324164-12-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/Kconfig | 1 + hw/smbios/Kconfig | 2 + hw/smbios/meson.build | 4 + hw/smbios/smbios.c | 164 +----------------------------- hw/smbios/smbios_legacy.c | 179 +++++++++++++++++++++++++++++++++ hw/smbios/smbios_legacy_stub.c | 15 +++ include/hw/firmware/smbios.h | 5 + 7 files changed, 207 insertions(+), 163 deletions(-) create mode 100644 hw/smbios/smbios_legacy.c create mode 100644 hw/smbios/smbios_legacy_stub.c 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/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 6c24fb75d3..242106fd87 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -31,38 +31,12 @@ #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_uuid_encoded = true; -/* end: legacy structures & constants for <= 2.0 machines */ - /* * SMBIOS tables provided by user with '-smbios file=' option */ uint8_t *usr_blobs; size_t usr_blobs_len; -static GArray *usr_blobs_sizes; static unsigned usr_table_max; static unsigned usr_table_cnt; @@ -546,7 +520,7 @@ static void smbios_check_type4_count(uint32_t expected_t4_count) } } -static void smbios_validate_table(void) +void smbios_validate_table(void) { if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) { @@ -556,134 +530,6 @@ static void smbios_validate_table(void) } } - -/* 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), - 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) -{ - int i; - size_t usr_offset; - - /* also 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_report("can't process fields for smbios " - "types > 1 on machine versions < 2.1!"); - exit(1); - } - - if (test_bit(4, smbios_have_binfile_bitmap)) { - error_report("can't process table for smbios " - "type 4 on machine versions < 2.1!"); - exit(1); - } - - g_free(smbios_entries); - smbios_entries_len = sizeof(uint16_t); - smbios_entries = g_malloc0(smbios_entries_len); - - 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; - (*(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(); - smbios_validate_table(); - *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) { if (test_bit(type, smbios_have_binfile_bitmap)) { @@ -1403,14 +1249,6 @@ static bool save_opt_list(size_t *ndest, char ***dest, QemuOpts *opts, return true; } -static 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); -} - void smbios_entry_add(QemuOpts *opts, Error **errp) { const char *val; diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c new file mode 100644 index 0000000000..21f143e738 --- /dev/null +++ b/hw/smbios/smbios_legacy.c @@ -0,0 +1,179 @@ +/* + * SMBIOS legacy support + * + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2013 Red Hat, Inc. + * + * Authors: + * Alex Williamson + * Markus Armbruster + * + * 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 "qemu/error-report.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) +{ + 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_report("can't process fields for smbios " + "types > 1 on machine versions < 2.1!"); + exit(1); + } + + if (test_bit(4, smbios_have_binfile_bitmap)) { + error_report("can't process table for smbios " + "type 4 on machine versions < 2.1!"); + exit(1); + } + + g_free(smbios_entries); + smbios_entries_len = sizeof(uint16_t); + smbios_entries = g_malloc0(smbios_entries_len); + + 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; + (*(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(); + smbios_validate_table(); + *length = smbios_entries_len; + return smbios_entries; +} 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 05707c6341..ccc51e72f5 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -17,6 +17,9 @@ * */ +extern uint8_t *usr_blobs; +extern GArray *usr_blobs_sizes; + typedef struct { const char *vendor, *version, *date; bool have_major_minor, uefi; @@ -323,6 +326,8 @@ struct smbios_type_127 { struct smbios_structure_header header; } QEMU_PACKED; +void smbios_validate_table(void); +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, From 643e1c9ef9d90a6e80b82998d41c91302fef506b Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:53 +0100 Subject: [PATCH 15/24] smbios: handle errors consistently Current code uses mix of error_report()+exit(1) and error_setg() to handle errors. Use newer error_setg() everywhere, beside consistency it will allow to detect error condition without killing QEMU and attempt switch-over to SMBIOS3.x tables/entrypoint in follow up patch. while at it, clear smbios_tables pointer after freeing. that will avoid double free if smbios_get_tables() is called multiple times. Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Message-Id: <20240314152302.2324164-13-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/fw_cfg.c | 3 ++- hw/smbios/smbios.c | 34 ++++++++++++++++++++++------------ hw/smbios/smbios_legacy.c | 22 ++++++++++++++-------- include/hw/firmware/smbios.h | 4 ++-- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index d1281066f4..e387bf50d0 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -71,7 +71,8 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg) smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]); if (pcmc->smbios_legacy_mode) { - smbios_tables = smbios_get_table_legacy(&smbios_tables_len); + 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; diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 242106fd87..f7ca7d77e3 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" @@ -511,23 +510,25 @@ opts_init(smbios_register_config); */ #define SMBIOS_21_MAX_TABLES_LEN 0xffff -static void smbios_check_type4_count(uint32_t expected_t4_count) +static bool smbios_check_type4_count(uint32_t expected_t4_count, Error **errp) { if (smbios_type4_count && smbios_type4_count != expected_t4_count) { - error_report("Expected %d SMBIOS Type 4 tables, got %d instead", - expected_t4_count, smbios_type4_count); - exit(1); + error_setg(errp, "Expected %d SMBIOS Type 4 tables, got %d instead", + expected_t4_count, smbios_type4_count); + return false; } + return true; } -void smbios_validate_table(void) +bool smbios_validate_table(Error **errp) { 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); + error_setg(errp, "SMBIOS 2.1 table length %zu exceeds %d", + smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN); + return false; } + return true; } bool smbios_skip_table(uint8_t type, bool required_table) @@ -1151,15 +1152,18 @@ void smbios_get_tables(MachineState *ms, smbios_build_type_41_table(errp); smbios_build_type_127_table(); - smbios_check_type4_count(ms->smp.sockets); - smbios_validate_table(); + if (!smbios_check_type4_count(ms->smp.sockets, errp)) { + goto err_exit; + } + if (!smbios_validate_table(errp)) { + goto err_exit; + } smbios_entry_point_setup(); /* 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); @@ -1168,6 +1172,12 @@ void smbios_get_tables(MachineState *ms, } else { abort(); } + + return; +err_exit: + g_free(smbios_tables); + smbios_tables = NULL; + return; } static void save_opt(const char **dest, QemuOpts *opts, const char *name) diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c index 21f143e738..a6544bf55a 100644 --- a/hw/smbios/smbios_legacy.c +++ b/hw/smbios/smbios_legacy.c @@ -19,7 +19,7 @@ #include "qemu/bswap.h" #include "hw/firmware/smbios.h" #include "sysemu/sysemu.h" -#include "qemu/error-report.h" +#include "qapi/error.h" struct smbios_header { uint16_t length; @@ -128,7 +128,7 @@ static void smbios_build_type_1_fields(void) } } -uint8_t *smbios_get_table_legacy(size_t *length) +uint8_t *smbios_get_table_legacy(size_t *length, Error **errp) { int i; size_t usr_offset; @@ -136,15 +136,15 @@ uint8_t *smbios_get_table_legacy(size_t *length) /* 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_report("can't process fields for smbios " + error_setg(errp, "can't process fields for smbios " "types > 1 on machine versions < 2.1!"); - exit(1); + goto err_exit; } if (test_bit(4, smbios_have_binfile_bitmap)) { - error_report("can't process table for smbios " - "type 4 on machine versions < 2.1!"); - exit(1); + error_setg(errp, "can't process table for smbios " + "type 4 on machine versions < 2.1!"); + goto err_exit; } g_free(smbios_entries); @@ -173,7 +173,13 @@ uint8_t *smbios_get_table_legacy(size_t *length) smbios_build_type_0_fields(); smbios_build_type_1_fields(); - smbios_validate_table(); + if (!smbios_validate_table(errp)) { + goto err_exit; + } + *length = smbios_entries_len; return smbios_entries; +err_exit: + g_free(smbios_entries); + return NULL; } diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index ccc51e72f5..d4b91d5a14 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -326,7 +326,7 @@ struct smbios_type_127 { struct smbios_structure_header header; } QEMU_PACKED; -void smbios_validate_table(void); +bool smbios_validate_table(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); @@ -334,7 +334,7 @@ void smbios_set_defaults(const char *manufacturer, const char *product, const char *version, bool uuid_encoded, SmbiosEntryPointType ep_type); void smbios_set_default_processor_family(uint16_t processor_family); -uint8_t *smbios_get_table_legacy(size_t *length); +uint8_t *smbios_get_table_legacy(size_t *length, Error **errp); void smbios_get_tables(MachineState *ms, const struct smbios_phys_mem_area *mem_array, const unsigned int mem_array_size, From 69ea07a56ef54050a61b91fe8236fef4908fe5d1 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:54 +0100 Subject: [PATCH 16/24] smbios: get rid of global smbios_ep_type Signed-off-by: Igor Mammedov Acked-by: Daniel Henrique Barboza Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-14-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt.c | 4 ++-- hw/i386/fw_cfg.c | 8 ++++---- hw/i386/fw_cfg.h | 3 ++- hw/i386/pc.c | 2 +- hw/loongarch/virt.c | 7 ++++--- hw/riscv/virt.c | 6 +++--- hw/smbios/smbios.c | 27 +++++++++++++++------------ hw/smbios/smbios_legacy.c | 2 +- include/hw/firmware/smbios.h | 5 +++-- 9 files changed, 35 insertions(+), 29 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index b634c908a7..a9a913aead 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1651,13 +1651,13 @@ static void virt_build_smbios(VirtMachineState *vms) smbios_set_defaults("QEMU", product, 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 */ 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/fw_cfg.c b/hw/i386/fw_cfg.c index e387bf50d0..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,8 +64,7 @@ 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_uuid_encoded, - pcms->smbios_entry_point_type); + pcmc->smbios_uuid_encoded); } /* tell smbios about cpuid version and features */ @@ -89,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..44eb073abd 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); diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 53bfdcee61..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, - 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 535fd047ba..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, - 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/smbios.c b/hw/smbios/smbios.c index f7ca7d77e3..0ab77b5ec8 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -43,7 +43,6 @@ 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; @@ -520,9 +519,9 @@ static bool smbios_check_type4_count(uint32_t expected_t4_count, Error **errp) return true; } -bool smbios_validate_table(Error **errp) +bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp) { - if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && + 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); @@ -669,14 +668,15 @@ 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) { 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; } @@ -1011,11 +1011,10 @@ void smbios_set_default_processor_family(uint16_t processor_family) void smbios_set_defaults(const char *manufacturer, const char *product, const char *version, - bool uuid_encoded, SmbiosEntryPointType ep_type) + bool uuid_encoded) { smbios_have_defaults = true; smbios_uuid_encoded = uuid_encoded; - smbios_ep_type = ep_type; SMBIOS_SET_DEFAULT(smbios_type1.manufacturer, manufacturer); SMBIOS_SET_DEFAULT(smbios_type1.product, product); @@ -1032,9 +1031,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); @@ -1084,6 +1083,7 @@ static void smbios_entry_point_setup(void) } 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, @@ -1092,6 +1092,9 @@ void smbios_get_tables(MachineState *ms, { unsigned i, dimm_cnt, offset; + assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 || + ep_type == SMBIOS_ENTRY_POINT_TYPE_64); + g_free(smbios_tables); smbios_tables = g_memdup2(usr_blobs, usr_blobs_len); smbios_tables_len = usr_blobs_len; @@ -1106,7 +1109,7 @@ void smbios_get_tables(MachineState *ms, assert(ms->smp.sockets >= 1); for (i = 0; i < ms->smp.sockets; i++) { - smbios_build_type_4_table(ms, i); + smbios_build_type_4_table(ms, i, ep_type); } smbios_build_type_8_table(); @@ -1155,10 +1158,10 @@ void smbios_get_tables(MachineState *ms, if (!smbios_check_type4_count(ms->smp.sockets, errp)) { goto err_exit; } - if (!smbios_validate_table(errp)) { + if (!smbios_validate_table(ep_type, errp)) { goto err_exit; } - smbios_entry_point_setup(); + smbios_entry_point_setup(ep_type); /* return tables blob and entry point (anchor), and their sizes */ *tables = smbios_tables; diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c index a6544bf55a..06907cd16c 100644 --- a/hw/smbios/smbios_legacy.c +++ b/hw/smbios/smbios_legacy.c @@ -173,7 +173,7 @@ uint8_t *smbios_get_table_legacy(size_t *length, Error **errp) smbios_build_type_0_fields(); smbios_build_type_1_fields(); - if (!smbios_validate_table(errp)) { + if (!smbios_validate_table(SMBIOS_ENTRY_POINT_TYPE_32, errp)) { goto err_exit; } diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index d4b91d5a14..8d3fb2fb3b 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -326,16 +326,17 @@ struct smbios_type_127 { struct smbios_structure_header header; } QEMU_PACKED; -bool smbios_validate_table(Error **errp); +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 uuid_encoded, SmbiosEntryPointType ep_type); + bool uuid_encoded); void smbios_set_default_processor_family(uint16_t processor_family); 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, From 6735a4943e024715cf256625f02231e88f76c40b Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:55 +0100 Subject: [PATCH 17/24] smbios: clear smbios_type4_count before building tables it will help to keep type 4 tables accounting correct in case SMBIOS tables are built multiple times. Signed-off-by: Igor Mammedov Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-15-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/smbios/smbios.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 0ab77b5ec8..48713aa505 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -1096,6 +1096,7 @@ void smbios_get_tables(MachineState *ms, ep_type == SMBIOS_ENTRY_POINT_TYPE_64); 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; From 4901b80e13be62fc8ea2d8844941f336076af266 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:56 +0100 Subject: [PATCH 18/24] smbios: extend smbios-entry-point-type with 'auto' value later patches will use it to pick SMBIOS version at runtime depending on configuration. Signed-off-by: Igor Mammedov Acked-by: Markus Armbruster Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-16-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- qapi/machine.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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: From 4840c8a2b4666cf1b320a387c3f8b609258e7654 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:57 +0100 Subject: [PATCH 19/24] smbios: in case of entry point is 'auto' try to build v2 tables 1st QEMU for some time now uses SMBIOS 3.0 for PC/Q35 machines by default, however Windows has a bug in locating SMBIOS 3.0 entrypoint and fails to find tables when booted on SeaBIOS (on UEFI SMBIOS 3.0 tables work fine since firmware hands over tables in another way) Missing SMBIOS tables may lead to some issues for guest though (worst are: possible reactiveation, inability to get virtio drivers from 'Windows Update') It's unclear at this point if MS will fix the issue on their side. So instead of it (or rather in addition) this patch will try to workaround the issue. aka, use smbios-entry-point-type=auto to make QEMU try generating conservative SMBIOS 2.0 tables and if that fails (due to limits/requested configuration) fallback to SMBIOS 3.0 tables. With this in place majority of users will use SMBIOS 2.0 tables which work fine with (Windows + legacy BIOS). The configurations that is not to possible to describe with SMBIOS 2.0 will switch automatically to SMBIOS 3.0 (which will trigger Windows bug but there is nothing QEMU can do here, so go and aks Microsoft to real fix). Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-17-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/smbios/smbios.c | 52 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 48713aa505..b0467347c5 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -1082,7 +1082,7 @@ static void smbios_entry_point_setup(SmbiosEntryPointType ep_type) } } -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, @@ -1091,6 +1091,7 @@ void smbios_get_tables(MachineState *ms, Error **errp) { unsigned i, dimm_cnt, offset; + ERRP_GUARD(); assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 || ep_type == SMBIOS_ENTRY_POINT_TYPE_64); @@ -1177,11 +1178,56 @@ void smbios_get_tables(MachineState *ms, abort(); } - return; + return true; err_exit: g_free(smbios_tables); smbios_tables = NULL; - return; + 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) From 5ed7948213af8cba0c6153611aa0aea55069bac2 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:58 +0100 Subject: [PATCH 20/24] smbios: error out when building type 4 table is not possible If SMBIOS v2 version is requested but number of cores/threads are more than it's possible to describe with v2, error out instead of silently ignoring the fact and filling core/thread count with bogus values. This will help caller to decide if it should fallback to SMBIOSv3 when smbios-entry-point-type='auto' Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-18-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/smbios/smbios.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index b0467347c5..eed5787b15 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -669,7 +669,8 @@ static void smbios_build_type_3_table(void) } static void smbios_build_type_4_table(MachineState *ms, unsigned instance, - SmbiosEntryPointType ep_type) + SmbiosEntryPointType ep_type, + Error **errp) { char sock_str[128]; size_t tbl_len = SMBIOS_TYPE_4_LEN_V28; @@ -723,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; @@ -1111,7 +1118,10 @@ static bool smbios_get_tables_ep(MachineState *ms, assert(ms->smp.sockets >= 1); for (i = 0; i < ms->smp.sockets; i++) { - smbios_build_type_4_table(ms, i, ep_type); + smbios_build_type_4_table(ms, i, ep_type, errp); + if (*errp) { + goto err_exit; + } } smbios_build_type_8_table(); From c74f0126ce5d2b3ffec929c5f9f4ce961c7e2366 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:22:59 +0100 Subject: [PATCH 21/24] tests: acpi/smbios: whitelist expected blobs Signed-off-by: Igor Mammedov Acked-by: Ani Sinha Message-Id: <20240314152302.2324164-19-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..81148a604f 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/SSDT.dimmpxm", From 2c7c45b3d0ba097ac99bf4b9a13c6de1d7724032 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:23:00 +0100 Subject: [PATCH 22/24] pc/q35: set SMBIOS entry point type to 'auto' by default Use smbios-entry-point-type='auto' for newer machine types as a workaround for Windows not detecting SMBIOS tables. Which makes QEMU pick SMBIOS tables based on configuration (with 2.x preferred and fallback to 3.x if the former isn't compatible with configuration) Default compat setting of smbios-entry-point-type after series for pc/q35 machines: * 9.0-newer: 'auto' * 8.1-8.2: '64' * 8.0-older: '32' Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2008 Signed-off-by: Igor Mammedov Reviewed-by: Ani Sinha Tested-by: Fiona Ebner Message-Id: <20240314152302.2324164-20-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 2 +- hw/i386/pc_piix.c | 4 ++++ hw/i386/pc_q35.c | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 44eb073abd..e80f02bef4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -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, From 86e372ad1e22df373878e5c1cbda2d5026a34331 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:23:01 +0100 Subject: [PATCH 23/24] tests: acpi: update expected SSDT.dimmpxm blob address shift is caused by switch to 32-bit SMBIOS entry point which has slightly different size from 64-bit one and happens to trigger a bit different memory layout. Expected diff: - Name (MEMA, 0x07FFE000) + Name (MEMA, 0x07FFF000) Signed-off-by: Igor Mammedov Acked-by: Ani Sinha Message-Id: <20240314152302.2324164-21-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/SSDT.dimmpxm | Bin 1815 -> 1815 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/q35/SSDT.dimmpxm b/tests/data/acpi/q35/SSDT.dimmpxm index 70f133412f5e0aa128ab210245a8de7304eeb843..9ea4e0d0ceaa8a5cbd706afb6d49de853fafe654 100644 GIT binary patch delta 23 ecmbQvH=U0wIM^jboSlJzam_|9E_UV*|JeaVTLvQl delta 23 ecmbQvH=U0wIM^jboSlJzanD9BE_UVz|JeaVy9Ofw diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 81148a604f..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/q35/SSDT.dimmpxm", From bb949df637bdb6136a9acca55a2371fe1721e109 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Mar 2024 16:23:02 +0100 Subject: [PATCH 24/24] smbios: add extra comments to smbios_get_table_legacy() Signed-off-by: Igor Mammedov Message-Id: <20240314152302.2324164-22-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/smbios/smbios_legacy.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c index 06907cd16c..c37a8ee821 100644 --- a/hw/smbios/smbios_legacy.c +++ b/hw/smbios/smbios_legacy.c @@ -151,6 +151,9 @@ uint8_t *smbios_get_table_legacy(size_t *length, Error **errp) 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++) { @@ -166,6 +169,10 @@ uint8_t *smbios_get_table_legacy(size_t *length, Error **errp) 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;