Build full smbios type 16 (physical memory array) and type 17 (memory device) tables, and make them available to the bios via fw_cfg. Type 17 tables will comply with smbios v2.3, which helps prevent the OS X GUI from crashing when "about this mac" is selected.
Signed-off-by: Gabriel Somlo <so...@cmu.edu> --- The arithmetic and bit ops are cut'n'paste from SeaBIOS, and I tested them and verified they work exactly the same. I'll send a follow-up patch later after I make the operations a bit more self-explanatory, and/or after I add some explanatory comments. But I wanted this posted before the 2.0 code freeze... :) Thanks, Gabriel hw/i386/smbios.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 50fc100..989d12c 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -72,6 +72,10 @@ 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), @@ -246,6 +250,39 @@ static const QemuOptDesc qemu_smbios_type4_opts[] = { { /* 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); @@ -471,6 +508,51 @@ static void smbios_build_type_4_table(unsigned cpu_num) smbios_type4_count++; } +static void smbios_build_type_16_table(unsigned memdev_count) +{ + 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) */ + /* if ram_size < 2T, use value in Kilobytes; 0x80000000 == 2T and over; + * TODO: support smbios v2.7 extended capacity, or multiple arrays. */ + t->maximum_capacity = ram_size < 2ULL << 40 ? ram_size >> 10 : 0x80000000; + t->memory_error_information_handle = 0xFFFE; /* Not provided */ + t->number_of_memory_devices = memdev_count; + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_17_table(unsigned memdev_num, + unsigned memdev_size_mb) +{ + char loc_str[128]; + + SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + memdev_num, true); /* required */ + + t->physical_memory_array_handle = 0x1000; /* Type 16 (Phys. Mem. Array) */ + t->memory_error_information_handle = 0xFFFE; /* Not provided */ + t->total_width = 64; /* hardcoded in SeaBIOS */ + t->data_width = 64; /* hardcoded in SeaBIOS */ + assert(memdev_size_mb <= 0x7fff); + t->size = memdev_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, memdev_num); + 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 = 0; /* hardcoded in SeaBIOS */ + 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); + + SMBIOS_BUILD_TABLE_POST; +} + #define SMBIOS_SET_DEFAULT(field, value) \ if (!field) { \ field = value; \ @@ -494,11 +576,13 @@ void smbios_set_defaults(const char *manufacturer, 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); } uint8_t *smbios_get_table(size_t *length) { - unsigned i; + unsigned i, ram_size_mb, memdev_count; if (!smbios_immutable) { smbios_build_type_0_table(); @@ -508,6 +592,15 @@ uint8_t *smbios_get_table(size_t *length) for (i = 0; i < smp_cpus; i++) { smbios_build_type_4_table(i); } + ram_size_mb = ram_size >> 20; + memdev_count = (ram_size_mb + 0x3fff) >> 14; /* per SeaBIOS */ + smbios_build_type_16_table(memdev_count); + for (i = 0; i < memdev_count; i++) { + /* arithmetic and magic constants per SeaBIOS; TODO: clarify */ + uint32_t memdev_size_mb = (i == memdev_count - 1) ? + ((ram_size_mb - 1) & 0x3fff) + 1 : 16384; + smbios_build_type_17_table(i, memdev_size_mb); + } smbios_validate_table(); smbios_immutable = true; } @@ -678,6 +771,19 @@ void smbios_entry_add(QemuOpts *opts) 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); -- 1.8.1.4