This patch adds a set of macros which build full smbios tables of a given type, including the logic to decide whether a given table type should be built or not.
To illustrate this new functionality, we introduce and optionally build a table of type 2 (base board), which is required by some versions of OS X (10.7 and 10.8).
Signed-off-by: Gabriel Somlo somlo@cmu.edu --- hw/i386/smbios.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++ include/hw/i386/smbios.h | 18 +++++- 2 files changed, 175 insertions(+), 1 deletion(-)
diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 6889332..06f572d 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -48,6 +48,7 @@ static uint8_t *smbios_entries; static size_t smbios_entries_len; static int smbios_type4_count = 0; static bool smbios_immutable; +static bool smbios_have_defaults;
static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); @@ -63,6 +64,10 @@ static struct { /* uuid is in qemu_uuid[] */ } type1;
+static struct { + const char *manufacturer, *product, *version, *serial, *asset, *location; +} type2; + static QemuOptsList qemu_smbios_opts = { .name = "smbios", .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), @@ -146,6 +151,39 @@ 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 void smbios_register_config(void) { qemu_add_opts(&qemu_smbios_opts); @@ -161,6 +199,90 @@ static void smbios_validate_table(void) } }
+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_table *w; \ + struct smbios_type_##tbl_type *t; \ + size_t w_off, t_off; /* wrapper, table offsets into smbios_entries */ \ + int str_index = 0; \ + do { \ + /* should we skip building this table ? */ \ + if (smbios_skip_table(tbl_type, tbl_required)) { \ + 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); \ + 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; \ + } while (0) + +#define SMBIOS_TABLE_SET_STR(tbl_type, field, value) \ + do { \ + int len = (value != NULL) ? strlen(value) + 1 : 0; \ + if (len > 1) { \ + smbios_entries = g_realloc(smbios_entries, \ + 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); \ + t = (struct smbios_type_##tbl_type *)(smbios_entries + t_off);\ + t->field = ++str_index; \ + w->header.length += len; \ + } else { \ + t->field = 0; \ + } \ + } while (0) + +#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; \ + } \ + \ + /* update fw_cfg smbios element count */ \ + *(uint16_t *)smbios_entries += 1; \ + } while (0) + static void smbios_add_field(int type, int offset, const void *data, size_t len) { struct smbios_field *field; @@ -230,6 +352,24 @@ static void smbios_build_type_1_fields(void) } }
+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; +} + #define SMBIOS_SET_DEFAULT(field, value) \ if (!field) { \ field = value; \ @@ -238,14 +378,19 @@ static void smbios_build_type_1_fields(void) void smbios_set_defaults(const char *manufacturer, const char *product, const char *version) { + smbios_have_defaults = true; 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); }
uint8_t *smbios_get_table(size_t *length) { if (!smbios_immutable) { + smbios_build_type_2_table(); smbios_build_type_0_fields(); smbios_build_type_1_fields(); smbios_validate_table(); @@ -381,6 +526,19 @@ 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; 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 3425d40..2642e1a 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -62,6 +62,22 @@ struct smbios_type_1 { uint8_t family_str; } QEMU_PACKED;
+/* 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.3) */ struct smbios_type_3 { struct smbios_structure_header header; @@ -78,7 +94,7 @@ struct smbios_type_3 { uint8_t height; uint8_t number_of_power_cords; uint8_t contained_element_count; - // contained elements follow + /* contained elements follow */ } QEMU_PACKED;
/* SMBIOS type 4 - Processor Information (v2.0) */