xref: /openbmc/qemu/hw/smbios/smbios.c (revision fd8caa25)
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