160d8f328SWei Huang /* 260d8f328SWei Huang * SMBIOS Support 360d8f328SWei Huang * 460d8f328SWei Huang * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. 560d8f328SWei Huang * Copyright (C) 2013 Red Hat, Inc. 660d8f328SWei Huang * 760d8f328SWei Huang * Authors: 860d8f328SWei Huang * Alex Williamson <alex.williamson@hp.com> 960d8f328SWei Huang * Markus Armbruster <armbru@redhat.com> 1060d8f328SWei Huang * 1160d8f328SWei Huang * This work is licensed under the terms of the GNU GPL, version 2. See 1260d8f328SWei Huang * the COPYING file in the top-level directory. 1360d8f328SWei Huang * 1460d8f328SWei Huang * Contributions after 2012-01-13 are licensed under the terms of the 1560d8f328SWei Huang * GNU GPL, version 2 or (at your option) any later version. 1660d8f328SWei Huang */ 1760d8f328SWei Huang 180430891cSPeter Maydell #include "qemu/osdep.h" 19968dfd05SPhilippe Mathieu-Daudé #include "qemu/units.h" 20da34e65cSMarkus Armbruster #include "qapi/error.h" 2160d8f328SWei Huang #include "qemu/config-file.h" 2260d8f328SWei Huang #include "qemu/error-report.h" 230b8fa32fSMarkus Armbruster #include "qemu/module.h" 24922a01a0SMarkus Armbruster #include "qemu/option.h" 2560d8f328SWei Huang #include "sysemu/sysemu.h" 26cea25275SFam Zheng #include "qemu/uuid.h" 27a2eb5c0cSPhilippe Mathieu-Daudé #include "hw/firmware/smbios.h" 2860d8f328SWei Huang #include "hw/loader.h" 29a0628599SLike Xu #include "hw/boards.h" 3005dfb447SVincent Bernat #include "hw/pci/pci_bus.h" 310517cc98SCorey Minyard #include "smbios_build.h" 3260d8f328SWei Huang 3360d8f328SWei Huang /* legacy structures and constants for <= 2.0 machines */ 3460d8f328SWei Huang struct smbios_header { 3560d8f328SWei Huang uint16_t length; 3660d8f328SWei Huang uint8_t type; 3760d8f328SWei Huang } QEMU_PACKED; 3860d8f328SWei Huang 3960d8f328SWei Huang struct smbios_field { 4060d8f328SWei Huang struct smbios_header header; 4160d8f328SWei Huang uint8_t type; 4260d8f328SWei Huang uint16_t offset; 4360d8f328SWei Huang uint8_t data[]; 4460d8f328SWei Huang } QEMU_PACKED; 4560d8f328SWei Huang 4660d8f328SWei Huang struct smbios_table { 4760d8f328SWei Huang struct smbios_header header; 4860d8f328SWei Huang uint8_t data[]; 4960d8f328SWei Huang } QEMU_PACKED; 5060d8f328SWei Huang 5160d8f328SWei Huang #define SMBIOS_FIELD_ENTRY 0 5260d8f328SWei Huang #define SMBIOS_TABLE_ENTRY 1 5360d8f328SWei Huang 5460d8f328SWei Huang static uint8_t *smbios_entries; 5560d8f328SWei Huang static size_t smbios_entries_len; 5660d8f328SWei Huang static bool smbios_legacy = true; 5760d8f328SWei Huang static bool smbios_uuid_encoded = true; 5860d8f328SWei Huang /* end: legacy structures & constants for <= 2.0 machines */ 5960d8f328SWei Huang 6060d8f328SWei Huang 610517cc98SCorey Minyard uint8_t *smbios_tables; 620517cc98SCorey Minyard size_t smbios_tables_len; 630517cc98SCorey Minyard unsigned smbios_table_max; 640517cc98SCorey Minyard unsigned smbios_table_cnt; 6510be11d0SEduardo Habkost static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32; 6686299120SWei Huang 6786299120SWei Huang static SmbiosEntryPoint ep; 6860d8f328SWei Huang 6960d8f328SWei Huang static int smbios_type4_count = 0; 7060d8f328SWei Huang static bool smbios_immutable; 7160d8f328SWei Huang static bool smbios_have_defaults; 7260d8f328SWei Huang static uint32_t smbios_cpuid_version, smbios_cpuid_features, smbios_smp_sockets; 7360d8f328SWei Huang 7460d8f328SWei Huang static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); 7560d8f328SWei Huang static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); 7660d8f328SWei Huang 7760d8f328SWei Huang static struct { 7860d8f328SWei Huang const char *vendor, *version, *date; 7960d8f328SWei Huang bool have_major_minor, uefi; 8060d8f328SWei Huang uint8_t major, minor; 8160d8f328SWei Huang } type0; 8260d8f328SWei Huang 8360d8f328SWei Huang static struct { 8460d8f328SWei Huang const char *manufacturer, *product, *version, *serial, *sku, *family; 859c5ce8dbSFam Zheng /* uuid is in qemu_uuid */ 8660d8f328SWei Huang } type1; 8760d8f328SWei Huang 8860d8f328SWei Huang static struct { 8960d8f328SWei Huang const char *manufacturer, *product, *version, *serial, *asset, *location; 9060d8f328SWei Huang } type2; 9160d8f328SWei Huang 9260d8f328SWei Huang static struct { 9360d8f328SWei Huang const char *manufacturer, *version, *serial, *asset, *sku; 9460d8f328SWei Huang } type3; 9560d8f328SWei Huang 96c906e039SYing Fang /* 97c906e039SYing Fang * SVVP requires max_speed and current_speed to be set and not being 98c906e039SYing Fang * 0 which counts as unknown (SMBIOS 3.1.0/Table 21). Set the 99c906e039SYing Fang * default value to 2000MHz as we did before. 100c906e039SYing Fang */ 101c906e039SYing Fang #define DEFAULT_CPU_SPEED 2000 102c906e039SYing Fang 10360d8f328SWei Huang static struct { 10460d8f328SWei Huang const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; 105c906e039SYing Fang uint64_t max_speed; 106c906e039SYing Fang uint64_t current_speed; 107cb5fb04fSPatrick Venture uint64_t processor_id; 108c906e039SYing Fang } type4 = { 109c906e039SYing Fang .max_speed = DEFAULT_CPU_SPEED, 110cb5fb04fSPatrick Venture .current_speed = DEFAULT_CPU_SPEED, 111cb5fb04fSPatrick Venture .processor_id = 0, 112c906e039SYing Fang }; 11360d8f328SWei Huang 114*fd8caa25SHal Martin struct type8_instance { 115*fd8caa25SHal Martin const char *internal_reference, *external_reference; 116*fd8caa25SHal Martin uint8_t connector_type, port_type; 117*fd8caa25SHal Martin QTAILQ_ENTRY(type8_instance) next; 118*fd8caa25SHal Martin }; 119*fd8caa25SHal Martin static QTAILQ_HEAD(, type8_instance) type8 = QTAILQ_HEAD_INITIALIZER(type8); 120*fd8caa25SHal Martin 12160d8f328SWei Huang static struct { 1222d6dcbf9SDaniel P. Berrange size_t nvalues; 123bb99f477SDaniel P. Berrangé char **values; 1242d6dcbf9SDaniel P. Berrange } type11; 1252d6dcbf9SDaniel P. Berrange 1262d6dcbf9SDaniel P. Berrange static struct { 12760d8f328SWei Huang const char *loc_pfx, *bank, *manufacturer, *serial, *asset, *part; 12860d8f328SWei Huang uint16_t speed; 12960d8f328SWei Huang } type17; 13060d8f328SWei Huang 13105dfb447SVincent Bernat static QEnumLookup type41_kind_lookup = { 13205dfb447SVincent Bernat .array = (const char *const[]) { 13305dfb447SVincent Bernat "other", 13405dfb447SVincent Bernat "unknown", 13505dfb447SVincent Bernat "video", 13605dfb447SVincent Bernat "scsi", 13705dfb447SVincent Bernat "ethernet", 13805dfb447SVincent Bernat "tokenring", 13905dfb447SVincent Bernat "sound", 14005dfb447SVincent Bernat "pata", 14105dfb447SVincent Bernat "sata", 14205dfb447SVincent Bernat "sas", 14305dfb447SVincent Bernat }, 14405dfb447SVincent Bernat .size = 10 14505dfb447SVincent Bernat }; 14605dfb447SVincent Bernat struct type41_instance { 14705dfb447SVincent Bernat const char *designation, *pcidev; 14805dfb447SVincent Bernat uint8_t instance, kind; 14905dfb447SVincent Bernat QTAILQ_ENTRY(type41_instance) next; 15005dfb447SVincent Bernat }; 15105dfb447SVincent Bernat static QTAILQ_HEAD(, type41_instance) type41 = QTAILQ_HEAD_INITIALIZER(type41); 15205dfb447SVincent Bernat 15360d8f328SWei Huang static QemuOptsList qemu_smbios_opts = { 15460d8f328SWei Huang .name = "smbios", 15560d8f328SWei Huang .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), 15660d8f328SWei Huang .desc = { 15760d8f328SWei Huang /* 15860d8f328SWei Huang * no elements => accept any params 15960d8f328SWei Huang * validation will happen later 16060d8f328SWei Huang */ 16160d8f328SWei Huang { /* end of list */ } 16260d8f328SWei Huang } 16360d8f328SWei Huang }; 16460d8f328SWei Huang 16560d8f328SWei Huang static const QemuOptDesc qemu_smbios_file_opts[] = { 16660d8f328SWei Huang { 16760d8f328SWei Huang .name = "file", 16860d8f328SWei Huang .type = QEMU_OPT_STRING, 16960d8f328SWei Huang .help = "binary file containing an SMBIOS element", 17060d8f328SWei Huang }, 17160d8f328SWei Huang { /* end of list */ } 17260d8f328SWei Huang }; 17360d8f328SWei Huang 17460d8f328SWei Huang static const QemuOptDesc qemu_smbios_type0_opts[] = { 17560d8f328SWei Huang { 17660d8f328SWei Huang .name = "type", 17760d8f328SWei Huang .type = QEMU_OPT_NUMBER, 17860d8f328SWei Huang .help = "SMBIOS element type", 17960d8f328SWei Huang },{ 18060d8f328SWei Huang .name = "vendor", 18160d8f328SWei Huang .type = QEMU_OPT_STRING, 18260d8f328SWei Huang .help = "vendor name", 18360d8f328SWei Huang },{ 18460d8f328SWei Huang .name = "version", 18560d8f328SWei Huang .type = QEMU_OPT_STRING, 18660d8f328SWei Huang .help = "version number", 18760d8f328SWei Huang },{ 18860d8f328SWei Huang .name = "date", 18960d8f328SWei Huang .type = QEMU_OPT_STRING, 19060d8f328SWei Huang .help = "release date", 19160d8f328SWei Huang },{ 19260d8f328SWei Huang .name = "release", 19360d8f328SWei Huang .type = QEMU_OPT_STRING, 19460d8f328SWei Huang .help = "revision number", 19560d8f328SWei Huang },{ 19660d8f328SWei Huang .name = "uefi", 19760d8f328SWei Huang .type = QEMU_OPT_BOOL, 19860d8f328SWei Huang .help = "uefi support", 19960d8f328SWei Huang }, 20060d8f328SWei Huang { /* end of list */ } 20160d8f328SWei Huang }; 20260d8f328SWei Huang 20360d8f328SWei Huang static const QemuOptDesc qemu_smbios_type1_opts[] = { 20460d8f328SWei Huang { 20560d8f328SWei Huang .name = "type", 20660d8f328SWei Huang .type = QEMU_OPT_NUMBER, 20760d8f328SWei Huang .help = "SMBIOS element type", 20860d8f328SWei Huang },{ 20960d8f328SWei Huang .name = "manufacturer", 21060d8f328SWei Huang .type = QEMU_OPT_STRING, 21160d8f328SWei Huang .help = "manufacturer name", 21260d8f328SWei Huang },{ 21360d8f328SWei Huang .name = "product", 21460d8f328SWei Huang .type = QEMU_OPT_STRING, 21560d8f328SWei Huang .help = "product name", 21660d8f328SWei Huang },{ 21760d8f328SWei Huang .name = "version", 21860d8f328SWei Huang .type = QEMU_OPT_STRING, 21960d8f328SWei Huang .help = "version number", 22060d8f328SWei Huang },{ 22160d8f328SWei Huang .name = "serial", 22260d8f328SWei Huang .type = QEMU_OPT_STRING, 22360d8f328SWei Huang .help = "serial number", 22460d8f328SWei Huang },{ 22560d8f328SWei Huang .name = "uuid", 22660d8f328SWei Huang .type = QEMU_OPT_STRING, 22760d8f328SWei Huang .help = "UUID", 22860d8f328SWei Huang },{ 22960d8f328SWei Huang .name = "sku", 23060d8f328SWei Huang .type = QEMU_OPT_STRING, 23160d8f328SWei Huang .help = "SKU number", 23260d8f328SWei Huang },{ 23360d8f328SWei Huang .name = "family", 23460d8f328SWei Huang .type = QEMU_OPT_STRING, 23560d8f328SWei Huang .help = "family name", 23660d8f328SWei Huang }, 23760d8f328SWei Huang { /* end of list */ } 23860d8f328SWei Huang }; 23960d8f328SWei Huang 24060d8f328SWei Huang static const QemuOptDesc qemu_smbios_type2_opts[] = { 24160d8f328SWei Huang { 24260d8f328SWei Huang .name = "type", 24360d8f328SWei Huang .type = QEMU_OPT_NUMBER, 24460d8f328SWei Huang .help = "SMBIOS element type", 24560d8f328SWei Huang },{ 24660d8f328SWei Huang .name = "manufacturer", 24760d8f328SWei Huang .type = QEMU_OPT_STRING, 24860d8f328SWei Huang .help = "manufacturer name", 24960d8f328SWei Huang },{ 25060d8f328SWei Huang .name = "product", 25160d8f328SWei Huang .type = QEMU_OPT_STRING, 25260d8f328SWei Huang .help = "product name", 25360d8f328SWei Huang },{ 25460d8f328SWei Huang .name = "version", 25560d8f328SWei Huang .type = QEMU_OPT_STRING, 25660d8f328SWei Huang .help = "version number", 25760d8f328SWei Huang },{ 25860d8f328SWei Huang .name = "serial", 25960d8f328SWei Huang .type = QEMU_OPT_STRING, 26060d8f328SWei Huang .help = "serial number", 26160d8f328SWei Huang },{ 26260d8f328SWei Huang .name = "asset", 26360d8f328SWei Huang .type = QEMU_OPT_STRING, 26460d8f328SWei Huang .help = "asset tag number", 26560d8f328SWei Huang },{ 26660d8f328SWei Huang .name = "location", 26760d8f328SWei Huang .type = QEMU_OPT_STRING, 26860d8f328SWei Huang .help = "location in chassis", 26960d8f328SWei Huang }, 27060d8f328SWei Huang { /* end of list */ } 27160d8f328SWei Huang }; 27260d8f328SWei Huang 27360d8f328SWei Huang static const QemuOptDesc qemu_smbios_type3_opts[] = { 27460d8f328SWei Huang { 27560d8f328SWei Huang .name = "type", 27660d8f328SWei Huang .type = QEMU_OPT_NUMBER, 27760d8f328SWei Huang .help = "SMBIOS element type", 27860d8f328SWei Huang },{ 27960d8f328SWei Huang .name = "manufacturer", 28060d8f328SWei Huang .type = QEMU_OPT_STRING, 28160d8f328SWei Huang .help = "manufacturer name", 28260d8f328SWei Huang },{ 28360d8f328SWei Huang .name = "version", 28460d8f328SWei Huang .type = QEMU_OPT_STRING, 28560d8f328SWei Huang .help = "version number", 28660d8f328SWei Huang },{ 28760d8f328SWei Huang .name = "serial", 28860d8f328SWei Huang .type = QEMU_OPT_STRING, 28960d8f328SWei Huang .help = "serial number", 29060d8f328SWei Huang },{ 29160d8f328SWei Huang .name = "asset", 29260d8f328SWei Huang .type = QEMU_OPT_STRING, 29360d8f328SWei Huang .help = "asset tag number", 29460d8f328SWei Huang },{ 29560d8f328SWei Huang .name = "sku", 29660d8f328SWei Huang .type = QEMU_OPT_STRING, 29760d8f328SWei Huang .help = "SKU number", 29860d8f328SWei Huang }, 29960d8f328SWei Huang { /* end of list */ } 30060d8f328SWei Huang }; 30160d8f328SWei Huang 30260d8f328SWei Huang static const QemuOptDesc qemu_smbios_type4_opts[] = { 30360d8f328SWei Huang { 30460d8f328SWei Huang .name = "type", 30560d8f328SWei Huang .type = QEMU_OPT_NUMBER, 30660d8f328SWei Huang .help = "SMBIOS element type", 30760d8f328SWei Huang },{ 30860d8f328SWei Huang .name = "sock_pfx", 30960d8f328SWei Huang .type = QEMU_OPT_STRING, 31060d8f328SWei Huang .help = "socket designation string prefix", 31160d8f328SWei Huang },{ 31260d8f328SWei Huang .name = "manufacturer", 31360d8f328SWei Huang .type = QEMU_OPT_STRING, 31460d8f328SWei Huang .help = "manufacturer name", 31560d8f328SWei Huang },{ 31660d8f328SWei Huang .name = "version", 31760d8f328SWei Huang .type = QEMU_OPT_STRING, 31860d8f328SWei Huang .help = "version number", 31960d8f328SWei Huang },{ 320c906e039SYing Fang .name = "max-speed", 321c906e039SYing Fang .type = QEMU_OPT_NUMBER, 322c906e039SYing Fang .help = "max speed in MHz", 323c906e039SYing Fang },{ 324c906e039SYing Fang .name = "current-speed", 325c906e039SYing Fang .type = QEMU_OPT_NUMBER, 326c906e039SYing Fang .help = "speed at system boot in MHz", 327c906e039SYing Fang },{ 32860d8f328SWei Huang .name = "serial", 32960d8f328SWei Huang .type = QEMU_OPT_STRING, 33060d8f328SWei Huang .help = "serial number", 33160d8f328SWei Huang },{ 33260d8f328SWei Huang .name = "asset", 33360d8f328SWei Huang .type = QEMU_OPT_STRING, 33460d8f328SWei Huang .help = "asset tag number", 33560d8f328SWei Huang },{ 33660d8f328SWei Huang .name = "part", 33760d8f328SWei Huang .type = QEMU_OPT_STRING, 33860d8f328SWei Huang .help = "part number", 339cb5fb04fSPatrick Venture }, { 340cb5fb04fSPatrick Venture .name = "processor-id", 341cb5fb04fSPatrick Venture .type = QEMU_OPT_NUMBER, 342cb5fb04fSPatrick Venture .help = "processor id", 34360d8f328SWei Huang }, 34460d8f328SWei Huang { /* end of list */ } 34560d8f328SWei Huang }; 34660d8f328SWei Huang 347*fd8caa25SHal Martin static const QemuOptDesc qemu_smbios_type8_opts[] = { 348*fd8caa25SHal Martin { 349*fd8caa25SHal Martin .name = "internal_reference", 350*fd8caa25SHal Martin .type = QEMU_OPT_STRING, 351*fd8caa25SHal Martin .help = "internal reference designator", 352*fd8caa25SHal Martin }, 353*fd8caa25SHal Martin { 354*fd8caa25SHal Martin .name = "external_reference", 355*fd8caa25SHal Martin .type = QEMU_OPT_STRING, 356*fd8caa25SHal Martin .help = "external reference designator", 357*fd8caa25SHal Martin }, 358*fd8caa25SHal Martin { 359*fd8caa25SHal Martin .name = "connector_type", 360*fd8caa25SHal Martin .type = QEMU_OPT_NUMBER, 361*fd8caa25SHal Martin .help = "connector type", 362*fd8caa25SHal Martin }, 363*fd8caa25SHal Martin { 364*fd8caa25SHal Martin .name = "port_type", 365*fd8caa25SHal Martin .type = QEMU_OPT_NUMBER, 366*fd8caa25SHal Martin .help = "port type", 367*fd8caa25SHal Martin }, 368*fd8caa25SHal Martin }; 369*fd8caa25SHal Martin 3702d6dcbf9SDaniel P. Berrange static const QemuOptDesc qemu_smbios_type11_opts[] = { 3712d6dcbf9SDaniel P. Berrange { 3722d6dcbf9SDaniel P. Berrange .name = "value", 3732d6dcbf9SDaniel P. Berrange .type = QEMU_OPT_STRING, 3742d6dcbf9SDaniel P. Berrange .help = "OEM string data", 3752d6dcbf9SDaniel P. Berrange }, 376bb99f477SDaniel P. Berrangé { 377bb99f477SDaniel P. Berrangé .name = "path", 378bb99f477SDaniel P. Berrangé .type = QEMU_OPT_STRING, 379bb99f477SDaniel P. Berrangé .help = "OEM string data from file", 380bb99f477SDaniel P. Berrangé }, 3812d6dcbf9SDaniel P. Berrange }; 3822d6dcbf9SDaniel P. Berrange 38360d8f328SWei Huang static const QemuOptDesc qemu_smbios_type17_opts[] = { 38460d8f328SWei Huang { 38560d8f328SWei Huang .name = "type", 38660d8f328SWei Huang .type = QEMU_OPT_NUMBER, 38760d8f328SWei Huang .help = "SMBIOS element type", 38860d8f328SWei Huang },{ 38960d8f328SWei Huang .name = "loc_pfx", 39060d8f328SWei Huang .type = QEMU_OPT_STRING, 39160d8f328SWei Huang .help = "device locator string prefix", 39260d8f328SWei Huang },{ 39360d8f328SWei Huang .name = "bank", 39460d8f328SWei Huang .type = QEMU_OPT_STRING, 39560d8f328SWei Huang .help = "bank locator string", 39660d8f328SWei Huang },{ 39760d8f328SWei Huang .name = "manufacturer", 39860d8f328SWei Huang .type = QEMU_OPT_STRING, 39960d8f328SWei Huang .help = "manufacturer name", 40060d8f328SWei Huang },{ 40160d8f328SWei Huang .name = "serial", 40260d8f328SWei Huang .type = QEMU_OPT_STRING, 40360d8f328SWei Huang .help = "serial number", 40460d8f328SWei Huang },{ 40560d8f328SWei Huang .name = "asset", 40660d8f328SWei Huang .type = QEMU_OPT_STRING, 40760d8f328SWei Huang .help = "asset tag number", 40860d8f328SWei Huang },{ 40960d8f328SWei Huang .name = "part", 41060d8f328SWei Huang .type = QEMU_OPT_STRING, 41160d8f328SWei Huang .help = "part number", 41260d8f328SWei Huang },{ 41360d8f328SWei Huang .name = "speed", 41460d8f328SWei Huang .type = QEMU_OPT_NUMBER, 41560d8f328SWei Huang .help = "maximum capable speed", 41660d8f328SWei Huang }, 41760d8f328SWei Huang { /* end of list */ } 41860d8f328SWei Huang }; 41960d8f328SWei Huang 42005dfb447SVincent Bernat static const QemuOptDesc qemu_smbios_type41_opts[] = { 42105dfb447SVincent Bernat { 42205dfb447SVincent Bernat .name = "type", 42305dfb447SVincent Bernat .type = QEMU_OPT_NUMBER, 42405dfb447SVincent Bernat .help = "SMBIOS element type", 42505dfb447SVincent Bernat },{ 42605dfb447SVincent Bernat .name = "designation", 42705dfb447SVincent Bernat .type = QEMU_OPT_STRING, 42805dfb447SVincent Bernat .help = "reference designation string", 42905dfb447SVincent Bernat },{ 43005dfb447SVincent Bernat .name = "kind", 43105dfb447SVincent Bernat .type = QEMU_OPT_STRING, 43205dfb447SVincent Bernat .help = "device type", 43305dfb447SVincent Bernat .def_value_str = "other", 43405dfb447SVincent Bernat },{ 43505dfb447SVincent Bernat .name = "instance", 43605dfb447SVincent Bernat .type = QEMU_OPT_NUMBER, 43705dfb447SVincent Bernat .help = "device type instance", 43805dfb447SVincent Bernat },{ 43905dfb447SVincent Bernat .name = "pcidev", 44005dfb447SVincent Bernat .type = QEMU_OPT_STRING, 44105dfb447SVincent Bernat .help = "PCI device", 44205dfb447SVincent Bernat }, 44305dfb447SVincent Bernat { /* end of list */ } 44405dfb447SVincent Bernat }; 44505dfb447SVincent Bernat 44660d8f328SWei Huang static void smbios_register_config(void) 44760d8f328SWei Huang { 44860d8f328SWei Huang qemu_add_opts(&qemu_smbios_opts); 44960d8f328SWei Huang } 45060d8f328SWei Huang 45134294e2fSEduardo Habkost opts_init(smbios_register_config); 45260d8f328SWei Huang 45310c36666SDaniel P. Berrangé /* 45410c36666SDaniel P. Berrangé * The SMBIOS 2.1 "structure table length" field in the 45510c36666SDaniel P. Berrangé * entry point uses a 16-bit integer, so we're limited 45610c36666SDaniel P. Berrangé * in total table size 45710c36666SDaniel P. Berrangé */ 45810c36666SDaniel P. Berrangé #define SMBIOS_21_MAX_TABLES_LEN 0xffff 45910c36666SDaniel P. Berrangé 460a0628599SLike Xu static void smbios_validate_table(MachineState *ms) 46160d8f328SWei Huang { 462a0628599SLike Xu uint32_t expect_t4_count = smbios_legacy ? 463a0628599SLike Xu ms->smp.cpus : smbios_smp_sockets; 46460d8f328SWei Huang 46560d8f328SWei Huang if (smbios_type4_count && smbios_type4_count != expect_t4_count) { 46660d8f328SWei Huang error_report("Expected %d SMBIOS Type 4 tables, got %d instead", 46760d8f328SWei Huang expect_t4_count, smbios_type4_count); 46860d8f328SWei Huang exit(1); 46960d8f328SWei Huang } 47010c36666SDaniel P. Berrangé 47110be11d0SEduardo Habkost if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && 47210c36666SDaniel P. Berrangé smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) { 47310c36666SDaniel P. Berrangé error_report("SMBIOS 2.1 table length %zu exceeds %d", 47410c36666SDaniel P. Berrangé smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN); 47510c36666SDaniel P. Berrangé exit(1); 47610c36666SDaniel P. Berrangé } 47760d8f328SWei Huang } 47860d8f328SWei Huang 47960d8f328SWei Huang 48060d8f328SWei Huang /* legacy setup functions for <= 2.0 machines */ 48160d8f328SWei Huang static void smbios_add_field(int type, int offset, const void *data, size_t len) 48260d8f328SWei Huang { 48360d8f328SWei Huang struct smbios_field *field; 48460d8f328SWei Huang 48560d8f328SWei Huang if (!smbios_entries) { 48660d8f328SWei Huang smbios_entries_len = sizeof(uint16_t); 48760d8f328SWei Huang smbios_entries = g_malloc0(smbios_entries_len); 48860d8f328SWei Huang } 48960d8f328SWei Huang smbios_entries = g_realloc(smbios_entries, smbios_entries_len + 49060d8f328SWei Huang sizeof(*field) + len); 49160d8f328SWei Huang field = (struct smbios_field *)(smbios_entries + smbios_entries_len); 49260d8f328SWei Huang field->header.type = SMBIOS_FIELD_ENTRY; 49360d8f328SWei Huang field->header.length = cpu_to_le16(sizeof(*field) + len); 49460d8f328SWei Huang 49560d8f328SWei Huang field->type = type; 49660d8f328SWei Huang field->offset = cpu_to_le16(offset); 49760d8f328SWei Huang memcpy(field->data, data, len); 49860d8f328SWei Huang 49960d8f328SWei Huang smbios_entries_len += sizeof(*field) + len; 50060d8f328SWei Huang (*(uint16_t *)smbios_entries) = 50160d8f328SWei Huang cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); 50260d8f328SWei Huang } 50360d8f328SWei Huang 50460d8f328SWei Huang static void smbios_maybe_add_str(int type, int offset, const char *data) 50560d8f328SWei Huang { 50660d8f328SWei Huang if (data) { 50760d8f328SWei Huang smbios_add_field(type, offset, data, strlen(data) + 1); 50860d8f328SWei Huang } 50960d8f328SWei Huang } 51060d8f328SWei Huang 51160d8f328SWei Huang static void smbios_build_type_0_fields(void) 51260d8f328SWei Huang { 51360d8f328SWei Huang smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str), 51460d8f328SWei Huang type0.vendor); 51560d8f328SWei Huang smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str), 51660d8f328SWei Huang type0.version); 51760d8f328SWei Huang smbios_maybe_add_str(0, offsetof(struct smbios_type_0, 51860d8f328SWei Huang bios_release_date_str), 51960d8f328SWei Huang type0.date); 52060d8f328SWei Huang if (type0.have_major_minor) { 52160d8f328SWei Huang smbios_add_field(0, offsetof(struct smbios_type_0, 52260d8f328SWei Huang system_bios_major_release), 52360d8f328SWei Huang &type0.major, 1); 52460d8f328SWei Huang smbios_add_field(0, offsetof(struct smbios_type_0, 52560d8f328SWei Huang system_bios_minor_release), 52660d8f328SWei Huang &type0.minor, 1); 52760d8f328SWei Huang } 52860d8f328SWei Huang } 52960d8f328SWei Huang 53060d8f328SWei Huang static void smbios_build_type_1_fields(void) 53160d8f328SWei Huang { 53260d8f328SWei Huang smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str), 53360d8f328SWei Huang type1.manufacturer); 53460d8f328SWei Huang smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str), 53560d8f328SWei Huang type1.product); 53660d8f328SWei Huang smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str), 53760d8f328SWei Huang type1.version); 53860d8f328SWei Huang smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str), 53960d8f328SWei Huang type1.serial); 54060d8f328SWei Huang smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str), 54160d8f328SWei Huang type1.sku); 54260d8f328SWei Huang smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), 54360d8f328SWei Huang type1.family); 54460d8f328SWei Huang if (qemu_uuid_set) { 54560d8f328SWei Huang /* We don't encode the UUID in the "wire format" here because this 54660d8f328SWei Huang * function is for legacy mode and needs to keep the guest ABI, and 54760d8f328SWei Huang * because we don't know what's the SMBIOS version advertised by the 54860d8f328SWei Huang * BIOS. 54960d8f328SWei Huang */ 55060d8f328SWei Huang smbios_add_field(1, offsetof(struct smbios_type_1, uuid), 5519c5ce8dbSFam Zheng &qemu_uuid, 16); 55260d8f328SWei Huang } 55360d8f328SWei Huang } 55460d8f328SWei Huang 555a0628599SLike Xu uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length) 55660d8f328SWei Huang { 55760d8f328SWei Huang if (!smbios_legacy) { 55860d8f328SWei Huang *length = 0; 55960d8f328SWei Huang return NULL; 56060d8f328SWei Huang } 56160d8f328SWei Huang 56260d8f328SWei Huang if (!smbios_immutable) { 56360d8f328SWei Huang smbios_build_type_0_fields(); 56460d8f328SWei Huang smbios_build_type_1_fields(); 565a0628599SLike Xu smbios_validate_table(ms); 56660d8f328SWei Huang smbios_immutable = true; 56760d8f328SWei Huang } 56860d8f328SWei Huang *length = smbios_entries_len; 56960d8f328SWei Huang return smbios_entries; 57060d8f328SWei Huang } 57160d8f328SWei Huang /* end: legacy setup functions for <= 2.0 machines */ 57260d8f328SWei Huang 57360d8f328SWei Huang 5740517cc98SCorey Minyard bool smbios_skip_table(uint8_t type, bool required_table) 57560d8f328SWei Huang { 57660d8f328SWei Huang if (test_bit(type, have_binfile_bitmap)) { 57760d8f328SWei Huang return true; /* user provided their own binary blob(s) */ 57860d8f328SWei Huang } 57960d8f328SWei Huang if (test_bit(type, have_fields_bitmap)) { 58060d8f328SWei Huang return false; /* user provided fields via command line */ 58160d8f328SWei Huang } 58260d8f328SWei Huang if (smbios_have_defaults && required_table) { 58360d8f328SWei Huang return false; /* we're building tables, and this one's required */ 58460d8f328SWei Huang } 58560d8f328SWei Huang return true; 58660d8f328SWei Huang } 58760d8f328SWei Huang 5883818acf5SAni Sinha #define T0_BASE 0x000 5893818acf5SAni Sinha #define T1_BASE 0x100 5903818acf5SAni Sinha #define T2_BASE 0x200 5913818acf5SAni Sinha #define T3_BASE 0x300 5923818acf5SAni Sinha #define T4_BASE 0x400 5933818acf5SAni Sinha #define T11_BASE 0xe00 5943818acf5SAni Sinha 5953818acf5SAni Sinha #define T16_BASE 0x1000 5963818acf5SAni Sinha #define T17_BASE 0x1100 5973818acf5SAni Sinha #define T19_BASE 0x1300 5983818acf5SAni Sinha #define T32_BASE 0x2000 5993818acf5SAni Sinha #define T41_BASE 0x2900 6003818acf5SAni Sinha #define T127_BASE 0x7F00 6013818acf5SAni Sinha 60260d8f328SWei Huang static void smbios_build_type_0_table(void) 60360d8f328SWei Huang { 6043818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(0, T0_BASE, false); /* optional, leave up to BIOS */ 60560d8f328SWei Huang 60660d8f328SWei Huang SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor); 60760d8f328SWei Huang SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version); 60860d8f328SWei Huang 60960d8f328SWei Huang t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */ 61060d8f328SWei Huang 61160d8f328SWei Huang SMBIOS_TABLE_SET_STR(0, bios_release_date_str, type0.date); 61260d8f328SWei Huang 61360d8f328SWei Huang t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */ 61460d8f328SWei Huang 61560d8f328SWei Huang t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */ 61660d8f328SWei Huang t->bios_characteristics_extension_bytes[0] = 0; 61760d8f328SWei Huang t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */ 61860d8f328SWei Huang if (type0.uefi) { 61960d8f328SWei Huang t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */ 62060d8f328SWei Huang } 62160d8f328SWei Huang 62260d8f328SWei Huang if (type0.have_major_minor) { 62360d8f328SWei Huang t->system_bios_major_release = type0.major; 62460d8f328SWei Huang t->system_bios_minor_release = type0.minor; 62560d8f328SWei Huang } else { 62660d8f328SWei Huang t->system_bios_major_release = 0; 62760d8f328SWei Huang t->system_bios_minor_release = 0; 62860d8f328SWei Huang } 62960d8f328SWei Huang 63060d8f328SWei Huang /* hardcoded in SeaBIOS */ 63160d8f328SWei Huang t->embedded_controller_major_release = 0xFF; 63260d8f328SWei Huang t->embedded_controller_minor_release = 0xFF; 63360d8f328SWei Huang 63460d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 63560d8f328SWei Huang } 63660d8f328SWei Huang 63760d8f328SWei Huang /* Encode UUID from the big endian encoding described on RFC4122 to the wire 63860d8f328SWei Huang * format specified by SMBIOS version 2.6. 63960d8f328SWei Huang */ 6409c5ce8dbSFam Zheng static void smbios_encode_uuid(struct smbios_uuid *uuid, QemuUUID *in) 64160d8f328SWei Huang { 642664ee768SMarc-André Lureau memcpy(uuid, in, 16); 64360d8f328SWei Huang if (smbios_uuid_encoded) { 64460d8f328SWei Huang uuid->time_low = bswap32(uuid->time_low); 64560d8f328SWei Huang uuid->time_mid = bswap16(uuid->time_mid); 64660d8f328SWei Huang uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); 64760d8f328SWei Huang } 64860d8f328SWei Huang } 64960d8f328SWei Huang 65060d8f328SWei Huang static void smbios_build_type_1_table(void) 65160d8f328SWei Huang { 6523818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(1, T1_BASE, true); /* required */ 65360d8f328SWei Huang 65460d8f328SWei Huang SMBIOS_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer); 65560d8f328SWei Huang SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product); 65660d8f328SWei Huang SMBIOS_TABLE_SET_STR(1, version_str, type1.version); 65760d8f328SWei Huang SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial); 65860d8f328SWei Huang if (qemu_uuid_set) { 6599c5ce8dbSFam Zheng smbios_encode_uuid(&t->uuid, &qemu_uuid); 66060d8f328SWei Huang } else { 66160d8f328SWei Huang memset(&t->uuid, 0, 16); 66260d8f328SWei Huang } 66360d8f328SWei Huang t->wake_up_type = 0x06; /* power switch */ 66460d8f328SWei Huang SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku); 66560d8f328SWei Huang SMBIOS_TABLE_SET_STR(1, family_str, type1.family); 66660d8f328SWei Huang 66760d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 66860d8f328SWei Huang } 66960d8f328SWei Huang 67060d8f328SWei Huang static void smbios_build_type_2_table(void) 67160d8f328SWei Huang { 6723818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(2, T2_BASE, false); /* optional */ 67360d8f328SWei Huang 67460d8f328SWei Huang SMBIOS_TABLE_SET_STR(2, manufacturer_str, type2.manufacturer); 67560d8f328SWei Huang SMBIOS_TABLE_SET_STR(2, product_str, type2.product); 67660d8f328SWei Huang SMBIOS_TABLE_SET_STR(2, version_str, type2.version); 67760d8f328SWei Huang SMBIOS_TABLE_SET_STR(2, serial_number_str, type2.serial); 67860d8f328SWei Huang SMBIOS_TABLE_SET_STR(2, asset_tag_number_str, type2.asset); 67960d8f328SWei Huang t->feature_flags = 0x01; /* Motherboard */ 68060d8f328SWei Huang SMBIOS_TABLE_SET_STR(2, location_str, type2.location); 68160d8f328SWei Huang t->chassis_handle = cpu_to_le16(0x300); /* Type 3 (System enclosure) */ 68260d8f328SWei Huang t->board_type = 0x0A; /* Motherboard */ 68360d8f328SWei Huang t->contained_element_count = 0; 68460d8f328SWei Huang 68560d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 68660d8f328SWei Huang } 68760d8f328SWei Huang 68860d8f328SWei Huang static void smbios_build_type_3_table(void) 68960d8f328SWei Huang { 6903818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(3, T3_BASE, true); /* required */ 69160d8f328SWei Huang 69260d8f328SWei Huang SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer); 69360d8f328SWei Huang t->type = 0x01; /* Other */ 69460d8f328SWei Huang SMBIOS_TABLE_SET_STR(3, version_str, type3.version); 69560d8f328SWei Huang SMBIOS_TABLE_SET_STR(3, serial_number_str, type3.serial); 69660d8f328SWei Huang SMBIOS_TABLE_SET_STR(3, asset_tag_number_str, type3.asset); 69760d8f328SWei Huang t->boot_up_state = 0x03; /* Safe */ 69860d8f328SWei Huang t->power_supply_state = 0x03; /* Safe */ 69960d8f328SWei Huang t->thermal_state = 0x03; /* Safe */ 70060d8f328SWei Huang t->security_status = 0x02; /* Unknown */ 70160d8f328SWei Huang t->oem_defined = cpu_to_le32(0); 70260d8f328SWei Huang t->height = 0; 70360d8f328SWei Huang t->number_of_power_cords = 0; 70460d8f328SWei Huang t->contained_element_count = 0; 705b81a5f94SDaniel P. Berrangé t->contained_element_record_length = 0; 70660d8f328SWei Huang SMBIOS_TABLE_SET_STR(3, sku_number_str, type3.sku); 70760d8f328SWei Huang 70860d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 70960d8f328SWei Huang } 71060d8f328SWei Huang 711a0628599SLike Xu static void smbios_build_type_4_table(MachineState *ms, unsigned instance) 71260d8f328SWei Huang { 71360d8f328SWei Huang char sock_str[128]; 71460d8f328SWei Huang 7153818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(4, T4_BASE + instance, true); /* required */ 71660d8f328SWei Huang 71760d8f328SWei Huang snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance); 71860d8f328SWei Huang SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); 71960d8f328SWei Huang t->processor_type = 0x03; /* CPU */ 72060d8f328SWei Huang t->processor_family = 0x01; /* Other */ 72160d8f328SWei Huang SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); 722cb5fb04fSPatrick Venture if (type4.processor_id == 0) { 72360d8f328SWei Huang t->processor_id[0] = cpu_to_le32(smbios_cpuid_version); 72460d8f328SWei Huang t->processor_id[1] = cpu_to_le32(smbios_cpuid_features); 725cb5fb04fSPatrick Venture } else { 726cb5fb04fSPatrick Venture t->processor_id[0] = cpu_to_le32((uint32_t)type4.processor_id); 727cb5fb04fSPatrick Venture t->processor_id[1] = cpu_to_le32(type4.processor_id >> 32); 728cb5fb04fSPatrick Venture } 72960d8f328SWei Huang SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); 73060d8f328SWei Huang t->voltage = 0; 73160d8f328SWei Huang t->external_clock = cpu_to_le16(0); /* Unknown */ 732c906e039SYing Fang t->max_speed = cpu_to_le16(type4.max_speed); 733c906e039SYing Fang t->current_speed = cpu_to_le16(type4.current_speed); 73460d8f328SWei Huang t->status = 0x41; /* Socket populated, CPU enabled */ 73560d8f328SWei Huang t->processor_upgrade = 0x01; /* Other */ 73660d8f328SWei Huang t->l1_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ 73760d8f328SWei Huang t->l2_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ 73860d8f328SWei Huang t->l3_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ 73960d8f328SWei Huang SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial); 74060d8f328SWei Huang SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); 74160d8f328SWei Huang SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); 742a0628599SLike Xu t->core_count = t->core_enabled = ms->smp.cores; 743a0628599SLike Xu t->thread_count = ms->smp.threads; 74460d8f328SWei Huang t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */ 74560d8f328SWei Huang t->processor_family2 = cpu_to_le16(0x01); /* Other */ 74660d8f328SWei Huang 74760d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 74860d8f328SWei Huang smbios_type4_count++; 74960d8f328SWei Huang } 75060d8f328SWei Huang 751*fd8caa25SHal Martin static void smbios_build_type_8_table(void) 752*fd8caa25SHal Martin { 753*fd8caa25SHal Martin unsigned instance = 0; 754*fd8caa25SHal Martin struct type8_instance *t8; 755*fd8caa25SHal Martin 756*fd8caa25SHal Martin QTAILQ_FOREACH(t8, &type8, next) { 757*fd8caa25SHal Martin SMBIOS_BUILD_TABLE_PRE(8, T0_BASE + instance, true); 758*fd8caa25SHal Martin 759*fd8caa25SHal Martin SMBIOS_TABLE_SET_STR(8, internal_reference_str, t8->internal_reference); 760*fd8caa25SHal Martin SMBIOS_TABLE_SET_STR(8, external_reference_str, t8->external_reference); 761*fd8caa25SHal Martin /* most vendors seem to set this to None */ 762*fd8caa25SHal Martin t->internal_connector_type = 0x0; 763*fd8caa25SHal Martin t->external_connector_type = t8->connector_type; 764*fd8caa25SHal Martin t->port_type = t8->port_type; 765*fd8caa25SHal Martin 766*fd8caa25SHal Martin SMBIOS_BUILD_TABLE_POST; 767*fd8caa25SHal Martin instance++; 768*fd8caa25SHal Martin } 769*fd8caa25SHal Martin } 770*fd8caa25SHal Martin 7712d6dcbf9SDaniel P. Berrange static void smbios_build_type_11_table(void) 7722d6dcbf9SDaniel P. Berrange { 7732d6dcbf9SDaniel P. Berrange char count_str[128]; 7742d6dcbf9SDaniel P. Berrange size_t i; 7752d6dcbf9SDaniel P. Berrange 7762d6dcbf9SDaniel P. Berrange if (type11.nvalues == 0) { 7772d6dcbf9SDaniel P. Berrange return; 7782d6dcbf9SDaniel P. Berrange } 7792d6dcbf9SDaniel P. Berrange 7803818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(11, T11_BASE, true); /* required */ 7812d6dcbf9SDaniel P. Berrange 7822d6dcbf9SDaniel P. Berrange snprintf(count_str, sizeof(count_str), "%zu", type11.nvalues); 7832d6dcbf9SDaniel P. Berrange t->count = type11.nvalues; 7842d6dcbf9SDaniel P. Berrange 7852d6dcbf9SDaniel P. Berrange for (i = 0; i < type11.nvalues; i++) { 7862d6dcbf9SDaniel P. Berrange SMBIOS_TABLE_SET_STR_LIST(11, type11.values[i]); 787bb99f477SDaniel P. Berrangé g_free(type11.values[i]); 788bb99f477SDaniel P. Berrangé type11.values[i] = NULL; 7892d6dcbf9SDaniel P. Berrange } 7902d6dcbf9SDaniel P. Berrange 7912d6dcbf9SDaniel P. Berrange SMBIOS_BUILD_TABLE_POST; 7922d6dcbf9SDaniel P. Berrange } 7932d6dcbf9SDaniel P. Berrange 79460d8f328SWei Huang #define MAX_T16_STD_SZ 0x80000000 /* 2T in Kilobytes */ 79560d8f328SWei Huang 79660d8f328SWei Huang static void smbios_build_type_16_table(unsigned dimm_cnt) 79760d8f328SWei Huang { 79860d8f328SWei Huang uint64_t size_kb; 79960d8f328SWei Huang 8003818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(16, T16_BASE, true); /* required */ 80160d8f328SWei Huang 80260d8f328SWei Huang t->location = 0x01; /* Other */ 80360d8f328SWei Huang t->use = 0x03; /* System memory */ 80460d8f328SWei Huang t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ 80586378b29SPaolo Bonzini size_kb = QEMU_ALIGN_UP(current_machine->ram_size, KiB) / KiB; 80660d8f328SWei Huang if (size_kb < MAX_T16_STD_SZ) { 80760d8f328SWei Huang t->maximum_capacity = cpu_to_le32(size_kb); 80860d8f328SWei Huang t->extended_maximum_capacity = cpu_to_le64(0); 80960d8f328SWei Huang } else { 81060d8f328SWei Huang t->maximum_capacity = cpu_to_le32(MAX_T16_STD_SZ); 81186378b29SPaolo Bonzini t->extended_maximum_capacity = cpu_to_le64(current_machine->ram_size); 81260d8f328SWei Huang } 81360d8f328SWei Huang t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ 81460d8f328SWei Huang t->number_of_memory_devices = cpu_to_le16(dimm_cnt); 81560d8f328SWei Huang 81660d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 81760d8f328SWei Huang } 81860d8f328SWei Huang 81960d8f328SWei Huang #define MAX_T17_STD_SZ 0x7FFF /* (32G - 1M), in Megabytes */ 82060d8f328SWei Huang #define MAX_T17_EXT_SZ 0x80000000 /* 2P, in Megabytes */ 82160d8f328SWei Huang 82260d8f328SWei Huang static void smbios_build_type_17_table(unsigned instance, uint64_t size) 82360d8f328SWei Huang { 82460d8f328SWei Huang char loc_str[128]; 82560d8f328SWei Huang uint64_t size_mb; 82660d8f328SWei Huang 8273818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(17, T17_BASE + instance, true); /* required */ 82860d8f328SWei Huang 82960d8f328SWei Huang t->physical_memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ 83060d8f328SWei Huang t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ 83160d8f328SWei Huang t->total_width = cpu_to_le16(0xFFFF); /* Unknown */ 83260d8f328SWei Huang t->data_width = cpu_to_le16(0xFFFF); /* Unknown */ 833968dfd05SPhilippe Mathieu-Daudé size_mb = QEMU_ALIGN_UP(size, MiB) / MiB; 83460d8f328SWei Huang if (size_mb < MAX_T17_STD_SZ) { 83560d8f328SWei Huang t->size = cpu_to_le16(size_mb); 83660d8f328SWei Huang t->extended_size = cpu_to_le32(0); 83760d8f328SWei Huang } else { 83860d8f328SWei Huang assert(size_mb < MAX_T17_EXT_SZ); 83960d8f328SWei Huang t->size = cpu_to_le16(MAX_T17_STD_SZ); 84060d8f328SWei Huang t->extended_size = cpu_to_le32(size_mb); 84160d8f328SWei Huang } 84260d8f328SWei Huang t->form_factor = 0x09; /* DIMM */ 84360d8f328SWei Huang t->device_set = 0; /* Not in a set */ 84460d8f328SWei Huang snprintf(loc_str, sizeof(loc_str), "%s %d", type17.loc_pfx, instance); 84560d8f328SWei Huang SMBIOS_TABLE_SET_STR(17, device_locator_str, loc_str); 84660d8f328SWei Huang SMBIOS_TABLE_SET_STR(17, bank_locator_str, type17.bank); 84760d8f328SWei Huang t->memory_type = 0x07; /* RAM */ 84860d8f328SWei Huang t->type_detail = cpu_to_le16(0x02); /* Other */ 84960d8f328SWei Huang t->speed = cpu_to_le16(type17.speed); 85060d8f328SWei Huang SMBIOS_TABLE_SET_STR(17, manufacturer_str, type17.manufacturer); 85160d8f328SWei Huang SMBIOS_TABLE_SET_STR(17, serial_number_str, type17.serial); 85260d8f328SWei Huang SMBIOS_TABLE_SET_STR(17, asset_tag_number_str, type17.asset); 85360d8f328SWei Huang SMBIOS_TABLE_SET_STR(17, part_number_str, type17.part); 85460d8f328SWei Huang t->attributes = 0; /* Unknown */ 85560d8f328SWei Huang t->configured_clock_speed = t->speed; /* reuse value for max speed */ 85660d8f328SWei Huang t->minimum_voltage = cpu_to_le16(0); /* Unknown */ 85760d8f328SWei Huang t->maximum_voltage = cpu_to_le16(0); /* Unknown */ 85860d8f328SWei Huang t->configured_voltage = cpu_to_le16(0); /* Unknown */ 85960d8f328SWei Huang 86060d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 86160d8f328SWei Huang } 86260d8f328SWei Huang 863a379d455SAni Sinha static void smbios_build_type_19_table(unsigned instance, unsigned offset, 86460d8f328SWei Huang uint64_t start, uint64_t size) 86560d8f328SWei Huang { 86660d8f328SWei Huang uint64_t end, start_kb, end_kb; 86760d8f328SWei Huang 868a379d455SAni Sinha SMBIOS_BUILD_TABLE_PRE(19, T19_BASE + offset + instance, 869a379d455SAni Sinha true); /* required */ 87060d8f328SWei Huang 87160d8f328SWei Huang end = start + size - 1; 87260d8f328SWei Huang assert(end > start); 873968dfd05SPhilippe Mathieu-Daudé start_kb = start / KiB; 874968dfd05SPhilippe Mathieu-Daudé end_kb = end / KiB; 87560d8f328SWei Huang if (start_kb < UINT32_MAX && end_kb < UINT32_MAX) { 87660d8f328SWei Huang t->starting_address = cpu_to_le32(start_kb); 87760d8f328SWei Huang t->ending_address = cpu_to_le32(end_kb); 87860d8f328SWei Huang t->extended_starting_address = 87960d8f328SWei Huang t->extended_ending_address = cpu_to_le64(0); 88060d8f328SWei Huang } else { 88160d8f328SWei Huang t->starting_address = t->ending_address = cpu_to_le32(UINT32_MAX); 88260d8f328SWei Huang t->extended_starting_address = cpu_to_le64(start); 88360d8f328SWei Huang t->extended_ending_address = cpu_to_le64(end); 88460d8f328SWei Huang } 88560d8f328SWei Huang t->memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ 88660d8f328SWei Huang t->partition_width = 1; /* One device per row */ 88760d8f328SWei Huang 88860d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 88960d8f328SWei Huang } 89060d8f328SWei Huang 89160d8f328SWei Huang static void smbios_build_type_32_table(void) 89260d8f328SWei Huang { 8933818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(32, T32_BASE, true); /* required */ 89460d8f328SWei Huang 89560d8f328SWei Huang memset(t->reserved, 0, 6); 89660d8f328SWei Huang t->boot_status = 0; /* No errors detected */ 89760d8f328SWei Huang 89860d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 89960d8f328SWei Huang } 90060d8f328SWei Huang 90105dfb447SVincent Bernat static void smbios_build_type_41_table(Error **errp) 90205dfb447SVincent Bernat { 90305dfb447SVincent Bernat unsigned instance = 0; 90405dfb447SVincent Bernat struct type41_instance *t41; 90505dfb447SVincent Bernat 90605dfb447SVincent Bernat QTAILQ_FOREACH(t41, &type41, next) { 9073818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(41, T41_BASE + instance, true); 90805dfb447SVincent Bernat 90905dfb447SVincent Bernat SMBIOS_TABLE_SET_STR(41, reference_designation_str, t41->designation); 91005dfb447SVincent Bernat t->device_type = t41->kind; 91105dfb447SVincent Bernat t->device_type_instance = t41->instance; 91205dfb447SVincent Bernat t->segment_group_number = cpu_to_le16(0); 91305dfb447SVincent Bernat t->bus_number = 0; 91405dfb447SVincent Bernat t->device_number = 0; 91505dfb447SVincent Bernat 91605dfb447SVincent Bernat if (t41->pcidev) { 91705dfb447SVincent Bernat PCIDevice *pdev = NULL; 91805dfb447SVincent Bernat int rc = pci_qdev_find_device(t41->pcidev, &pdev); 91905dfb447SVincent Bernat if (rc != 0) { 92005dfb447SVincent Bernat error_setg(errp, 92105dfb447SVincent Bernat "No PCI device %s for SMBIOS type 41 entry %s", 92205dfb447SVincent Bernat t41->pcidev, t41->designation); 92305dfb447SVincent Bernat return; 92405dfb447SVincent Bernat } 92505dfb447SVincent Bernat /* 92605dfb447SVincent Bernat * We only handle the case were the device is attached to 92705dfb447SVincent Bernat * the PCI root bus. The general case is more complex as 92805dfb447SVincent Bernat * bridges are enumerated later and the table would need 92905dfb447SVincent Bernat * to be updated at this moment. 93005dfb447SVincent Bernat */ 93105dfb447SVincent Bernat if (!pci_bus_is_root(pci_get_bus(pdev))) { 93205dfb447SVincent Bernat error_setg(errp, 93305dfb447SVincent Bernat "Cannot create type 41 entry for PCI device %s: " 93405dfb447SVincent Bernat "not attached to the root bus", 93505dfb447SVincent Bernat t41->pcidev); 93605dfb447SVincent Bernat return; 93705dfb447SVincent Bernat } 93805dfb447SVincent Bernat t->segment_group_number = cpu_to_le16(0); 93905dfb447SVincent Bernat t->bus_number = pci_dev_bus_num(pdev); 94005dfb447SVincent Bernat t->device_number = pdev->devfn; 94105dfb447SVincent Bernat } 94205dfb447SVincent Bernat 94305dfb447SVincent Bernat SMBIOS_BUILD_TABLE_POST; 94405dfb447SVincent Bernat instance++; 94505dfb447SVincent Bernat } 94605dfb447SVincent Bernat } 94705dfb447SVincent Bernat 94860d8f328SWei Huang static void smbios_build_type_127_table(void) 94960d8f328SWei Huang { 9503818acf5SAni Sinha SMBIOS_BUILD_TABLE_PRE(127, T127_BASE, true); /* required */ 95160d8f328SWei Huang SMBIOS_BUILD_TABLE_POST; 95260d8f328SWei Huang } 95360d8f328SWei Huang 95460d8f328SWei Huang void smbios_set_cpuid(uint32_t version, uint32_t features) 95560d8f328SWei Huang { 95660d8f328SWei Huang smbios_cpuid_version = version; 95760d8f328SWei Huang smbios_cpuid_features = features; 95860d8f328SWei Huang } 95960d8f328SWei Huang 96060d8f328SWei Huang #define SMBIOS_SET_DEFAULT(field, value) \ 96160d8f328SWei Huang if (!field) { \ 96260d8f328SWei Huang field = value; \ 96360d8f328SWei Huang } 96460d8f328SWei Huang 96560d8f328SWei Huang void smbios_set_defaults(const char *manufacturer, const char *product, 96660d8f328SWei Huang const char *version, bool legacy_mode, 96786299120SWei Huang bool uuid_encoded, SmbiosEntryPointType ep_type) 96860d8f328SWei Huang { 96960d8f328SWei Huang smbios_have_defaults = true; 97060d8f328SWei Huang smbios_legacy = legacy_mode; 97160d8f328SWei Huang smbios_uuid_encoded = uuid_encoded; 97286299120SWei Huang smbios_ep_type = ep_type; 97360d8f328SWei Huang 97460d8f328SWei Huang /* drop unwanted version of command-line file blob(s) */ 97560d8f328SWei Huang if (smbios_legacy) { 97660d8f328SWei Huang g_free(smbios_tables); 97760d8f328SWei Huang /* in legacy mode, also complain if fields were given for types > 1 */ 97860d8f328SWei Huang if (find_next_bit(have_fields_bitmap, 97960d8f328SWei Huang SMBIOS_MAX_TYPE+1, 2) < SMBIOS_MAX_TYPE+1) { 98060d8f328SWei Huang error_report("can't process fields for smbios " 98160d8f328SWei Huang "types > 1 on machine versions < 2.1!"); 98260d8f328SWei Huang exit(1); 98360d8f328SWei Huang } 98460d8f328SWei Huang } else { 98560d8f328SWei Huang g_free(smbios_entries); 98660d8f328SWei Huang } 98760d8f328SWei Huang 98860d8f328SWei Huang SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer); 98960d8f328SWei Huang SMBIOS_SET_DEFAULT(type1.product, product); 99060d8f328SWei Huang SMBIOS_SET_DEFAULT(type1.version, version); 99160d8f328SWei Huang SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); 99260d8f328SWei Huang SMBIOS_SET_DEFAULT(type2.product, product); 99360d8f328SWei Huang SMBIOS_SET_DEFAULT(type2.version, version); 99460d8f328SWei Huang SMBIOS_SET_DEFAULT(type3.manufacturer, manufacturer); 99560d8f328SWei Huang SMBIOS_SET_DEFAULT(type3.version, version); 99660d8f328SWei Huang SMBIOS_SET_DEFAULT(type4.sock_pfx, "CPU"); 99760d8f328SWei Huang SMBIOS_SET_DEFAULT(type4.manufacturer, manufacturer); 99860d8f328SWei Huang SMBIOS_SET_DEFAULT(type4.version, version); 99960d8f328SWei Huang SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); 100060d8f328SWei Huang SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer); 100160d8f328SWei Huang } 100260d8f328SWei Huang 100360d8f328SWei Huang static void smbios_entry_point_setup(void) 100460d8f328SWei Huang { 100586299120SWei Huang switch (smbios_ep_type) { 100610be11d0SEduardo Habkost case SMBIOS_ENTRY_POINT_TYPE_32: 100786299120SWei Huang memcpy(ep.ep21.anchor_string, "_SM_", 4); 100886299120SWei Huang memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5); 100986299120SWei Huang ep.ep21.length = sizeof(struct smbios_21_entry_point); 101086299120SWei Huang ep.ep21.entry_point_revision = 0; /* formatted_area reserved */ 101186299120SWei Huang memset(ep.ep21.formatted_area, 0, 5); 101260d8f328SWei Huang 101360d8f328SWei Huang /* compliant with smbios spec v2.8 */ 101486299120SWei Huang ep.ep21.smbios_major_version = 2; 101586299120SWei Huang ep.ep21.smbios_minor_version = 8; 101686299120SWei Huang ep.ep21.smbios_bcd_revision = 0x28; 101760d8f328SWei Huang 101860d8f328SWei Huang /* set during table construction, but BIOS may override: */ 101986299120SWei Huang ep.ep21.structure_table_length = cpu_to_le16(smbios_tables_len); 102086299120SWei Huang ep.ep21.max_structure_size = cpu_to_le16(smbios_table_max); 102186299120SWei Huang ep.ep21.number_of_structures = cpu_to_le16(smbios_table_cnt); 102260d8f328SWei Huang 102386299120SWei Huang /* BIOS must recalculate */ 102486299120SWei Huang ep.ep21.checksum = 0; 102586299120SWei Huang ep.ep21.intermediate_checksum = 0; 102686299120SWei Huang ep.ep21.structure_table_address = cpu_to_le32(0); 102786299120SWei Huang 102886299120SWei Huang break; 102910be11d0SEduardo Habkost case SMBIOS_ENTRY_POINT_TYPE_64: 103086299120SWei Huang memcpy(ep.ep30.anchor_string, "_SM3_", 5); 103186299120SWei Huang ep.ep30.length = sizeof(struct smbios_30_entry_point); 103286299120SWei Huang ep.ep30.entry_point_revision = 1; 103386299120SWei Huang ep.ep30.reserved = 0; 103486299120SWei Huang 103586299120SWei Huang /* compliant with smbios spec 3.0 */ 103686299120SWei Huang ep.ep30.smbios_major_version = 3; 103786299120SWei Huang ep.ep30.smbios_minor_version = 0; 103886299120SWei Huang ep.ep30.smbios_doc_rev = 0; 103986299120SWei Huang 104086299120SWei Huang /* set during table construct, but BIOS might override */ 104186299120SWei Huang ep.ep30.structure_table_max_size = cpu_to_le32(smbios_tables_len); 104286299120SWei Huang 104386299120SWei Huang /* BIOS must recalculate */ 104486299120SWei Huang ep.ep30.checksum = 0; 104586299120SWei Huang ep.ep30.structure_table_address = cpu_to_le64(0); 104686299120SWei Huang 104786299120SWei Huang break; 104886299120SWei Huang default: 104986299120SWei Huang abort(); 105086299120SWei Huang break; 105186299120SWei Huang } 105260d8f328SWei Huang } 105360d8f328SWei Huang 1054a0628599SLike Xu void smbios_get_tables(MachineState *ms, 1055a0628599SLike Xu const struct smbios_phys_mem_area *mem_array, 105660d8f328SWei Huang const unsigned int mem_array_size, 105760d8f328SWei Huang uint8_t **tables, size_t *tables_len, 105805dfb447SVincent Bernat uint8_t **anchor, size_t *anchor_len, 105905dfb447SVincent Bernat Error **errp) 106060d8f328SWei Huang { 1061a379d455SAni Sinha unsigned i, dimm_cnt, offset; 106260d8f328SWei Huang 106360d8f328SWei Huang if (smbios_legacy) { 106460d8f328SWei Huang *tables = *anchor = NULL; 106560d8f328SWei Huang *tables_len = *anchor_len = 0; 106660d8f328SWei Huang return; 106760d8f328SWei Huang } 106860d8f328SWei Huang 106960d8f328SWei Huang if (!smbios_immutable) { 107060d8f328SWei Huang smbios_build_type_0_table(); 107160d8f328SWei Huang smbios_build_type_1_table(); 107260d8f328SWei Huang smbios_build_type_2_table(); 107360d8f328SWei Huang smbios_build_type_3_table(); 107460d8f328SWei Huang 1075a0628599SLike Xu smbios_smp_sockets = DIV_ROUND_UP(ms->smp.cpus, 1076a0628599SLike Xu ms->smp.cores * ms->smp.threads); 107760d8f328SWei Huang assert(smbios_smp_sockets >= 1); 107860d8f328SWei Huang 107960d8f328SWei Huang for (i = 0; i < smbios_smp_sockets; i++) { 1080a0628599SLike Xu smbios_build_type_4_table(ms, i); 108160d8f328SWei Huang } 108260d8f328SWei Huang 1083*fd8caa25SHal Martin smbios_build_type_8_table(); 10842d6dcbf9SDaniel P. Berrange smbios_build_type_11_table(); 10852d6dcbf9SDaniel P. Berrange 1086968dfd05SPhilippe Mathieu-Daudé #define MAX_DIMM_SZ (16 * GiB) 108760d8f328SWei Huang #define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ 108886378b29SPaolo Bonzini : ((current_machine->ram_size - 1) % MAX_DIMM_SZ) + 1) 108960d8f328SWei Huang 109086378b29SPaolo Bonzini dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; 109160d8f328SWei Huang 1092a379d455SAni Sinha /* 1093a379d455SAni Sinha * The offset determines if we need to keep additional space betweeen 1094a379d455SAni Sinha * table 17 and table 19 header handle numbers so that they do 1095a379d455SAni Sinha * not overlap. For example, for a VM with larger than 8 TB guest 1096a379d455SAni Sinha * memory and DIMM like chunks of 16 GiB, the default space between 1097a379d455SAni Sinha * the two tables (T19_BASE - T17_BASE = 512) is not enough. 1098a379d455SAni Sinha */ 1099a379d455SAni Sinha offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \ 1100a379d455SAni Sinha dimm_cnt - (T19_BASE - T17_BASE) : 0; 1101a379d455SAni Sinha 110260d8f328SWei Huang smbios_build_type_16_table(dimm_cnt); 110360d8f328SWei Huang 110460d8f328SWei Huang for (i = 0; i < dimm_cnt; i++) { 110560d8f328SWei Huang smbios_build_type_17_table(i, GET_DIMM_SZ); 110660d8f328SWei Huang } 110760d8f328SWei Huang 110860d8f328SWei Huang for (i = 0; i < mem_array_size; i++) { 1109a379d455SAni Sinha smbios_build_type_19_table(i, offset, mem_array[i].address, 111060d8f328SWei Huang mem_array[i].length); 111160d8f328SWei Huang } 111260d8f328SWei Huang 111363670bd3SAni Sinha /* 111463670bd3SAni Sinha * make sure 16 bit handle numbers in the headers of tables 19 111563670bd3SAni Sinha * and 32 do not overlap. 111663670bd3SAni Sinha */ 111763670bd3SAni Sinha assert((mem_array_size + offset) < (T32_BASE - T19_BASE)); 111863670bd3SAni Sinha 111960d8f328SWei Huang smbios_build_type_32_table(); 112035658f6eSCorey Minyard smbios_build_type_38_table(); 112105dfb447SVincent Bernat smbios_build_type_41_table(errp); 112260d8f328SWei Huang smbios_build_type_127_table(); 112360d8f328SWei Huang 1124a0628599SLike Xu smbios_validate_table(ms); 112560d8f328SWei Huang smbios_entry_point_setup(); 112660d8f328SWei Huang smbios_immutable = true; 112760d8f328SWei Huang } 112860d8f328SWei Huang 112960d8f328SWei Huang /* return tables blob and entry point (anchor), and their sizes */ 113060d8f328SWei Huang *tables = smbios_tables; 113160d8f328SWei Huang *tables_len = smbios_tables_len; 113260d8f328SWei Huang *anchor = (uint8_t *)&ep; 113386299120SWei Huang 113486299120SWei Huang /* calculate length based on anchor string */ 113586299120SWei Huang if (!strncmp((char *)&ep, "_SM_", 4)) { 113686299120SWei Huang *anchor_len = sizeof(struct smbios_21_entry_point); 113786299120SWei Huang } else if (!strncmp((char *)&ep, "_SM3_", 5)) { 113886299120SWei Huang *anchor_len = sizeof(struct smbios_30_entry_point); 113986299120SWei Huang } else { 114086299120SWei Huang abort(); 114186299120SWei Huang } 114260d8f328SWei Huang } 114360d8f328SWei Huang 114460d8f328SWei Huang static void save_opt(const char **dest, QemuOpts *opts, const char *name) 114560d8f328SWei Huang { 114660d8f328SWei Huang const char *val = qemu_opt_get(opts, name); 114760d8f328SWei Huang 114860d8f328SWei Huang if (val) { 114960d8f328SWei Huang *dest = val; 115060d8f328SWei Huang } 115160d8f328SWei Huang } 115260d8f328SWei Huang 11532d6dcbf9SDaniel P. Berrange 11542d6dcbf9SDaniel P. Berrange struct opt_list { 11552d6dcbf9SDaniel P. Berrange size_t *ndest; 1156bb99f477SDaniel P. Berrangé char ***dest; 11572d6dcbf9SDaniel P. Berrange }; 11582d6dcbf9SDaniel P. Berrange 11592d6dcbf9SDaniel P. Berrange static int save_opt_one(void *opaque, 11602d6dcbf9SDaniel P. Berrange const char *name, const char *value, 11612d6dcbf9SDaniel P. Berrange Error **errp) 11622d6dcbf9SDaniel P. Berrange { 11632d6dcbf9SDaniel P. Berrange struct opt_list *opt = opaque; 11642d6dcbf9SDaniel P. Berrange 1165bb99f477SDaniel P. Berrangé if (g_str_equal(name, "path")) { 1166bb99f477SDaniel P. Berrangé g_autoptr(GByteArray) data = g_byte_array_new(); 1167bb99f477SDaniel P. Berrangé g_autofree char *buf = g_new(char, 4096); 1168bb99f477SDaniel P. Berrangé ssize_t ret; 1169bb99f477SDaniel P. Berrangé int fd = qemu_open(value, O_RDONLY, errp); 1170bb99f477SDaniel P. Berrangé if (fd < 0) { 1171bb99f477SDaniel P. Berrangé return -1; 11722d6dcbf9SDaniel P. Berrange } 11732d6dcbf9SDaniel P. Berrange 1174bb99f477SDaniel P. Berrangé while (1) { 1175bb99f477SDaniel P. Berrangé ret = read(fd, buf, 4096); 1176bb99f477SDaniel P. Berrangé if (ret == 0) { 1177bb99f477SDaniel P. Berrangé break; 1178bb99f477SDaniel P. Berrangé } 1179bb99f477SDaniel P. Berrangé if (ret < 0) { 1180bb99f477SDaniel P. Berrangé error_setg(errp, "Unable to read from %s: %s", 1181bb99f477SDaniel P. Berrangé value, strerror(errno)); 11828055d2fbSPhilippe Mathieu-Daudé qemu_close(fd); 1183bb99f477SDaniel P. Berrangé return -1; 1184bb99f477SDaniel P. Berrangé } 1185bb99f477SDaniel P. Berrangé if (memchr(buf, '\0', ret)) { 1186bb99f477SDaniel P. Berrangé error_setg(errp, "NUL in OEM strings value in %s", value); 11878055d2fbSPhilippe Mathieu-Daudé qemu_close(fd); 1188bb99f477SDaniel P. Berrangé return -1; 1189bb99f477SDaniel P. Berrangé } 1190bb99f477SDaniel P. Berrangé g_byte_array_append(data, (guint8 *)buf, ret); 1191bb99f477SDaniel P. Berrangé } 1192bb99f477SDaniel P. Berrangé 11938055d2fbSPhilippe Mathieu-Daudé qemu_close(fd); 1194bb99f477SDaniel P. Berrangé 1195bb99f477SDaniel P. Berrangé *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); 1196bb99f477SDaniel P. Berrangé (*opt->dest)[*opt->ndest] = (char *)g_byte_array_free(data, FALSE); 11972d6dcbf9SDaniel P. Berrange (*opt->ndest)++; 1198bb99f477SDaniel P. Berrangé data = NULL; 1199bb99f477SDaniel P. Berrangé } else if (g_str_equal(name, "value")) { 1200bb99f477SDaniel P. Berrangé *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); 1201bb99f477SDaniel P. Berrangé (*opt->dest)[*opt->ndest] = g_strdup(value); 1202bb99f477SDaniel P. Berrangé (*opt->ndest)++; 1203bb99f477SDaniel P. Berrangé } else if (!g_str_equal(name, "type")) { 1204bb99f477SDaniel P. Berrangé error_setg(errp, "Unexpected option %s", name); 1205bb99f477SDaniel P. Berrangé return -1; 1206bb99f477SDaniel P. Berrangé } 1207bb99f477SDaniel P. Berrangé 12082d6dcbf9SDaniel P. Berrange return 0; 12092d6dcbf9SDaniel P. Berrange } 12102d6dcbf9SDaniel P. Berrange 1211bb99f477SDaniel P. Berrangé static bool save_opt_list(size_t *ndest, char ***dest, QemuOpts *opts, 1212bb99f477SDaniel P. Berrangé Error **errp) 12132d6dcbf9SDaniel P. Berrange { 12142d6dcbf9SDaniel P. Berrange struct opt_list opt = { 1215bb99f477SDaniel P. Berrangé ndest, dest, 12162d6dcbf9SDaniel P. Berrange }; 1217bb99f477SDaniel P. Berrangé if (!qemu_opt_foreach(opts, save_opt_one, &opt, errp)) { 1218bb99f477SDaniel P. Berrangé return false; 1219bb99f477SDaniel P. Berrangé } 1220bb99f477SDaniel P. Berrangé return true; 12212d6dcbf9SDaniel P. Berrange } 12222d6dcbf9SDaniel P. Berrange 12231007a37eSLeif Lindholm void smbios_entry_add(QemuOpts *opts, Error **errp) 122460d8f328SWei Huang { 122560d8f328SWei Huang const char *val; 122660d8f328SWei Huang 122760d8f328SWei Huang assert(!smbios_immutable); 122860d8f328SWei Huang 122960d8f328SWei Huang val = qemu_opt_get(opts, "file"); 123060d8f328SWei Huang if (val) { 123160d8f328SWei Huang struct smbios_structure_header *header; 123260d8f328SWei Huang int size; 123360d8f328SWei Huang struct smbios_table *table; /* legacy mode only */ 123460d8f328SWei Huang 1235668f62ecSMarkus Armbruster if (!qemu_opts_validate(opts, qemu_smbios_file_opts, errp)) { 12361028283cSMarkus Armbruster return; 12371028283cSMarkus Armbruster } 123860d8f328SWei Huang 123960d8f328SWei Huang size = get_image_size(val); 124060d8f328SWei Huang if (size == -1 || size < sizeof(struct smbios_structure_header)) { 12411028283cSMarkus Armbruster error_setg(errp, "Cannot read SMBIOS file %s", val); 12421028283cSMarkus Armbruster return; 124360d8f328SWei Huang } 124460d8f328SWei Huang 124560d8f328SWei Huang /* 124660d8f328SWei Huang * NOTE: standard double '\0' terminator expected, per smbios spec. 124760d8f328SWei Huang * (except in legacy mode, where the second '\0' is implicit and 124860d8f328SWei Huang * will be inserted by the BIOS). 124960d8f328SWei Huang */ 125060d8f328SWei Huang smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size); 125160d8f328SWei Huang header = (struct smbios_structure_header *)(smbios_tables + 125260d8f328SWei Huang smbios_tables_len); 125360d8f328SWei Huang 1254b7abb791SPeter Maydell if (load_image_size(val, (uint8_t *)header, size) != size) { 12551028283cSMarkus Armbruster error_setg(errp, "Failed to load SMBIOS file %s", val); 12561028283cSMarkus Armbruster return; 125760d8f328SWei Huang } 125860d8f328SWei Huang 125957e30696SPaolo Bonzini if (header->type <= SMBIOS_MAX_TYPE) { 126060d8f328SWei Huang if (test_bit(header->type, have_fields_bitmap)) { 12611028283cSMarkus Armbruster error_setg(errp, 12621028283cSMarkus Armbruster "can't load type %d struct, fields already specified!", 126360d8f328SWei Huang header->type); 12641028283cSMarkus Armbruster return; 126560d8f328SWei Huang } 126660d8f328SWei Huang set_bit(header->type, have_binfile_bitmap); 126757e30696SPaolo Bonzini } 126860d8f328SWei Huang 126960d8f328SWei Huang if (header->type == 4) { 127060d8f328SWei Huang smbios_type4_count++; 127160d8f328SWei Huang } 127260d8f328SWei Huang 127360d8f328SWei Huang smbios_tables_len += size; 127460d8f328SWei Huang if (size > smbios_table_max) { 127560d8f328SWei Huang smbios_table_max = size; 127660d8f328SWei Huang } 127760d8f328SWei Huang smbios_table_cnt++; 127860d8f328SWei Huang 127960d8f328SWei Huang /* add a copy of the newly loaded blob to legacy smbios_entries */ 128060d8f328SWei Huang /* NOTE: This code runs before smbios_set_defaults(), so we don't 128160d8f328SWei Huang * yet know which mode (legacy vs. aggregate-table) will be 128260d8f328SWei Huang * required. We therefore add the binary blob to both legacy 128360d8f328SWei Huang * (smbios_entries) and aggregate (smbios_tables) tables, and 128460d8f328SWei Huang * delete the one we don't need from smbios_set_defaults(), 128560d8f328SWei Huang * once we know which machine version has been requested. 128660d8f328SWei Huang */ 128760d8f328SWei Huang if (!smbios_entries) { 128860d8f328SWei Huang smbios_entries_len = sizeof(uint16_t); 128960d8f328SWei Huang smbios_entries = g_malloc0(smbios_entries_len); 129060d8f328SWei Huang } 129160d8f328SWei Huang smbios_entries = g_realloc(smbios_entries, smbios_entries_len + 129260d8f328SWei Huang size + sizeof(*table)); 129360d8f328SWei Huang table = (struct smbios_table *)(smbios_entries + smbios_entries_len); 129460d8f328SWei Huang table->header.type = SMBIOS_TABLE_ENTRY; 129560d8f328SWei Huang table->header.length = cpu_to_le16(sizeof(*table) + size); 129660d8f328SWei Huang memcpy(table->data, header, size); 129760d8f328SWei Huang smbios_entries_len += sizeof(*table) + size; 129860d8f328SWei Huang (*(uint16_t *)smbios_entries) = 129960d8f328SWei Huang cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); 130060d8f328SWei Huang /* end: add a copy of the newly loaded blob to legacy smbios_entries */ 130160d8f328SWei Huang 130260d8f328SWei Huang return; 130360d8f328SWei Huang } 130460d8f328SWei Huang 130560d8f328SWei Huang val = qemu_opt_get(opts, "type"); 130660d8f328SWei Huang if (val) { 130760d8f328SWei Huang unsigned long type = strtoul(val, NULL, 0); 130860d8f328SWei Huang 130960d8f328SWei Huang if (type > SMBIOS_MAX_TYPE) { 13101028283cSMarkus Armbruster error_setg(errp, "out of range!"); 13111028283cSMarkus Armbruster return; 131260d8f328SWei Huang } 131360d8f328SWei Huang 131460d8f328SWei Huang if (test_bit(type, have_binfile_bitmap)) { 13151028283cSMarkus Armbruster error_setg(errp, "can't add fields, binary file already loaded!"); 13161028283cSMarkus Armbruster return; 131760d8f328SWei Huang } 131860d8f328SWei Huang set_bit(type, have_fields_bitmap); 131960d8f328SWei Huang 132060d8f328SWei Huang switch (type) { 132160d8f328SWei Huang case 0: 1322668f62ecSMarkus Armbruster if (!qemu_opts_validate(opts, qemu_smbios_type0_opts, errp)) { 13231028283cSMarkus Armbruster return; 13241028283cSMarkus Armbruster } 132560d8f328SWei Huang save_opt(&type0.vendor, opts, "vendor"); 132660d8f328SWei Huang save_opt(&type0.version, opts, "version"); 132760d8f328SWei Huang save_opt(&type0.date, opts, "date"); 132860d8f328SWei Huang type0.uefi = qemu_opt_get_bool(opts, "uefi", false); 132960d8f328SWei Huang 133060d8f328SWei Huang val = qemu_opt_get(opts, "release"); 133160d8f328SWei Huang if (val) { 133260d8f328SWei Huang if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { 13331028283cSMarkus Armbruster error_setg(errp, "Invalid release"); 13341028283cSMarkus Armbruster return; 133560d8f328SWei Huang } 133660d8f328SWei Huang type0.have_major_minor = true; 133760d8f328SWei Huang } 133860d8f328SWei Huang return; 133960d8f328SWei Huang case 1: 1340668f62ecSMarkus Armbruster if (!qemu_opts_validate(opts, qemu_smbios_type1_opts, errp)) { 13411028283cSMarkus Armbruster return; 13421028283cSMarkus Armbruster } 134360d8f328SWei Huang save_opt(&type1.manufacturer, opts, "manufacturer"); 134460d8f328SWei Huang save_opt(&type1.product, opts, "product"); 134560d8f328SWei Huang save_opt(&type1.version, opts, "version"); 134660d8f328SWei Huang save_opt(&type1.serial, opts, "serial"); 134760d8f328SWei Huang save_opt(&type1.sku, opts, "sku"); 134860d8f328SWei Huang save_opt(&type1.family, opts, "family"); 134960d8f328SWei Huang 135060d8f328SWei Huang val = qemu_opt_get(opts, "uuid"); 135160d8f328SWei Huang if (val) { 13529c5ce8dbSFam Zheng if (qemu_uuid_parse(val, &qemu_uuid) != 0) { 13531028283cSMarkus Armbruster error_setg(errp, "Invalid UUID"); 13541028283cSMarkus Armbruster return; 135560d8f328SWei Huang } 135660d8f328SWei Huang qemu_uuid_set = true; 135760d8f328SWei Huang } 135860d8f328SWei Huang return; 135960d8f328SWei Huang case 2: 1360668f62ecSMarkus Armbruster if (!qemu_opts_validate(opts, qemu_smbios_type2_opts, errp)) { 13611028283cSMarkus Armbruster return; 13621028283cSMarkus Armbruster } 136360d8f328SWei Huang save_opt(&type2.manufacturer, opts, "manufacturer"); 136460d8f328SWei Huang save_opt(&type2.product, opts, "product"); 136560d8f328SWei Huang save_opt(&type2.version, opts, "version"); 136660d8f328SWei Huang save_opt(&type2.serial, opts, "serial"); 136760d8f328SWei Huang save_opt(&type2.asset, opts, "asset"); 136860d8f328SWei Huang save_opt(&type2.location, opts, "location"); 136960d8f328SWei Huang return; 137060d8f328SWei Huang case 3: 1371668f62ecSMarkus Armbruster if (!qemu_opts_validate(opts, qemu_smbios_type3_opts, errp)) { 13721028283cSMarkus Armbruster return; 13731028283cSMarkus Armbruster } 137460d8f328SWei Huang save_opt(&type3.manufacturer, opts, "manufacturer"); 137560d8f328SWei Huang save_opt(&type3.version, opts, "version"); 137660d8f328SWei Huang save_opt(&type3.serial, opts, "serial"); 137760d8f328SWei Huang save_opt(&type3.asset, opts, "asset"); 137860d8f328SWei Huang save_opt(&type3.sku, opts, "sku"); 137960d8f328SWei Huang return; 138060d8f328SWei Huang case 4: 1381668f62ecSMarkus Armbruster if (!qemu_opts_validate(opts, qemu_smbios_type4_opts, errp)) { 13821028283cSMarkus Armbruster return; 13831028283cSMarkus Armbruster } 138460d8f328SWei Huang save_opt(&type4.sock_pfx, opts, "sock_pfx"); 138560d8f328SWei Huang save_opt(&type4.manufacturer, opts, "manufacturer"); 138660d8f328SWei Huang save_opt(&type4.version, opts, "version"); 138760d8f328SWei Huang save_opt(&type4.serial, opts, "serial"); 138860d8f328SWei Huang save_opt(&type4.asset, opts, "asset"); 138960d8f328SWei Huang save_opt(&type4.part, opts, "part"); 1390cb5fb04fSPatrick Venture /* If the value is 0, it will take the value from the CPU model. */ 1391cb5fb04fSPatrick Venture type4.processor_id = qemu_opt_get_number(opts, "processor-id", 0); 1392c906e039SYing Fang type4.max_speed = qemu_opt_get_number(opts, "max-speed", 1393c906e039SYing Fang DEFAULT_CPU_SPEED); 1394c906e039SYing Fang type4.current_speed = qemu_opt_get_number(opts, "current-speed", 1395c906e039SYing Fang DEFAULT_CPU_SPEED); 1396c906e039SYing Fang if (type4.max_speed > UINT16_MAX || 1397c906e039SYing Fang type4.current_speed > UINT16_MAX) { 1398c906e039SYing Fang error_setg(errp, "SMBIOS CPU speed is too large (> %d)", 1399c906e039SYing Fang UINT16_MAX); 1400c906e039SYing Fang } 140160d8f328SWei Huang return; 1402*fd8caa25SHal Martin case 8: 1403*fd8caa25SHal Martin if (!qemu_opts_validate(opts, qemu_smbios_type8_opts, errp)) { 1404*fd8caa25SHal Martin return; 1405*fd8caa25SHal Martin } 1406*fd8caa25SHal Martin struct type8_instance *t; 1407*fd8caa25SHal Martin t = g_new0(struct type8_instance, 1); 1408*fd8caa25SHal Martin save_opt(&t->internal_reference, opts, "internal_reference"); 1409*fd8caa25SHal Martin save_opt(&t->external_reference, opts, "external_reference"); 1410*fd8caa25SHal Martin t->connector_type = qemu_opt_get_number(opts, "connector_type", 0); 1411*fd8caa25SHal Martin t->port_type = qemu_opt_get_number(opts, "port_type", 0); 1412*fd8caa25SHal Martin QTAILQ_INSERT_TAIL(&type8, t, next); 1413*fd8caa25SHal Martin return; 14142d6dcbf9SDaniel P. Berrange case 11: 1415668f62ecSMarkus Armbruster if (!qemu_opts_validate(opts, qemu_smbios_type11_opts, errp)) { 14161028283cSMarkus Armbruster return; 14171028283cSMarkus Armbruster } 1418bb99f477SDaniel P. Berrangé if (!save_opt_list(&type11.nvalues, &type11.values, opts, errp)) { 1419bb99f477SDaniel P. Berrangé return; 1420bb99f477SDaniel P. Berrangé } 14212d6dcbf9SDaniel P. Berrange return; 142260d8f328SWei Huang case 17: 1423668f62ecSMarkus Armbruster if (!qemu_opts_validate(opts, qemu_smbios_type17_opts, errp)) { 14241028283cSMarkus Armbruster return; 14251028283cSMarkus Armbruster } 142660d8f328SWei Huang save_opt(&type17.loc_pfx, opts, "loc_pfx"); 142760d8f328SWei Huang save_opt(&type17.bank, opts, "bank"); 142860d8f328SWei Huang save_opt(&type17.manufacturer, opts, "manufacturer"); 142960d8f328SWei Huang save_opt(&type17.serial, opts, "serial"); 143060d8f328SWei Huang save_opt(&type17.asset, opts, "asset"); 143160d8f328SWei Huang save_opt(&type17.part, opts, "part"); 143260d8f328SWei Huang type17.speed = qemu_opt_get_number(opts, "speed", 0); 143360d8f328SWei Huang return; 143405dfb447SVincent Bernat case 41: { 143505dfb447SVincent Bernat struct type41_instance *t; 143605dfb447SVincent Bernat Error *local_err = NULL; 143705dfb447SVincent Bernat 143805dfb447SVincent Bernat if (!qemu_opts_validate(opts, qemu_smbios_type41_opts, errp)) { 143905dfb447SVincent Bernat return; 144005dfb447SVincent Bernat } 144105dfb447SVincent Bernat t = g_new0(struct type41_instance, 1); 144205dfb447SVincent Bernat save_opt(&t->designation, opts, "designation"); 144305dfb447SVincent Bernat t->kind = qapi_enum_parse(&type41_kind_lookup, 144405dfb447SVincent Bernat qemu_opt_get(opts, "kind"), 144505dfb447SVincent Bernat 0, &local_err) + 1; 144605dfb447SVincent Bernat t->kind |= 0x80; /* enabled */ 144705dfb447SVincent Bernat if (local_err != NULL) { 144805dfb447SVincent Bernat error_propagate(errp, local_err); 144905dfb447SVincent Bernat g_free(t); 145005dfb447SVincent Bernat return; 145105dfb447SVincent Bernat } 145205dfb447SVincent Bernat t->instance = qemu_opt_get_number(opts, "instance", 1); 145305dfb447SVincent Bernat save_opt(&t->pcidev, opts, "pcidev"); 145405dfb447SVincent Bernat 145505dfb447SVincent Bernat QTAILQ_INSERT_TAIL(&type41, t, next); 145605dfb447SVincent Bernat return; 145705dfb447SVincent Bernat } 145860d8f328SWei Huang default: 14591028283cSMarkus Armbruster error_setg(errp, 14601028283cSMarkus Armbruster "Don't know how to build fields for SMBIOS type %ld", 146160d8f328SWei Huang type); 14621028283cSMarkus Armbruster return; 146360d8f328SWei Huang } 146460d8f328SWei Huang } 146560d8f328SWei Huang 14661028283cSMarkus Armbruster error_setg(errp, "Must specify type= or file="); 146760d8f328SWei Huang } 1468