From: "Gabriel L. Somlo" <so...@cmu.edu> Build full smbios type 16 (physical memory array) and type 17 (memory device) tables, and make them available to the bios via fw_cfg.
Signed-off-by: Gabriel Somlo <so...@cmu.edu> --- hw/i386/smbios.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index fc8b3ef..58bef22 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -70,6 +70,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), @@ -244,6 +248,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); @@ -466,6 +503,47 @@ static void smbios_build_type_4_table(unsigned instance) smbios_type4_count++; } +static void smbios_build_type_16_table(unsigned memdev_count) +{ + unsigned ram_size_kb = ram_size >> 10; + + 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_kb < 0x80000000) ? ram_size_kb : 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 instance, unsigned size_mb) +{ + char loc_str[128]; + + SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */ + + t->physical_memory_array_handle = 0x1000; /* Type 16 (Phys. Mem. Array) */ + t->memory_error_information_handle = 0; /* SeaBIOS, should be 0xFFFE(N/A) */ + t->total_width = 64; /* hardcoded in SeaBIOS */ + t->data_width = 64; /* hardcoded in SeaBIOS */ + assert(size_mb <= 0x7FFF); + t->size = 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, instance); + 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 */ + + SMBIOS_BUILD_TABLE_POST; +} + #define SMBIOS_SET_DEFAULT(field, value) \ if (!field) { \ field = value; \ @@ -500,11 +578,12 @@ void smbios_set_defaults(const char *manufacturer, /* not set in SeaBIOS SMBIOS_SET_DEFAULT(type4.version, version); */ + SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); } 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(); @@ -515,6 +594,15 @@ uint8_t *smbios_get_table(size_t *length) /* count CPUs starting with 1, to minimize diff vs. SeaBIOS */ smbios_build_type_4_table(i + 1); } + ram_size_mb = ram_size >> 20; + /* up to 16GB (0x4000MB) per memory device: */ + memdev_count = (ram_size_mb + 0x3FFF) >> 14; + smbios_build_type_16_table(memdev_count); + for (i = 0; i < memdev_count; i++) { + uint32_t size_mb = (i == memdev_count - 1) ? + ((ram_size_mb - 1) & 0x3FFF) + 1 : 0x4000; + smbios_build_type_17_table(i, size_mb); + } smbios_validate_table(); smbios_immutable = true; } @@ -685,6 +773,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