Build a complete set of smbios tables as a monolithic blob; Also, build an entry point structure, and insert both the set of tables and the entry point into distinct fw_cfg files.
This patch expects a SeaBIOS version equal or later than commit XXXX<insert-seabios-commit-no-here>XXXXX. An earlier version will work, but without the ability to retrieve any smbios data passed from QEMU, effectively resulting in any command-line smbios options being ignored.
Signed-off-by: Gabriel Somlo somlo@cmu.edu --- hw/i386/pc.c | 17 +++--- hw/i386/smbios.c | 135 ++++++++++++++++++++++++----------------------- include/hw/i386/smbios.h | 23 +++++++- 3 files changed, 100 insertions(+), 75 deletions(-)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 6e3962e..b3c7cc2 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -73,7 +73,7 @@ #define ACPI_DATA_SIZE 0x10000 #define BIOS_CFG_IOPORT 0x510 #define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) -#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) +#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) /* deprecated */ #define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) #define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3) #define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4) @@ -627,8 +627,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); @@ -655,10 +655,13 @@ 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); - if (smbios_table) - fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, - smbios_table, smbios_len); + smbios_get_tables(&smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len); + 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, diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index b1f1d46..12cd06a 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -21,24 +21,12 @@ #include "hw/i386/smbios.h" #include "hw/loader.h"
-/* - * Structures shared with the BIOS - */ -struct smbios_header { - uint16_t length; - uint8_t type; -} 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 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; @@ -312,9 +300,8 @@ static bool smbios_skip_table(uint8_t type, bool required_table) }
#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \ - struct smbios_table *w; \ struct smbios_type_##tbl_type *t; \ - size_t w_off, t_off; /* wrapper, table offsets into smbios_entries */ \ + size_t t_off; /* table offset into smbios_entries */ \ int str_index = 0; \ do { \ /* should we skip building this table ? */ \ @@ -322,24 +309,13 @@ static bool smbios_skip_table(uint8_t type, bool required_table) return; \ } \ \ - /* initialize fw_cfg smbios element count */ \ - if (!smbios_entries) { \ - smbios_entries_len = sizeof(uint16_t); \ - smbios_entries = g_malloc0(smbios_entries_len); \ - } \ - \ - /* use offsets of wrapper w and table t within smbios_entries */ \ - /* (pointers must be updated after each realloc) */ \ - w_off = smbios_entries_len; \ - t_off = w_off + sizeof(*w); \ - smbios_entries_len = t_off + sizeof(*t); \ + /* use offset of table t within smbios_entries */ \ + /* (pointer must be updated after each realloc) */ \ + t_off = smbios_entries_len; \ + smbios_entries_len += sizeof(*t); \ smbios_entries = g_realloc(smbios_entries, smbios_entries_len); \ - w = (struct smbios_table *)(smbios_entries + w_off); \ t = (struct smbios_type_##tbl_type *)(smbios_entries + t_off); \ \ - w->header.type = SMBIOS_TABLE_ENTRY; \ - w->header.length = sizeof(*w) + sizeof(*t); \ - \ t->header.type = tbl_type; \ t->header.length = sizeof(*t); \ t->header.handle = tbl_handle; \ @@ -353,11 +329,9 @@ static bool smbios_skip_table(uint8_t type, bool required_table) smbios_entries_len + len); \ memcpy(smbios_entries + smbios_entries_len, value, len); \ smbios_entries_len += len; \ - /* update pointer(s) post-realloc */ \ - w = (struct smbios_table *)(smbios_entries + w_off); \ + /* update pointer post-realloc */ \ t = (struct smbios_type_##tbl_type *)(smbios_entries + t_off);\ t->field = ++str_index; \ - w->header.length += len; \ } else { \ t->field = 0; \ } \ @@ -365,20 +339,23 @@ static bool smbios_skip_table(uint8_t type, bool required_table)
#define SMBIOS_BUILD_TABLE_POST \ do { \ - /* add empty string if no strings defined in table */ \ - /* (NOTE: terminating \0 currently handled by fw_cfg/seabios) */ \ - if (str_index == 0) { \ - smbios_entries = g_realloc(smbios_entries, \ - smbios_entries_len + 1); \ - *(smbios_entries + smbios_entries_len) = 0; \ - smbios_entries_len += 1; \ - /* update pointer(s) post-realloc */ \ - w = (struct smbios_table *)(smbios_entries + w_off); \ - w->header.length += 1; \ + size_t term_cnt, t_size; \ + \ + /* add '\0' terminator (add two if no strings defined) */ \ + term_cnt = (str_index == 0) ? 2 : 1; \ + smbios_entries = g_realloc(smbios_entries, \ + smbios_entries_len + term_cnt); \ + memset(smbios_entries + smbios_entries_len, 0, term_cnt); \ + smbios_entries_len += term_cnt; \ + \ + /* update smbios max. element size */ \ + t_size = smbios_entries_len - t_off; \ + if (t_size > smbios_table_max) { \ + smbios_table_max = t_size; \ } \ \ - /* update fw_cfg smbios element count */ \ - *(uint16_t *)smbios_entries += 1; \ + /* update smbios element count */ \ + smbios_table_cnt++; \ } while (0)
static void smbios_build_type_0_table(void) @@ -675,7 +652,32 @@ void smbios_set_defaults(const char *manufacturer, SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); }
-uint8_t *smbios_get_table(size_t *length) +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_entries_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_entries */ +} + +void smbios_get_tables(uint8_t **tables, size_t *tables_len, + uint8_t **anchor, size_t *anchor_len) { unsigned i, dimm_cnt;
@@ -774,10 +776,15 @@ uint8_t *smbios_get_table(size_t *length) smbios_build_type_127_table();
smbios_validate_table(); + smbios_entry_point_setup(); smbios_immutable = true; } - *length = smbios_entries_len; - return smbios_entries; + + /* return tables blob and entry point (anchor), and their sizes */ + *tables = smbios_entries; + *tables_len = smbios_entries_len; + *anchor = (uint8_t *)&ep; + *anchor_len = sizeof(struct smbios_entry_point); }
static void save_opt(const char **dest, QemuOpts *opts, const char *name) @@ -798,7 +805,6 @@ void smbios_entry_add(QemuOpts *opts) val = qemu_opt_get(opts, "file"); if (val) { struct smbios_structure_header *header; - struct smbios_table *table; int size;
qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err); @@ -813,24 +819,16 @@ 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 + size);
- 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); + header = (struct smbios_structure_header *) + (smbios_entries + smbios_entries_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 add binary type %d table! " "(fields already specified)", header->type); @@ -842,9 +840,12 @@ void smbios_entry_add(QemuOpts *opts) smbios_type4_count++; }
- smbios_entries_len += sizeof(*table) + size; - (*(uint16_t *)smbios_entries) = - cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); + smbios_entries_len += size; + if (size > smbios_table_max) { + smbios_table_max = size; + } + smbios_table_cnt++; + return; }
diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 2a0d384..8a4e3b1 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -23,12 +23,33 @@ void smbios_set_defaults(const char *manufacturer, const char *product, const char *version, ram_addr_t below_4g_mem_size, ram_addr_t above_4g_mem_size); -uint8_t *smbios_get_table(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 */
+/* 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;