[SeaBIOS] [QEMU v5 PATCH 18/18] SMBIOS: Generate complete smbios tables, including entry point

Gabriel L. Somlo gsomlo at gmail.com
Fri Apr 11 18:11:58 CEST 2014


Build a full 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<to-be-filled-out-once-applied>XXXXX. An earlier
version will work, but will not be able 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>
---

Before applying this patch, QEMU should switch to a version of SeaBIOS
which includes this (or a similar) commit:

      http://www.seabios.org/pipermail/seabios/2014-April/007823.html

While older SeaBIOS versions will still work, any command-line "-smbios"
options would get ignored.

Thanks,
   Gabriel

 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 ff353cf..8d54489 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)
@@ -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,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 2f0755f..20f6b7c 100644
--- a/hw/i386/smbios.c
+++ b/hw/i386/smbios.c
@@ -23,24 +23,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;
@@ -318,9 +306,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 ? */                        \
@@ -328,24 +315,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;                                    \
@@ -359,11 +335,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;                                                 \
         }                                                                 \
@@ -371,20 +345,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)
@@ -666,7 +643,32 @@ void smbios_set_defaults(const char *manufacturer,
     SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer);
 }
 
-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, instance;
 
@@ -705,10 +707,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)
@@ -729,7 +736,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);
@@ -744,24 +750,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);
@@ -773,9 +771,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 4525c9b..096d12b 100644
--- a/include/hw/i386/smbios.h
+++ b/include/hw/i386/smbios.h
@@ -21,12 +21,33 @@ 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);
-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