Build full smbios type 4 (processor information) tables, and make them available to the bios via fw_cfg.
Signed-off-by: Gabriel Somlo <so...@cmu.edu> --- hw/i386/pc.c | 7 +++++ hw/i386/smbios.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index e715a33..6d85dca 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -979,6 +979,9 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) pc_new_cpu(current_cpu_model, apic_id, icc_bridge, errp); } +/* smbios type 4 (processor information) needs these */ +extern uint32_t smbios_cpuid_version, smbios_cpuid_features; + void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) { int i; @@ -1011,6 +1014,10 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) sysbus_mmio_map_overlap(SYS_BUS_DEVICE(icc_bridge), 0, APIC_DEFAULT_ADDRESS, 0x1000); } + + /* tell smbios about cpuid version and features */ + smbios_cpuid_version = cpu->env.cpuid_version; + smbios_cpuid_features = cpu->env.features[FEAT_1_EDX]; } /* pci-info ROM file. Little endian format */ diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index eaf5bcc..50fc100 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -37,6 +37,9 @@ struct smbios_table { #define SMBIOS_FIELD_ENTRY 0 #define SMBIOS_TABLE_ENTRY 1 +/* cpuid version and features, set from pc_cpus_init() */ +uint32_t smbios_cpuid_version, smbios_cpuid_features; + static uint8_t *smbios_entries; static size_t smbios_entries_len; static int smbios_type4_count = 0; @@ -65,6 +68,10 @@ static struct { const char *manufacturer, *version, *serial, *asset; } type3; +static struct { + const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; +} type4; + static QemuOptsList qemu_smbios_opts = { .name = "smbios", .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), @@ -206,6 +213,39 @@ static const QemuOptDesc qemu_smbios_type3_opts[] = { { /* end of list */ } }; +static const QemuOptDesc qemu_smbios_type4_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "sock_pfx", + .type = QEMU_OPT_STRING, + .help = "socket designation string prefix", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer 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 = "part", + .type = QEMU_OPT_STRING, + .help = "part number", + }, + { /* end of list */ } +}; + static void smbios_register_config(void) { qemu_add_opts(&qemu_smbios_opts); @@ -400,6 +440,37 @@ static void smbios_build_type_3_table(void) SMBIOS_BUILD_TABLE_POST; } +static void smbios_build_type_4_table(unsigned cpu_num) +{ + char sock_str[128]; + + SMBIOS_BUILD_TABLE_PRE(4, 0x400 + cpu_num, true); /* required */ + + snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, cpu_num); + SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); + t->processor_type = 0x03; /* CPU */ + t->processor_family = 0x01; /* Other */ + SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); + t->processor_id[0] = smbios_cpuid_version; + t->processor_id[1] = smbios_cpuid_features; + SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); + t->voltage = 0; + t->external_clock = 0; + t->max_speed = 0; + t->current_speed = 0; + t->status = 0x41; /* Socket populated, CPU enabled */ + t->processor_upgrade = 0x01; /* Other */ + t->l1_cache_handle = 0xFFFF; /* N/A */ + t->l2_cache_handle = 0xFFFF; /* N/A */ + t->l3_cache_handle = 0xFFFF; /* N/A */ + SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial); + SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); + SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); + + SMBIOS_BUILD_TABLE_POST; + smbios_type4_count++; +} + #define SMBIOS_SET_DEFAULT(field, value) \ if (!field) { \ field = value; \ @@ -420,15 +491,23 @@ void smbios_set_defaults(const char *manufacturer, SMBIOS_SET_DEFAULT(type2.version, version); SMBIOS_SET_DEFAULT(type3.manufacturer, manufacturer); SMBIOS_SET_DEFAULT(type3.version, version); + SMBIOS_SET_DEFAULT(type4.sock_pfx, "CPU"); + SMBIOS_SET_DEFAULT(type4.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type4.version, version); } uint8_t *smbios_get_table(size_t *length) { + unsigned i; + if (!smbios_immutable) { smbios_build_type_0_table(); smbios_build_type_1_table(); smbios_build_type_2_table(); smbios_build_type_3_table(); + for (i = 0; i < smp_cpus; i++) { + smbios_build_type_4_table(i); + } smbios_validate_table(); smbios_immutable = true; } @@ -586,6 +665,19 @@ void smbios_entry_add(QemuOpts *opts) save_opt(&type3.serial, opts, "serial"); save_opt(&type3.asset, opts, "asset"); return; + case 4: + qemu_opts_validate(opts, qemu_smbios_type4_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type4.sock_pfx, opts, "sock_pfx"); + save_opt(&type4.manufacturer, opts, "manufacturer"); + save_opt(&type4.version, opts, "version"); + save_opt(&type4.serial, opts, "serial"); + save_opt(&type4.asset, opts, "asset"); + save_opt(&type4.part, opts, "part"); + return; default: error_report("Don't know how to build fields for SMBIOS type %ld", type); -- 1.8.1.4