[SeaBIOS] [QEMU v6 PATCH 11/17] SMBIOS: Generate aggregate smbios tables, including entry point
Gabriel L. Somlo
gsomlo at gmail.com
Mon Apr 14 22:55:05 CEST 2014
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 at 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;
--
1.9.0
More information about the SeaBIOS
mailing list