New in version 7 of the patch set:
- patch set now down to only 7 patches;
- machine versions 2.0 and older currently unaffected by this patch set, will continue using field overrides as before
- patches 1..6 are very small and simple, and set the stage for patch #7
- patch 7 introduces the new aggregate-table functionality; it consists mostly of insertions, and should therefore be relatively easy to follow.
- "-smbios file=<foo>" functionality happens before we know whether we're in legacy or new/aggregate-table mode, so we insert the file blob(s) into both the legacy and the aggregate tables, and keep the one we need once we know which one that is.
For those interested, the code is also structured for easy subsequent removal of the legacy mode, if/when that becomes applicable ;)
Thanks again for any feedback and comments (or for just applying the patch) !
Gabriel
Gabriel L. Somlo (7): SMBIOS: Rename symbols to better reflect future use SMBIOS: Update header file definitions SMBIOS: Use macro to set smbios defaults SMBIOS: Use bitmaps to prevent incompatible comand line options E820: Add interface for accessing e820 table PC: Add (empty) compat functions for machine version 2.0 SMBIOS: Build aggregate smbios tables and entry point
hw/i386/pc.c | 39 ++- hw/i386/pc_piix.c | 29 +- hw/i386/pc_q35.c | 25 +- hw/i386/smbios.c | 789 +++++++++++++++++++++++++++++++++++++++++++---- include/hw/i386/pc.h | 2 + include/hw/i386/smbios.h | 99 ++++-- 6 files changed, 879 insertions(+), 104 deletions(-)
Rename the following symbols:
- smbios_set_type1_defaults() to the more general smbios_set_defaults(); - bool smbios_type1_defaults to the more general smbios_defaults; - smbios_get_table() to smbios_get_table_legacy();
This patch contains no functional changes.
Signed-off-by: Gabriel Somlo somlo@cmu.edu --- hw/i386/pc.c | 2 +- hw/i386/pc_piix.c | 14 +++++++------- hw/i386/pc_q35.c | 10 +++++----- hw/i386/smbios.c | 18 ++++++++++++------ include/hw/i386/smbios.h | 6 +++--- 5 files changed, 28 insertions(+), 22 deletions(-)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 14f0d91..f95814b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -655,7 +655,7 @@ static FWCfgState *bochs_bios_init(void) acpi_tables, acpi_tables_len); fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override());
- smbios_table = smbios_get_table(&smbios_len); + smbios_table = smbios_get_table_legacy(&smbios_len); if (smbios_table) fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, smbios_table, smbios_len); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 7930a26..cca310b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -60,7 +60,7 @@ static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
static bool has_pci_info; static bool has_acpi_build = true; -static bool smbios_type1_defaults = true; +static bool smbios_defaults = true; /* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte * pages in the host. @@ -143,10 +143,10 @@ static void pc_init1(QEMUMachineInitArgs *args, guest_info->has_pci_info = has_pci_info; guest_info->isapc_ram_fw = !pci_enabled;
- if (smbios_type1_defaults) { + if (smbios_defaults) { /* These values are guest ABI, do not change */ - smbios_set_type1_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", - args->machine->name); + smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", + args->machine->name); }
/* allocate ram and load rom/bios */ @@ -264,7 +264,7 @@ static void pc_init_pci(QEMUMachineInitArgs *args)
static void pc_compat_1_7(QEMUMachineInitArgs *args) { - smbios_type1_defaults = false; + smbios_defaults = false; gigabyte_align = false; option_rom_has_mr = true; x86_cpu_compat_disable_kvm_features(FEAT_1_ECX, CPUID_EXT_X2APIC); @@ -345,7 +345,7 @@ static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args) { has_pci_info = false; has_acpi_build = false; - smbios_type1_defaults = false; + smbios_defaults = false; x86_cpu_compat_disable_kvm_features(FEAT_KVM, KVM_FEATURE_PV_EOI); enable_compat_apic_id_mode(); pc_init1(args, 1, 0); @@ -355,7 +355,7 @@ static void pc_init_isa(QEMUMachineInitArgs *args) { has_pci_info = false; has_acpi_build = false; - smbios_type1_defaults = false; + smbios_defaults = false; if (!args->cpu_model) { args->cpu_model = "486"; } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index c844dc2..b2b80c9 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -50,7 +50,7 @@
static bool has_pci_info; static bool has_acpi_build = true; -static bool smbios_type1_defaults = true; +static bool smbios_defaults = true; /* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte * pages in the host. @@ -130,10 +130,10 @@ static void pc_q35_init(QEMUMachineInitArgs *args) guest_info->isapc_ram_fw = false; guest_info->has_acpi_build = has_acpi_build;
- if (smbios_type1_defaults) { + if (smbios_defaults) { /* These values are guest ABI, do not change */ - smbios_set_type1_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", - args->machine->name); + smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", + args->machine->name); }
/* allocate ram and load rom/bios */ @@ -242,7 +242,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
static void pc_compat_1_7(QEMUMachineInitArgs *args) { - smbios_type1_defaults = false; + smbios_defaults = false; gigabyte_align = false; option_rom_has_mr = true; x86_cpu_compat_disable_kvm_features(FEAT_1_ECX, CPUID_EXT_X2APIC); diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index e8f41ad..e734d4c 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -21,9 +21,8 @@ #include "hw/i386/smbios.h" #include "hw/loader.h"
-/* - * Structures shared with the BIOS - */ + +/* legacy structures and constants for <= 2.0 machines */ struct smbios_header { uint16_t length; uint8_t type; @@ -46,6 +45,9 @@ struct smbios_table {
static uint8_t *smbios_entries; static size_t smbios_entries_len; +/* end: legacy structures & constants for <= 2.0 machines */ + + static int smbios_type4_count = 0; static bool smbios_immutable;
@@ -187,6 +189,8 @@ static void smbios_check_collision(int type, int entry) } }
+ +/* 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; @@ -256,8 +260,8 @@ static void smbios_build_type_1_fields(void) } }
-void smbios_set_type1_defaults(const char *manufacturer, - const char *product, const char *version) +void smbios_set_defaults(const char *manufacturer, const char *product, + const char *version) { if (!type1.manufacturer) { type1.manufacturer = manufacturer; @@ -270,7 +274,7 @@ void smbios_set_type1_defaults(const char *manufacturer, } }
-uint8_t *smbios_get_table(size_t *length) +uint8_t *smbios_get_table_legacy(size_t *length) { if (!smbios_immutable) { smbios_build_type_0_fields(); @@ -281,6 +285,8 @@ uint8_t *smbios_get_table(size_t *length) *length = smbios_entries_len; return smbios_entries; } +/* end: legacy setup functions for <= 2.0 machines */ +
static void save_opt(const char **dest, QemuOpts *opts, const char *name) { diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 18fb970..f808199 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -16,9 +16,9 @@ #include "qemu/option.h"
void smbios_entry_add(QemuOpts *opts); -void smbios_set_type1_defaults(const char *manufacturer, - const char *product, const char *version); -uint8_t *smbios_get_table(size_t *length); +void smbios_set_defaults(const char *manufacturer, const char *product, + const char *version); +uint8_t *smbios_get_table_legacy(size_t *length);
/* * SMBIOS spec defined tables
Add definitions for smbios entry point (anchor), and for type 2 (base board) structure which is required by some versions of OS X.
Remove definition for type 20 (memory device mapped address) structure, which is no longer required as of smbios spec v2.5.
Update all other structure definitions to bring them into compliance with smbios spec v2.8.
This patch contains no functional changes.
Signed-off-by: Gabriel Somlo somlo@cmu.edu --- include/hw/i386/smbios.h | 88 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 22 deletions(-)
diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index f808199..777e025 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -24,6 +24,26 @@ uint8_t *smbios_get_table_legacy(size_t *length); * SMBIOS spec defined tables */
+/* SMBIOS entry point (anchor). + * BIOS must place this at a 16-bit-aligned address between 0xf0000 and 0xfffff. + */ +struct smbios_entry_point { + uint8_t anchor_string[4]; + uint8_t checksum; + uint8_t length; + uint8_t smbios_major_version; + uint8_t smbios_minor_version; + uint16_t max_structure_size; + uint8_t entry_point_revision; + uint8_t formatted_area[5]; + uint8_t intermediate_anchor_string[5]; + uint8_t intermediate_checksum; + uint16_t structure_table_length; + uint32_t structure_table_address; + uint16_t number_of_structures; + uint8_t smbios_bcd_revision; +} QEMU_PACKED; + /* This goes at the beginning of every SMBIOS structure. */ struct smbios_structure_header { uint8_t type; @@ -60,7 +80,23 @@ struct smbios_type_1 { uint8_t family_str; } QEMU_PACKED;
-/* SMBIOS type 3 - System Enclosure (v2.3) */ +/* SMBIOS type 2 - Base Board */ +struct smbios_type_2 { + struct smbios_structure_header header; + uint8_t manufacturer_str; + uint8_t product_str; + uint8_t version_str; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t feature_flags; + uint8_t location_str; + uint16_t chassis_handle; + uint8_t board_type; + uint8_t contained_element_count; + /* contained elements follow */ +} QEMU_PACKED; + +/* SMBIOS type 3 - System Enclosure (v2.7) */ struct smbios_type_3 { struct smbios_structure_header header; uint8_t manufacturer_str; @@ -76,10 +112,11 @@ struct smbios_type_3 { uint8_t height; uint8_t number_of_power_cords; uint8_t contained_element_count; - // contained elements follow + uint8_t sku_number_str; + /* contained elements follow */ } QEMU_PACKED;
-/* SMBIOS type 4 - Processor Information (v2.0) */ +/* SMBIOS type 4 - Processor Information (v2.6) */ struct smbios_type_4 { struct smbios_structure_header header; uint8_t socket_designation_str; @@ -97,11 +134,17 @@ struct smbios_type_4 { uint16_t l1_cache_handle; uint16_t l2_cache_handle; uint16_t l3_cache_handle; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t part_number_str; + uint8_t core_count; + uint8_t core_enabled; + uint8_t thread_count; + uint16_t processor_characteristics; + uint16_t processor_family2; } QEMU_PACKED;
-/* SMBIOS type 16 - Physical Memory Array - * Associated with one type 17 (Memory Device). - */ +/* SMBIOS type 16 - Physical Memory Array (v2.7) */ struct smbios_type_16 { struct smbios_structure_header header; uint8_t location; @@ -110,10 +153,10 @@ struct smbios_type_16 { uint32_t maximum_capacity; uint16_t memory_error_information_handle; uint16_t number_of_memory_devices; + uint64_t extended_maximum_capacity; } QEMU_PACKED; -/* SMBIOS type 17 - Memory Device - * Associated with one type 19 - */ + +/* SMBIOS type 17 - Memory Device (v2.8) */ struct smbios_type_17 { struct smbios_structure_header header; uint16_t physical_memory_array_handle; @@ -127,27 +170,28 @@ struct smbios_type_17 { uint8_t bank_locator_str; uint8_t memory_type; uint16_t type_detail; + uint16_t speed; + uint8_t manufacturer_str; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t part_number_str; + uint8_t attributes; + uint32_t extended_size; + uint32_t configured_clock_speed; + uint32_t minimum_voltage; + uint32_t maximum_voltage; + uint32_t configured_voltage; } QEMU_PACKED;
-/* SMBIOS type 19 - Memory Array Mapped Address */ +/* SMBIOS type 19 - Memory Array Mapped Address (v2.7) */ struct smbios_type_19 { struct smbios_structure_header header; uint32_t starting_address; uint32_t ending_address; uint16_t memory_array_handle; uint8_t partition_width; -} QEMU_PACKED; - -/* SMBIOS type 20 - Memory Device Mapped Address */ -struct smbios_type_20 { - struct smbios_structure_header header; - uint32_t starting_address; - uint32_t ending_address; - uint16_t memory_device_handle; - uint16_t memory_array_mapped_address_handle; - uint8_t partition_row_position; - uint8_t interleave_position; - uint8_t interleaved_data_depth; + uint64_t extended_starting_address; + uint64_t extended_ending_address; } QEMU_PACKED;
/* SMBIOS type 32 - System Boot Information */
The function smbios_set_defaults() uses a repeating code pattern for each field. This patch replaces that pattern with a macro.
This patch contains no functional changes.
Signed-off-by: Gabriel Somlo somlo@cmu.edu --- hw/i386/smbios.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index e734d4c..9f83bfb 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -260,20 +260,6 @@ static void smbios_build_type_1_fields(void) } }
-void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version) -{ - if (!type1.manufacturer) { - type1.manufacturer = manufacturer; - } - if (!type1.product) { - type1.product = product; - } - if (!type1.version) { - type1.version = version; - } -} - uint8_t *smbios_get_table_legacy(size_t *length) { if (!smbios_immutable) { @@ -288,6 +274,19 @@ uint8_t *smbios_get_table_legacy(size_t *length) /* end: legacy setup functions for <= 2.0 machines */
+#define SMBIOS_SET_DEFAULT(field, value) \ + if (!field) { \ + field = value; \ + } + +void smbios_set_defaults(const char *manufacturer, const char *product, + const char *version) +{ + SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type1.product, product); + SMBIOS_SET_DEFAULT(type1.version, version); +} + static void save_opt(const char **dest, QemuOpts *opts, const char *name) { const char *val = qemu_opt_get(opts, name);
Replace existing smbios_check_collision() functionality with a pair of bitmaps: have_binfile_bitmap and have_fields_bitmap. Bits corresponding to each smbios type are set by smbios_entry_add(), which also uses the bitmaps to ensure that binary blobs and field values are never accepted for the same type.
These bitmaps will also be used in the future to decide whether or not to build a full table for a given smbios type.
Signed-off-by: Gabriel Somlo somlo@cmu.edu --- hw/i386/smbios.c | 50 +++++++++++++++++++----------------------------- include/hw/i386/smbios.h | 2 ++ 2 files changed, 22 insertions(+), 30 deletions(-)
diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 9f83bfb..6bbfd15 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -51,11 +51,8 @@ static size_t smbios_entries_len; static int smbios_type4_count = 0; static bool smbios_immutable;
-static struct { - bool seen; - int headertype; - Location loc; -} first_opt[2]; +static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); +static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1);
static struct { const char *vendor, *version, *date; @@ -166,29 +163,6 @@ static void smbios_validate_table(void) } }
-/* - * To avoid unresolvable overlaps in data, don't allow both - * tables and fields for the same smbios type. - */ -static void smbios_check_collision(int type, int entry) -{ - if (type < ARRAY_SIZE(first_opt)) { - if (first_opt[type].seen) { - if (first_opt[type].headertype != entry) { - error_report("Can't mix file= and type= for same type"); - loc_push_restore(&first_opt[type].loc); - error_report("This is the conflicting setting"); - loc_pop(&first_opt[type].loc); - exit(1); - } - } else { - first_opt[type].seen = true; - first_opt[type].headertype = entry; - loc_save(&first_opt[type].loc); - } - } -} -
/* legacy setup functions for <= 2.0 machines */ static void smbios_add_field(int type, int offset, const void *data, size_t len) @@ -337,7 +311,14 @@ void smbios_entry_add(QemuOpts *opts) }
header = (struct smbios_structure_header *)(table->data); - smbios_check_collision(header->type, SMBIOS_TABLE_ENTRY); + + if (test_bit(header->type, have_fields_bitmap)) { + error_report("can't load type %d struct, fields already specified!", + header->type); + exit(1); + } + set_bit(header->type, have_binfile_bitmap); + if (header->type == 4) { smbios_type4_count++; } @@ -352,7 +333,16 @@ void smbios_entry_add(QemuOpts *opts) if (val) { unsigned long type = strtoul(val, NULL, 0);
- smbios_check_collision(type, SMBIOS_FIELD_ENTRY); + if (type > SMBIOS_MAX_TYPE) { + error_report("out of range!"); + exit(1); + } + + if (test_bit(type, have_binfile_bitmap)) { + error_report("can't add fields, binary file already loaded!"); + exit(1); + } + set_bit(type, have_fields_bitmap);
switch (type) { case 0: diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 777e025..3a9361d 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -15,6 +15,8 @@
#include "qemu/option.h"
+#define SMBIOS_MAX_TYPE 127 + void smbios_entry_add(QemuOpts *opts); void smbios_set_defaults(const char *manufacturer, const char *product, const char *version);
Add the following two functions:
- e820_get_num_entries() - query the size of the e820 table - e820_get_entry() - grab an entry matching a given set of criteria
This interface is currently necessary for creating type 19 (memory array mapped address) structures in smbios.
Signed-off-by: Gabriel Somlo somlo@cmu.edu --- hw/i386/pc.c | 15 +++++++++++++++ include/hw/i386/pc.h | 2 ++ 2 files changed, 17 insertions(+)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f95814b..7155269 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -612,6 +612,21 @@ int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) return e820_entries; }
+int e820_get_num_entries(void) +{ + return e820_entries; +} + +bool e820_get_entry(int idx, uint32_t type, uint64_t *address, uint64_t *length) +{ + if (idx < e820_entries && e820_table[idx].type == cpu_to_le32(type)) { + *address = le64_to_cpu(e820_table[idx].address); + *length = le64_to_cpu(e820_table[idx].length); + return true; + } + return false; +} + /* Calculates the limit to CPU APIC ID values * * This function returns the limit for the APIC ID value, so that all diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 9010246..9f26e14 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -239,6 +239,8 @@ uint16_t pvpanic_port(void); #define E820_UNUSABLE 5
int e820_add_entry(uint64_t, uint64_t, uint32_t); +int e820_get_num_entries(void); +bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
#define PC_Q35_COMPAT_1_7 \ PC_COMPAT_1_7, \
This patch contains no functional changes.
Signed-off-by: Gabriel Somlo somlo@cmu.edu --- hw/i386/pc_piix.c | 13 ++++++++++++- hw/i386/pc_q35.c | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index cca310b..3d3f4b7 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -262,8 +262,13 @@ static void pc_init_pci(QEMUMachineInitArgs *args) pc_init1(args, 1, 1); }
+static void pc_compat_2_0(QEMUMachineInitArgs *args) +{ +} + static void pc_compat_1_7(QEMUMachineInitArgs *args) { + pc_compat_2_0(args); smbios_defaults = false; gigabyte_align = false; option_rom_has_mr = true; @@ -303,6 +308,12 @@ static void pc_compat_1_2(QEMUMachineInitArgs *args) x86_cpu_compat_disable_kvm_features(FEAT_KVM, KVM_FEATURE_PV_EOI); }
+static void pc_init_pci_2_0(QEMUMachineInitArgs *args) +{ + pc_compat_2_0(args); + pc_init_pci(args); +} + static void pc_init_pci_1_7(QEMUMachineInitArgs *args) { pc_compat_1_7(args); @@ -391,7 +402,7 @@ static QEMUMachine pc_i440fx_machine_v2_0 = { PC_I440FX_2_0_MACHINE_OPTIONS, .name = "pc-i440fx-2.0", .alias = "pc", - .init = pc_init_pci, + .init = pc_init_pci_2_0, .is_default = 1, };
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b2b80c9..4e40196 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -240,8 +240,13 @@ static void pc_q35_init(QEMUMachineInitArgs *args) } }
+static void pc_compat_2_0(QEMUMachineInitArgs *args) +{ +} + static void pc_compat_1_7(QEMUMachineInitArgs *args) { + pc_compat_2_0(args); smbios_defaults = false; gigabyte_align = false; option_rom_has_mr = true; @@ -268,6 +273,12 @@ static void pc_compat_1_4(QEMUMachineInitArgs *args) x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ); }
+static void pc_q35_init_2_0(QEMUMachineInitArgs *args) +{ + pc_compat_2_0(args); + pc_q35_init(args); +} + static void pc_q35_init_1_7(QEMUMachineInitArgs *args) { pc_compat_1_7(args); @@ -305,7 +316,7 @@ static QEMUMachine pc_q35_machine_v2_0 = { PC_Q35_2_0_MACHINE_OPTIONS, .name = "pc-q35-2.0", .alias = "q35", - .init = pc_q35_init, + .init = pc_q35_init_2_0, };
#define PC_Q35_1_7_MACHINE_OPTIONS PC_Q35_MACHINE_OPTIONS
Build an aggregate set of smbios tables and an entry point structure.
Insert tables and entry point into fw_cfg respectively under "etc/smbios/smbios-tables" and "etc/smbios/smbios-anchor".
Machine types <= 2.0 will for now continue using field-by-field overrides to SeaBIOS defaults, but for machine types 2.1 and up we expect the BIOS to look for and use the aggregate tables generated by this patch.
Signed-off-by: Gabriel Somlo somlo@cmu.edu --- hw/i386/pc.c | 24 +- hw/i386/pc_piix.c | 4 +- hw/i386/pc_q35.c | 4 +- hw/i386/smbios.c | 708 +++++++++++++++++++++++++++++++++++++++++++++-- include/hw/i386/smbios.h | 5 +- 5 files changed, 720 insertions(+), 25 deletions(-)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7155269..07de238 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -642,8 +642,8 @@ static unsigned int pc_apic_id_limit(unsigned int max_cpus) static FWCfgState *bochs_bios_init(void) { FWCfgState *fw_cfg; - uint8_t *smbios_table; - size_t smbios_len; + uint8_t *smbios_tables, *smbios_anchor; + size_t smbios_tables_len, smbios_anchor_len; uint64_t *numa_fw_cfg; int i, j; unsigned int apic_id_limit = pc_apic_id_limit(max_cpus); @@ -670,10 +670,21 @@ static FWCfgState *bochs_bios_init(void) acpi_tables, acpi_tables_len); fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override());
- smbios_table = smbios_get_table_legacy(&smbios_len); - if (smbios_table) + smbios_tables = smbios_get_table_legacy(&smbios_tables_len); + if (smbios_tables) { fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, - smbios_table, smbios_len); + smbios_tables, smbios_tables_len); + } + + smbios_get_tables(&smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len); + if (smbios_anchor) { + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables", + smbios_tables, smbios_tables_len); + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor", + smbios_anchor, smbios_anchor_len); + } + fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, &e820_reserve, sizeof(e820_reserve)); fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, @@ -1042,6 +1053,9 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) sysbus_mmio_map_overlap(SYS_BUS_DEVICE(icc_bridge), 0, APIC_DEFAULT_ADDRESS, 0x1000); } + + /* tell smbios about cpuid version and features */ + smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]); }
/* pci-info ROM file. Little endian format */ diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 3d3f4b7..f3e2c4e 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -61,6 +61,7 @@ static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; static bool has_pci_info; static bool has_acpi_build = true; static bool smbios_defaults = true; +static bool smbios_legacy_mode; /* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte * pages in the host. @@ -146,7 +147,7 @@ static void pc_init1(QEMUMachineInitArgs *args, if (smbios_defaults) { /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", - args->machine->name); + args->machine->name, smbios_legacy_mode); }
/* allocate ram and load rom/bios */ @@ -264,6 +265,7 @@ static void pc_init_pci(QEMUMachineInitArgs *args)
static void pc_compat_2_0(QEMUMachineInitArgs *args) { + smbios_legacy_mode = true; }
static void pc_compat_1_7(QEMUMachineInitArgs *args) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 4e40196..2b565cb 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -51,6 +51,7 @@ static bool has_pci_info; static bool has_acpi_build = true; static bool smbios_defaults = true; +static bool smbios_legacy_mode; /* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte * pages in the host. @@ -133,7 +134,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) if (smbios_defaults) { /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", - args->machine->name); + args->machine->name, smbios_legacy_mode); }
/* allocate ram and load rom/bios */ @@ -242,6 +243,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
static void pc_compat_2_0(QEMUMachineInitArgs *args) { + smbios_legacy_mode = true; }
static void pc_compat_1_7(QEMUMachineInitArgs *args) diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 6bbfd15..353e12e 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -18,6 +18,8 @@ #include "qemu/config-file.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" +#include "sysemu/cpus.h" +#include "hw/i386/pc.h" #include "hw/i386/smbios.h" #include "hw/loader.h"
@@ -45,11 +47,20 @@ struct smbios_table {
static uint8_t *smbios_entries; static size_t smbios_entries_len; +static bool smbios_legacy = true; /* end: legacy structures & constants for <= 2.0 machines */
+static uint8_t *smbios_tables; +static size_t smbios_tables_len; +static unsigned smbios_table_max; +static unsigned smbios_table_cnt; +static struct smbios_entry_point 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 DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); @@ -65,6 +76,22 @@ static struct { /* uuid is in qemu_uuid[] */ } type1;
+static struct { + const char *manufacturer, *product, *version, *serial, *asset, *location; +} type2; + +static struct { + const char *manufacturer, *version, *serial, *asset, *sku; +} type3; + +static struct { + const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; +} type4; + +static struct { + const char *loc_pfx, *bank, *manufacturer, *serial, *asset, *part; +} type17; + static QemuOptsList qemu_smbios_opts = { .name = "smbios", .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), @@ -148,6 +175,134 @@ static const QemuOptDesc qemu_smbios_type1_opts[] = { { /* end of list */ } };
+static const QemuOptDesc qemu_smbios_type2_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "product", + .type = QEMU_OPT_STRING, + .help = "product name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "location", + .type = QEMU_OPT_STRING, + .help = "location in chassis", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type3_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "sku", + .type = QEMU_OPT_STRING, + .help = "SKU number", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type4_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "sock_pfx", + .type = QEMU_OPT_STRING, + .help = "socket designation string prefix", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "part", + .type = QEMU_OPT_STRING, + .help = "part number", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type17_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "loc_pfx", + .type = QEMU_OPT_STRING, + .help = "device locator string prefix", + },{ + .name = "bank", + .type = QEMU_OPT_STRING, + .help = "bank locator string", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "part", + .type = QEMU_OPT_STRING, + .help = "part number", + }, + { /* end of list */ } +}; + static void smbios_register_config(void) { qemu_add_opts(&qemu_smbios_opts); @@ -157,8 +312,11 @@ machine_init(smbios_register_config);
static void smbios_validate_table(void) { - if (smbios_type4_count && smbios_type4_count != smp_cpus) { - error_report("Number of SMBIOS Type 4 tables must match cpu count"); + uint32_t expect_t4_count = smbios_legacy ? smp_cpus : smbios_smp_sockets; + + if (smbios_type4_count && smbios_type4_count != expect_t4_count) { + error_report("Expected %d SMBIOS Type 4 tables, got %d instead", + expect_t4_count, smbios_type4_count); exit(1); } } @@ -236,6 +394,11 @@ static void smbios_build_type_1_fields(void)
uint8_t *smbios_get_table_legacy(size_t *length) { + if (!smbios_legacy) { + *length = 0; + return NULL; + } + if (!smbios_immutable) { smbios_build_type_0_fields(); smbios_build_type_1_fields(); @@ -248,17 +411,454 @@ uint8_t *smbios_get_table_legacy(size_t *length) /* end: legacy setup functions for <= 2.0 machines */
+static bool smbios_skip_table(uint8_t type, bool required_table) +{ + if (test_bit(type, have_binfile_bitmap)) { + return true; /* user provided their own binary blob(s) */ + } + if (test_bit(type, have_fields_bitmap)) { + return false; /* user provided fields via command line */ + } + if (smbios_have_defaults && required_table) { + return false; /* we're building tables, and this one's required */ + } + return true; +} + +#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \ + struct smbios_type_##tbl_type *t; \ + size_t t_off; /* table offset into smbios_tables */ \ + int str_index = 0; \ + do { \ + /* should we skip building this table ? */ \ + if (smbios_skip_table(tbl_type, tbl_required)) { \ + return; \ + } \ + \ + /* use offset of table t within smbios_tables */ \ + /* (pointer must be updated after each realloc) */ \ + t_off = smbios_tables_len; \ + smbios_tables_len += sizeof(*t); \ + smbios_tables = g_realloc(smbios_tables, smbios_tables_len); \ + t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \ + \ + t->header.type = tbl_type; \ + t->header.length = sizeof(*t); \ + t->header.handle = tbl_handle; \ + } while (0) + +#define SMBIOS_TABLE_SET_STR(tbl_type, field, value) \ + do { \ + int len = (value != NULL) ? strlen(value) + 1 : 0; \ + if (len > 1) { \ + smbios_tables = g_realloc(smbios_tables, \ + smbios_tables_len + len); \ + memcpy(smbios_tables + smbios_tables_len, value, len); \ + smbios_tables_len += len; \ + /* update pointer post-realloc */ \ + t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \ + t->field = ++str_index; \ + } else { \ + t->field = 0; \ + } \ + } while (0) + +#define SMBIOS_BUILD_TABLE_POST \ + do { \ + size_t term_cnt, t_size; \ + \ + /* add '\0' terminator (add two if no strings defined) */ \ + term_cnt = (str_index == 0) ? 2 : 1; \ + smbios_tables = g_realloc(smbios_tables, \ + smbios_tables_len + term_cnt); \ + memset(smbios_tables + smbios_tables_len, 0, term_cnt); \ + smbios_tables_len += term_cnt; \ + \ + /* update smbios max. element size */ \ + t_size = smbios_tables_len - t_off; \ + if (t_size > smbios_table_max) { \ + smbios_table_max = t_size; \ + } \ + \ + /* update smbios element count */ \ + smbios_table_cnt++; \ + } while (0) + +static void smbios_build_type_0_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(0, 0x000, 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); + + t->bios_starting_address_segment = 0xE800; /* hardcoded in SeaBIOS */ + + SMBIOS_TABLE_SET_STR(0, bios_release_date_str, type0.date); + + t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */ + + /* BIOS characteristics not supported */ + memset(t->bios_characteristics, 0, 8); + t->bios_characteristics[0] = 0x08; + + /* Enable targeted content distribution (needed for SVVP, per SeaBIOS) */ + t->bios_characteristics_extension_bytes[0] = 0; + t->bios_characteristics_extension_bytes[1] = 4; + + if (type0.have_major_minor) { + t->system_bios_major_release = type0.major; + t->system_bios_minor_release = type0.minor; + } else { + t->system_bios_major_release = 0; + t->system_bios_minor_release = 0; + } + + /* hardcoded in SeaBIOS */ + t->embedded_controller_major_release = 0xFF; + t->embedded_controller_minor_release = 0xFF; + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_1_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(1, 0x100, 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); + if (qemu_uuid_set) { + memcpy(t->uuid, qemu_uuid, 16); + } 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_BUILD_TABLE_POST; +} + +static void smbios_build_type_2_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(2, 0x200, false); /* optional */ + + SMBIOS_TABLE_SET_STR(2, manufacturer_str, type2.manufacturer); + SMBIOS_TABLE_SET_STR(2, product_str, type2.product); + SMBIOS_TABLE_SET_STR(2, version_str, type2.version); + SMBIOS_TABLE_SET_STR(2, serial_number_str, type2.serial); + SMBIOS_TABLE_SET_STR(2, asset_tag_number_str, type2.asset); + t->feature_flags = 0x01; /* Motherboard */ + SMBIOS_TABLE_SET_STR(2, location_str, type2.location); + t->chassis_handle = 0x300; /* Type 3 (System enclosure) */ + t->board_type = 0x0A; /* Motherboard */ + t->contained_element_count = 0; + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_3_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(3, 0x300, true); /* required */ + + SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer); + t->type = 0x01; /* Other */ + SMBIOS_TABLE_SET_STR(3, version_str, type3.version); + SMBIOS_TABLE_SET_STR(3, serial_number_str, type3.serial); + SMBIOS_TABLE_SET_STR(3, asset_tag_number_str, type3.asset); + t->boot_up_state = 0x03; /* Safe */ + t->power_supply_state = 0x03; /* Safe */ + t->thermal_state = 0x03; /* Safe */ + t->security_status = 0x02; /* Unknown */ + t->oem_defined = 0; + t->height = 0; + t->number_of_power_cords = 0; + t->contained_element_count = 0; + SMBIOS_TABLE_SET_STR(3, sku_number_str, type3.sku); + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_4_table(unsigned instance) +{ + char sock_str[128]; + + SMBIOS_BUILD_TABLE_PRE(4, 0x400 + instance, true); /* required */ + + snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance); + SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); + t->processor_type = 0x03; /* CPU */ + SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); + t->processor_id[0] = smbios_cpuid_version; + t->processor_id[1] = smbios_cpuid_features; + SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); + t->voltage = 0; + t->external_clock = 0; /* Unknown */ + t->max_speed = 0; /* Unknown */ + t->current_speed = 0; /* Unknown */ + t->status = 0x41; /* Socket populated, CPU enabled */ + t->processor_upgrade = 0x01; /* Other */ + t->l1_cache_handle = 0xFFFF; /* N/A */ + t->l2_cache_handle = 0xFFFF; /* N/A */ + t->l3_cache_handle = 0xFFFF; /* N/A */ + SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial); + SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); + SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); + t->core_count = t->core_enabled = smp_cores; + t->thread_count = smp_threads; + t->processor_characteristics = 0x02; /* Unknown */ + t->processor_family = t->processor_family2 = 0x01; /* Other */ + + SMBIOS_BUILD_TABLE_POST; + smbios_type4_count++; +} + +#define ONE_KB ((ram_addr_t)1 << 10) +#define ONE_MB ((ram_addr_t)1 << 20) +#define ONE_GB ((ram_addr_t)1 << 30) + +#define MAX_T16_STD_SZ 0x80000000 /* 2T in Kilobytes */ + +static void smbios_build_type_16_table(unsigned dimm_cnt) +{ + ram_addr_t size_kb; + + SMBIOS_BUILD_TABLE_PRE(16, 0x1000, true); /* required */ + + t->location = 0x01; /* Other */ + t->use = 0x03; /* System memory */ + t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ + size_kb = QEMU_ALIGN_UP(ram_size, ONE_KB) / ONE_KB; + if (size_kb < MAX_T16_STD_SZ) { + t->maximum_capacity = size_kb; + t->extended_maximum_capacity = 0; + } else { + t->maximum_capacity = MAX_T16_STD_SZ; + t->extended_maximum_capacity = ram_size; + } + t->memory_error_information_handle = 0xFFFE; /* Not provided */ + t->number_of_memory_devices = dimm_cnt; + + SMBIOS_BUILD_TABLE_POST; +} + +#define MAX_T17_STD_SZ 0x7FFF /* (32G - 1M), in Megabytes */ +#define MAX_T17_EXT_SZ 0x80000000 /* 2P, in Megabytes */ + +static void smbios_build_type_17_table(unsigned instance, ram_addr_t size) +{ + char loc_str[128]; + ram_addr_t size_mb; + + SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */ + + t->physical_memory_array_handle = 0x1000; /* Type 16 (Phys. Mem. Array) */ + t->memory_error_information_handle = 0xFFFE; /* Not provided */ + t->total_width = 0xFFFF; /* Unknown */ + t->data_width = 0xFFFF; /* Unknown */ + size_mb = QEMU_ALIGN_UP(size, ONE_MB) / ONE_MB; + if (size_mb < MAX_T17_STD_SZ) { + t->size = size_mb; + t->extended_size = 0; + } else { + assert(size_mb < MAX_T17_EXT_SZ); + t->size = MAX_T17_STD_SZ; + t->extended_size = size_mb; + } + t->form_factor = 0x09; /* DIMM */ + t->device_set = 0; /* Not in a set */ + snprintf(loc_str, sizeof(loc_str), "%s %d", type17.loc_pfx, instance); + SMBIOS_TABLE_SET_STR(17, device_locator_str, loc_str); + SMBIOS_TABLE_SET_STR(17, bank_locator_str, type17.bank); + t->memory_type = 0x07; /* RAM */ + t->type_detail = 0x02; /* Other */ + t->speed = 0; /* Unknown */ + SMBIOS_TABLE_SET_STR(17, manufacturer_str, type17.manufacturer); + SMBIOS_TABLE_SET_STR(17, serial_number_str, type17.serial); + SMBIOS_TABLE_SET_STR(17, asset_tag_number_str, type17.asset); + SMBIOS_TABLE_SET_STR(17, part_number_str, type17.part); + t->attributes = 0; /* Unknown */ + t->configured_clock_speed = 0; /* Unknown */ + t->minimum_voltage = 0; /* Unknown */ + t->maximum_voltage = 0; /* Unknown */ + t->configured_voltage = 0; /* Unknown */ + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_19_table(unsigned instance, + ram_addr_t start, ram_addr_t size) +{ + ram_addr_t end, start_kb, end_kb; + + SMBIOS_BUILD_TABLE_PRE(19, 0x1300 + instance, true); /* required */ + + end = start + size - 1; + assert(end > start); + start_kb = start / ONE_KB; + end_kb = end / ONE_KB; + if (start_kb < UINT32_MAX && end_kb < UINT32_MAX) { + t->starting_address = start_kb; + t->ending_address = end_kb; + t->extended_starting_address = t->extended_ending_address = 0; + } else { + t->starting_address = t->ending_address = UINT32_MAX; + t->extended_starting_address = start; + t->extended_ending_address = end; + } + t->memory_array_handle = 0x1000; /* Type 16 (Phys. Mem. Array) */ + t->partition_width = 1; /* One device per row */ + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_32_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(32, 0x2000, true); /* required */ + + memset(t->reserved, 0, 6); + t->boot_status = 0; /* No errors detected */ + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_127_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(127, 0x7F00, true); /* required */ + SMBIOS_BUILD_TABLE_POST; +} + +void smbios_set_cpuid(uint32_t version, uint32_t features) +{ + smbios_cpuid_version = version; + smbios_cpuid_features = features; +} + #define SMBIOS_SET_DEFAULT(field, value) \ if (!field) { \ field = value; \ }
+#define G_FREE_UNLESS_NULL(ptr) \ + if (ptr != NULL) { \ + g_free(ptr); \ + } + void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version) + const char *version, bool legacy_mode) { + smbios_have_defaults = true; + smbios_legacy = legacy_mode; + + /* drop unwanted version of command-line file blob(s) */ + if (smbios_legacy) { + G_FREE_UNLESS_NULL(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_UNLESS_NULL(smbios_entries); + } + SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer); SMBIOS_SET_DEFAULT(type1.product, product); SMBIOS_SET_DEFAULT(type1.version, version); + SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type2.product, product); + SMBIOS_SET_DEFAULT(type2.version, version); + SMBIOS_SET_DEFAULT(type3.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type3.version, version); + SMBIOS_SET_DEFAULT(type4.sock_pfx, "CPU"); + SMBIOS_SET_DEFAULT(type4.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type4.version, version); + SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); + SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer); +} + +static void smbios_entry_point_setup(void) +{ + memcpy(ep.anchor_string, "_SM_", 4); + memcpy(ep.intermediate_anchor_string, "_DMI_", 5); + ep.length = sizeof(struct smbios_entry_point); + ep.entry_point_revision = 0; /* formatted_area reserved, per spec v2.1+ */ + memset(ep.formatted_area, 0, 5); + + /* compliant with smbios spec v2.8 */ + ep.smbios_major_version = 2; + ep.smbios_minor_version = 8; + ep.smbios_bcd_revision = 0x28; + + /* set during table construction, but BIOS may override: */ + ep.structure_table_length = smbios_tables_len; + ep.max_structure_size = smbios_table_max; + ep.number_of_structures = smbios_table_cnt; + + /* BIOS must recalculate: */ + ep.checksum = 0; + ep.intermediate_checksum = 0; + ep.structure_table_address = 0; /* where BIOS has copied smbios_tables */ +} + +void smbios_get_tables(uint8_t **tables, size_t *tables_len, + uint8_t **anchor, size_t *anchor_len) +{ + unsigned i, dimm_cnt, instance; + + 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(); + smbios_build_type_2_table(); + smbios_build_type_3_table(); + + smbios_smp_sockets = smp_cpus / (smp_cores * smp_threads); + assert(smbios_smp_sockets >= 1); + + for (i = 0; i < smbios_smp_sockets; i++) { + smbios_build_type_4_table(i); + } + +#define MAX_DIMM_SZ (16 * ONE_GB) +#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ : ram_size % MAX_DIMM_SZ) + + dimm_cnt = QEMU_ALIGN_UP(ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; + + 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, instance = 0; i < e820_get_num_entries(); i++) { + ram_addr_t address, length; + if (e820_get_entry(i, E820_RAM, &address, &length)) { + smbios_build_type_19_table(instance++, address, length); + } + } + + smbios_build_type_32_table(); + smbios_build_type_127_table(); + + smbios_validate_table(); + smbios_entry_point_setup(); + smbios_immutable = true; + } + + /* return tables blob and entry point (anchor), and their sizes */ + *tables = smbios_tables; + *tables_len = smbios_tables_len; + *anchor = (uint8_t *)&ep; + *anchor_len = sizeof(struct smbios_entry_point); }
static void save_opt(const char **dest, QemuOpts *opts, const char *name) @@ -276,11 +876,12 @@ void smbios_entry_add(QemuOpts *opts) const char *val;
assert(!smbios_immutable); + val = qemu_opt_get(opts, "file"); if (val) { struct smbios_structure_header *header; - struct smbios_table *table; int size; + struct smbios_table *table; /* legacy mode only */
qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err); if (local_err) { @@ -294,24 +895,20 @@ void smbios_entry_add(QemuOpts *opts) exit(1); }
- 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(*table) + size); - table = (struct smbios_table *)(smbios_entries + smbios_entries_len); - table->header.type = SMBIOS_TABLE_ENTRY; - table->header.length = cpu_to_le16(sizeof(*table) + size); + /* + * NOTE: standard double '\0' terminator expected, per smbios spec. + * (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);
- if (load_image(val, table->data) != size) { + if (load_image(val, (uint8_t *)header) != size) { error_report("Failed to load SMBIOS file %s", val); exit(1); }
- header = (struct smbios_structure_header *)(table->data); - if (test_bit(header->type, have_fields_bitmap)) { error_report("can't load type %d struct, fields already specified!", header->type); @@ -323,9 +920,35 @@ void smbios_entry_add(QemuOpts *opts) smbios_type4_count++; }
+ smbios_tables_len += size; + if (size > smbios_table_max) { + smbios_table_max = size; + } + smbios_table_cnt++; + + /* add a copy of the newly loaded blob to legacy smbios_entries */ + /* NOTE: This code runs before smbios_set_defaults(), so we don't + * yet know which mode (legacy vs. aggregate-table) will be + * required. We therefore add the binary blob to both legacy + * (smbios_entries) and aggregate (smbios_tables) tables, and + * delete the one we don't need from smbios_set_defaults(), + * once we know which machine version has been requested. + */ + 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 + + size + sizeof(*table)); + table = (struct smbios_table *)(smbios_entries + smbios_entries_len); + table->header.type = SMBIOS_TABLE_ENTRY; + table->header.length = cpu_to_le16(sizeof(*table) + size); + memcpy(table->data, header, size); smbios_entries_len += sizeof(*table) + size; (*(uint16_t *)smbios_entries) = cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); + /* end: add a copy of the newly loaded blob to legacy smbios_entries */ + return; }
@@ -386,6 +1009,57 @@ void smbios_entry_add(QemuOpts *opts) qemu_uuid_set = true; } return; + case 2: + qemu_opts_validate(opts, qemu_smbios_type2_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type2.manufacturer, opts, "manufacturer"); + save_opt(&type2.product, opts, "product"); + save_opt(&type2.version, opts, "version"); + save_opt(&type2.serial, opts, "serial"); + save_opt(&type2.asset, opts, "asset"); + save_opt(&type2.location, opts, "location"); + return; + case 3: + qemu_opts_validate(opts, qemu_smbios_type3_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type3.manufacturer, opts, "manufacturer"); + save_opt(&type3.version, opts, "version"); + save_opt(&type3.serial, opts, "serial"); + save_opt(&type3.asset, opts, "asset"); + save_opt(&type3.sku, opts, "sku"); + return; + case 4: + qemu_opts_validate(opts, qemu_smbios_type4_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type4.sock_pfx, opts, "sock_pfx"); + save_opt(&type4.manufacturer, opts, "manufacturer"); + save_opt(&type4.version, opts, "version"); + save_opt(&type4.serial, opts, "serial"); + save_opt(&type4.asset, opts, "asset"); + save_opt(&type4.part, opts, "part"); + return; + case 17: + qemu_opts_validate(opts, qemu_smbios_type17_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type17.loc_pfx, opts, "loc_pfx"); + save_opt(&type17.bank, opts, "bank"); + save_opt(&type17.manufacturer, opts, "manufacturer"); + save_opt(&type17.serial, opts, "serial"); + save_opt(&type17.asset, opts, "asset"); + save_opt(&type17.part, opts, "part"); + return; default: error_report("Don't know how to build fields for SMBIOS type %ld", type); diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 3a9361d..6d854b7 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -18,9 +18,12 @@ #define SMBIOS_MAX_TYPE 127
void smbios_entry_add(QemuOpts *opts); +void smbios_set_cpuid(uint32_t version, uint32_t features); void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version); + const char *version, bool legacy_mode); uint8_t *smbios_get_table_legacy(size_t *length); +void smbios_get_tables(uint8_t **tables, size_t *tables_len, + uint8_t **anchor, size_t *anchor_len);
/* * SMBIOS spec defined tables
Hi,
Thanks again for any feedback and comments (or for just applying the patch) !
Series looks good to me.
/me wonders though how you've tested the final revision and the compat stuff. Patch #6 doesn't actually add a new machine type where the non-legacy mode is active ...
Incremental patch attached.
I'll go wait for a few days for additional feedback, then go squash the fix into patch #6 and send out a pull request.
cheers, Gerd