xref: /illumos-gate/usr/src/cmd/bhyve/smbiostbl.c (revision 8361acf58a302751348aac091ab09484f3ecfb8c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <md5.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <uuid.h>
42 
43 #include <machine/vmm.h>
44 #include <vmmapi.h>
45 
46 #ifndef __FreeBSD__
47 #include <sys/sysmacros.h>
48 #endif
49 
50 #include "bhyverun.h"
51 #include "config.h"
52 #include "debug.h"
53 #include "smbiostbl.h"
54 
55 #define	MB			(1024*1024)
56 #define	GB			(1024ULL*1024*1024)
57 
58 #define SMBIOS_BASE		0xF1000
59 
60 #define	FIRMWARE_VERSION	"14.0"
61 /* The SMBIOS specification defines the date format to be mm/dd/yyyy */
62 #define	FIRMWARE_RELEASE_DATE	"10/10/2021"
63 
64 /* BHYVE_ACPI_BASE - SMBIOS_BASE) */
65 #define	SMBIOS_MAX_LENGTH	(0xF2400 - 0xF1000)
66 
67 #define	SMBIOS_TYPE_BIOS	0
68 #define	SMBIOS_TYPE_SYSTEM	1
69 #define	SMBIOS_TYPE_BOARD	2
70 #define	SMBIOS_TYPE_CHASSIS	3
71 #define	SMBIOS_TYPE_PROCESSOR	4
72 #define	SMBIOS_TYPE_MEMARRAY	16
73 #define	SMBIOS_TYPE_MEMDEVICE	17
74 #define	SMBIOS_TYPE_MEMARRAYMAP	19
75 #define	SMBIOS_TYPE_BOOT	32
76 #define	SMBIOS_TYPE_EOT		127
77 
78 struct smbios_structure {
79 	uint8_t		type;
80 	uint8_t		length;
81 	uint16_t	handle;
82 } __packed;
83 
84 struct smbios_string {
85 	const char *node;
86 	const char *value;
87 };
88 
89 typedef int (*initializer_func_t)(const struct smbios_structure *template_entry,
90     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
91     uint16_t *n);
92 
93 struct smbios_template_entry {
94 	const struct smbios_structure	*entry;
95 	const struct smbios_string 	*strings;
96 	initializer_func_t		initializer;
97 };
98 
99 /*
100  * SMBIOS Structure Table Entry Point
101  */
102 #define	SMBIOS_ENTRY_EANCHOR	"_SM_"
103 #define	SMBIOS_ENTRY_EANCHORLEN	4
104 #define	SMBIOS_ENTRY_IANCHOR	"_DMI_"
105 #define	SMBIOS_ENTRY_IANCHORLEN	5
106 
107 struct smbios_entry_point {
108 	char		eanchor[4];	/* anchor tag */
109 	uint8_t		echecksum;	/* checksum of entry point structure */
110 	uint8_t		eplen;		/* length in bytes of entry point */
111 	uint8_t		major;		/* major version of the SMBIOS spec */
112 	uint8_t		minor;		/* minor version of the SMBIOS spec */
113 	uint16_t	maxssize;	/* maximum size in bytes of a struct */
114 	uint8_t		revision;	/* entry point structure revision */
115 	uint8_t		format[5];	/* entry point rev-specific data */
116 	char		ianchor[5];	/* intermediate anchor tag */
117 	uint8_t		ichecksum;	/* intermediate checksum */
118 	uint16_t	stlen;		/* len in bytes of structure table */
119 	uint32_t	staddr;		/* physical addr of structure table */
120 	uint16_t	stnum;		/* number of structure table entries */
121 	uint8_t		bcdrev;		/* BCD value representing DMI ver */
122 } __packed;
123 
124 /*
125  * BIOS Information
126  */
127 #define	SMBIOS_FL_ISA		0x00000010	/* ISA is supported */
128 #define	SMBIOS_FL_PCI		0x00000080	/* PCI is supported */
129 #define	SMBIOS_FL_SHADOW	0x00001000	/* BIOS shadowing is allowed */
130 #define	SMBIOS_FL_CDBOOT	0x00008000	/* Boot from CD is supported */
131 #define	SMBIOS_FL_SELBOOT	0x00010000	/* Selectable Boot supported */
132 #define	SMBIOS_FL_EDD		0x00080000	/* EDD Spec is supported */
133 
134 #define	SMBIOS_XB1_FL_ACPI	0x00000001	/* ACPI is supported */
135 
136 #define	SMBIOS_XB2_FL_BBS	0x00000001	/* BIOS Boot Specification */
137 #define	SMBIOS_XB2_FL_VM	0x00000010	/* Virtual Machine */
138 
139 struct smbios_table_type0 {
140 	struct smbios_structure	header;
141 	uint8_t			vendor;		/* vendor string */
142 	uint8_t			version;	/* version string */
143 	uint16_t		segment;	/* address segment location */
144 	uint8_t			rel_date;	/* release date */
145 	uint8_t			size;		/* rom size */
146 	uint64_t		cflags;		/* characteristics */
147 	uint8_t			xc_bytes[2];	/* characteristics ext bytes */
148 	uint8_t			sb_major_rel;	/* system bios version */
149 	uint8_t			sb_minor_rele;
150 	uint8_t			ecfw_major_rel;	/* embedded ctrl fw version */
151 	uint8_t			ecfw_minor_rel;
152 } __packed;
153 
154 /*
155  * System Information
156  */
157 #define	SMBIOS_WAKEUP_SWITCH	0x06	/* power switch */
158 
159 struct smbios_table_type1 {
160 	struct smbios_structure	header;
161 	uint8_t			manufacturer;	/* manufacturer string */
162 	uint8_t			product;	/* product name string */
163 	uint8_t			version;	/* version string */
164 	uint8_t			serial;		/* serial number string */
165 	uint8_t			uuid[16];	/* uuid byte array */
166 	uint8_t			wakeup;		/* wake-up event */
167 	uint8_t			sku;		/* sku number string */
168 	uint8_t			family;		/* family name string */
169 } __packed;
170 
171 /*
172  * Baseboard (or Module) Information
173  */
174 #define SMBIOS_BRF_HOSTING	0x1
175 #define SMBIOS_BRT_MOTHERBOARD	0xa
176 
177 struct smbios_table_type2 {
178 	struct smbios_structure	header;
179 	uint8_t			manufacturer;	/* manufacturer string */
180 	uint8_t			product;	/* product name string */
181 	uint8_t			version;	/* version string */
182 	uint8_t			serial;		/* serial number string */
183 	uint8_t			asset;		/* asset tag string */
184 	uint8_t			fflags;		/* feature flags */
185 	uint8_t			location;	/* location in chassis */
186 	uint16_t		chandle;	/* chassis handle */
187 	uint8_t			type;		/* board type */
188 	uint8_t			n_objs;		/* number of contained object handles */
189 } __packed;
190 
191 /*
192  * System Enclosure or Chassis
193  */
194 #define	SMBIOS_CHT_UNKNOWN	0x02	/* unknown */
195 #define	SMBIOS_CHT_DESKTOP	0x03	/* desktop */
196 
197 #define	SMBIOS_CHST_SAFE	0x03	/* safe */
198 
199 #define	SMBIOS_CHSC_NONE	0x03	/* none */
200 
201 struct smbios_table_type3 {
202 	struct smbios_structure	header;
203 	uint8_t			manufacturer;	/* manufacturer string */
204 	uint8_t			type;		/* type */
205 	uint8_t			version;	/* version string */
206 	uint8_t			serial;		/* serial number string */
207 	uint8_t			asset;		/* asset tag string */
208 	uint8_t			bustate;	/* boot-up state */
209 	uint8_t			psstate;	/* power supply state */
210 	uint8_t			tstate;		/* thermal state */
211 	uint8_t			security;	/* security status */
212 	uint32_t		oemdata;	/* OEM-specific data */
213 	uint8_t			uheight;	/* height in 'u's */
214 	uint8_t			cords;		/* number of power cords */
215 	uint8_t			elems;		/* number of element records */
216 	uint8_t			elemlen;	/* length of records */
217 	uint8_t			sku;		/* sku number string */
218 } __packed;
219 
220 /*
221  * Processor Information
222  */
223 #define	SMBIOS_PRT_CENTRAL	0x03	/* central processor */
224 
225 #define	SMBIOS_PRF_OTHER	0x01	/* other */
226 
227 #define	SMBIOS_PRS_PRESENT	0x40	/* socket is populated */
228 #define	SMBIOS_PRS_ENABLED	0x1	/* enabled */
229 
230 #define	SMBIOS_PRU_NONE		0x06	/* none */
231 
232 #define	SMBIOS_PFL_64B	0x04	/* 64-bit capable */
233 
234 struct smbios_table_type4 {
235 	struct smbios_structure	header;
236 	uint8_t			socket;		/* socket designation string */
237 	uint8_t			type;		/* processor type */
238 	uint8_t			family;		/* processor family */
239 	uint8_t			manufacturer;	/* manufacturer string */
240 	uint64_t		cpuid;		/* processor cpuid */
241 	uint8_t			version;	/* version string */
242 	uint8_t			voltage;	/* voltage */
243 	uint16_t		clkspeed;	/* ext clock speed in mhz */
244 	uint16_t		maxspeed;	/* maximum speed in mhz */
245 	uint16_t		curspeed;	/* current speed in mhz */
246 	uint8_t			status;		/* status */
247 	uint8_t			upgrade;	/* upgrade */
248 	uint16_t		l1handle;	/* l1 cache handle */
249 	uint16_t		l2handle;	/* l2 cache handle */
250 	uint16_t		l3handle;	/* l3 cache handle */
251 	uint8_t			serial;		/* serial number string */
252 	uint8_t			asset;		/* asset tag string */
253 	uint8_t			part;		/* part number string */
254 	uint8_t			cores;		/* cores per socket */
255 	uint8_t			ecores;		/* enabled cores */
256 	uint8_t			threads;	/* threads per socket */
257 	uint16_t		cflags;		/* processor characteristics */
258 	uint16_t		family2;	/* processor family 2 */
259 } __packed;
260 
261 /*
262  * Physical Memory Array
263  */
264 #define	SMBIOS_MAL_SYSMB	0x03	/* system board or motherboard */
265 
266 #define	SMBIOS_MAU_SYSTEM	0x03	/* system memory */
267 
268 #define	SMBIOS_MAE_NONE		0x03	/* none */
269 
270 struct smbios_table_type16 {
271 	struct smbios_structure	header;
272 	uint8_t			location;	/* physical device location */
273 	uint8_t			use;		/* device functional purpose */
274 	uint8_t			ecc;		/* err detect/correct method */
275 	uint32_t		size;		/* max mem capacity in kb */
276 	uint16_t		errhand;	/* handle of error (if any) */
277 	uint16_t		ndevs;		/* num of slots or sockets */
278 	uint64_t		xsize;		/* max mem capacity in bytes */
279 } __packed;
280 
281 /*
282  * Memory Device
283  */
284 #define	SMBIOS_MDFF_UNKNOWN	0x02	/* unknown */
285 
286 #define	SMBIOS_MDT_UNKNOWN	0x02	/* unknown */
287 
288 #define	SMBIOS_MDF_UNKNOWN	0x0004	/* unknown */
289 
290 struct smbios_table_type17 {
291 	struct smbios_structure	header;
292 	uint16_t		arrayhand;	/* handle of physl mem array */
293 	uint16_t		errhand;	/* handle of mem error data */
294 	uint16_t		twidth;		/* total width in bits */
295 	uint16_t		dwidth;		/* data width in bits */
296 	uint16_t		size;		/* size in kb or mb */
297 	uint8_t			form;		/* form factor */
298 	uint8_t			set;		/* set */
299 	uint8_t			dloc;		/* device locator string */
300 	uint8_t			bloc;		/* phys bank locator string */
301 	uint8_t			type;		/* memory type */
302 	uint16_t		flags;		/* memory characteristics */
303 	uint16_t		maxspeed;	/* maximum speed in mhz */
304 	uint8_t			manufacturer;	/* manufacturer string */
305 	uint8_t			serial;		/* serial number string */
306 	uint8_t			asset;		/* asset tag string */
307 	uint8_t			part;		/* part number string */
308 	uint8_t			attributes;	/* attributes */
309 	uint32_t		xsize;		/* extended size in mb */
310 	uint16_t		curspeed;	/* current speed in mhz */
311 	uint16_t		minvoltage;	/* minimum voltage */
312 	uint16_t		maxvoltage;	/* maximum voltage */
313 	uint16_t		curvoltage;	/* configured voltage */
314 } __packed;
315 
316 /*
317  * Memory Array Mapped Address
318  */
319 struct smbios_table_type19 {
320 	struct smbios_structure	header;
321 	uint32_t		saddr;		/* start phys addr in kb */
322 	uint32_t		eaddr;		/* end phys addr in kb */
323 	uint16_t		arrayhand;	/* physical mem array handle */
324 	uint8_t			width;		/* num of dev in row */
325 	uint64_t		xsaddr;		/* start phys addr in bytes */
326 	uint64_t		xeaddr;		/* end phys addr in bytes */
327 } __packed;
328 
329 /*
330  * System Boot Information
331  */
332 #define	SMBIOS_BOOT_NORMAL	0	/* no errors detected */
333 
334 struct smbios_table_type32 {
335 	struct smbios_structure	header;
336 	uint8_t			reserved[6];
337 	uint8_t			status;		/* boot status */
338 } __packed;
339 
340 /*
341  * End-of-Table
342  */
343 struct smbios_table_type127 {
344 	struct smbios_structure	header;
345 } __packed;
346 
347 static const struct smbios_table_type0 smbios_type0_template = {
348 	{ SMBIOS_TYPE_BIOS, sizeof (struct smbios_table_type0), 0 },
349 	1,	/* bios vendor string */
350 	2,	/* bios version string */
351 	0xF000,	/* bios address segment location */
352 	3,	/* bios release date */
353 	0x0,	/* bios size (64k * (n + 1) is the size in bytes) */
354 	SMBIOS_FL_ISA | SMBIOS_FL_PCI | SMBIOS_FL_SHADOW |
355 	    SMBIOS_FL_CDBOOT | SMBIOS_FL_EDD,
356 	{ SMBIOS_XB1_FL_ACPI, SMBIOS_XB2_FL_BBS | SMBIOS_XB2_FL_VM },
357 	0x0,	/* bios major release */
358 	0x0,	/* bios minor release */
359 	0xff,	/* embedded controller firmware major release */
360 	0xff	/* embedded controller firmware minor release */
361 };
362 
363 static const struct smbios_string smbios_type0_strings[] = {
364 	{ "bios.vendor", "BHYVE" },			/* vendor string */
365 	{ "bios.version", FIRMWARE_VERSION },		/* bios version string */
366 	{ "bios.release_date", FIRMWARE_RELEASE_DATE },	/* bios release date string */
367 	{ 0 }
368 };
369 
370 static const struct smbios_table_type1 smbios_type1_template = {
371 	{ SMBIOS_TYPE_SYSTEM, sizeof (struct smbios_table_type1), 0 },
372 	1,		/* manufacturer string */
373 	2,		/* product string */
374 	3,		/* version string */
375 	4,		/* serial number string */
376 	{ 0 },
377 	SMBIOS_WAKEUP_SWITCH,
378 	5,		/* sku string */
379 	6		/* family string */
380 };
381 
382 static int smbios_type1_initializer(const struct smbios_structure *template_entry,
383     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
384     uint16_t *n);
385 
386 static const struct smbios_string smbios_type1_strings[] = {
387 	{ "system.manufacturer", "illumos" },	     /* manufacturer string */
388 	{ "system.product_name", "BHYVE" },	     /* product string */
389 	{ "system.version", "1.0" },		     /* version string */
390 	{ "system.serial_number", "None" },	     /* serial number string */
391 	{ "system.sku", "None" },		     /* sku string */
392 	{ "system.family_name", "Virtual Machine" }, /* family string */
393 	{ 0 }
394 };
395 
396 static const struct smbios_table_type2 smbios_type2_template = {
397 	{ SMBIOS_TYPE_BOARD, sizeof (struct smbios_table_type2), 0 },
398 	1,			/* manufacturer string */
399 	2,			/* product string */
400 	3,			/* version string */
401 	4,			/* serial number string */
402 	5,			/* asset tag string */
403 	SMBIOS_BRF_HOSTING,	/* feature flags */
404 	6,			/* location string */
405 	SMBIOS_CHT_DESKTOP,	/* chassis handle */
406 	SMBIOS_BRT_MOTHERBOARD,	/* board type */
407 	0
408 };
409 
410 static const struct smbios_string smbios_type2_strings[] = {
411 	{ "board.manufacturer", "illumos" },	/* manufacturer string */
412 	{ "board.product_name", "BHYVE" },	/* product name string */
413 	{ "board.version", "1.0" },		/* version string */
414 	{ "board.serial_number", "None" },	/* serial number string */
415 	{ "board.asset_tag", "None" },		/* asset tag string */
416 	{ "board.location", "None" },		/* location string */
417 	{ 0 }
418 };
419 
420 static const struct smbios_table_type3 smbios_type3_template = {
421 	{ SMBIOS_TYPE_CHASSIS, sizeof (struct smbios_table_type3), 0 },
422 	1,		/* manufacturer string */
423 	SMBIOS_CHT_UNKNOWN,
424 	2,		/* version string */
425 	3,		/* serial number string */
426 	4,		/* asset tag string */
427 	SMBIOS_CHST_SAFE,
428 	SMBIOS_CHST_SAFE,
429 	SMBIOS_CHST_SAFE,
430 	SMBIOS_CHSC_NONE,
431 	0,		/* OEM specific data, we have none */
432 	0,		/* height in 'u's (0=enclosure height unspecified) */
433 	0,		/* number of power cords (0=number unspecified) */
434 	0,		/* number of contained element records */
435 	0,		/* length of records */
436 	5		/* sku number string */
437 };
438 
439 static const struct smbios_string smbios_type3_strings[] = {
440 	{ "chassis.manufacturer", "illumos" },	/* manufacturer string */
441 	{ "chassis.version", "1.0" },		/* version string */
442 	{ "chassis.serial_number", "None" },	/* serial number string */
443 	{ "chassis.asset_tag", "None" },	/* asset tag string */
444 	{ "chassis.sku", "None" },		/* sku number string */
445 	{ 0 }
446 };
447 
448 static const struct smbios_table_type4 smbios_type4_template = {
449 	{ SMBIOS_TYPE_PROCESSOR, sizeof (struct smbios_table_type4), 0 },
450 	1,		/* socket designation string */
451 	SMBIOS_PRT_CENTRAL,
452 	SMBIOS_PRF_OTHER,
453 	2,		/* manufacturer string */
454 	0,		/* cpuid */
455 	3,		/* version string */
456 	0,		/* voltage */
457 	0,		/* external clock frequency in mhz (0=unknown) */
458 	0,		/* maximum frequency in mhz (0=unknown) */
459 	0,		/* current frequency in mhz (0=unknown) */
460 	SMBIOS_PRS_PRESENT | SMBIOS_PRS_ENABLED,
461 	SMBIOS_PRU_NONE,
462 	-1,		/* l1 cache handle */
463 	-1,		/* l2 cache handle */
464 	-1,		/* l3 cache handle */
465 	4,		/* serial number string */
466 	5,		/* asset tag string */
467 	6,		/* part number string */
468 	0,		/* cores per socket (0=unknown) */
469 	0,		/* enabled cores per socket (0=unknown) */
470 	0,		/* threads per socket (0=unknown) */
471 	SMBIOS_PFL_64B,
472 	SMBIOS_PRF_OTHER
473 };
474 
475 static const struct smbios_string smbios_type4_strings[] = {
476 	{ NULL, " " },		/* socket designation string */
477 	{ NULL, " " },		/* manufacturer string */
478 	{ NULL, " " },		/* version string */
479 	{ NULL, "None" },	/* serial number string */
480 	{ NULL, "None" },	/* asset tag string */
481 	{ NULL, "None" },	/* part number string */
482 	{ 0 }
483 };
484 
485 static int smbios_type4_initializer(
486     const struct smbios_structure *template_entry,
487     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
488     uint16_t *n);
489 
490 static const struct smbios_table_type16 smbios_type16_template = {
491 	{ SMBIOS_TYPE_MEMARRAY, sizeof (struct smbios_table_type16),  0 },
492 	SMBIOS_MAL_SYSMB,
493 	SMBIOS_MAU_SYSTEM,
494 	SMBIOS_MAE_NONE,
495 	0x80000000,	/* max mem capacity in kb (0x80000000=use extended) */
496 	-1,		/* handle of error (if any) */
497 	0,		/* number of slots or sockets (TBD) */
498 	0		/* extended maximum memory capacity in bytes (TBD) */
499 };
500 
501 static int smbios_type16_initializer(
502     const struct smbios_structure *template_entry,
503     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
504     uint16_t *n);
505 
506 static const struct smbios_table_type17 smbios_type17_template = {
507 	{ SMBIOS_TYPE_MEMDEVICE, sizeof (struct smbios_table_type17),  0 },
508 	-1,		/* handle of physical memory array */
509 	-1,		/* handle of memory error data */
510 	64,		/* total width in bits including ecc */
511 	64,		/* data width in bits */
512 	0,		/* size in kb or mb (0x7fff=use extended)*/
513 	SMBIOS_MDFF_UNKNOWN,
514 	0,		/* set (0x00=none, 0xff=unknown) */
515 	1,		/* device locator string */
516 	2,		/* physical bank locator string */
517 	SMBIOS_MDT_UNKNOWN,
518 	SMBIOS_MDF_UNKNOWN,
519 	0,		/* maximum memory speed in mhz (0=unknown) */
520 	3,		/* manufacturer string */
521 	4,		/* serial number string */
522 	5,		/* asset tag string */
523 	6,		/* part number string */
524 	0,		/* attributes (0=unknown rank information) */
525 	0,		/* extended size in mb (TBD) */
526 	0,		/* current speed in mhz (0=unknown) */
527 	0,		/* minimum voltage in mv (0=unknown) */
528 	0,		/* maximum voltage in mv (0=unknown) */
529 	0		/* configured voltage in mv (0=unknown) */
530 };
531 
532 static const struct smbios_string smbios_type17_strings[] = {
533 	{ NULL, " " },		/* device locator string */
534 	{ NULL, " " },		/* physical bank locator string */
535 	{ NULL, " " },		/* manufacturer string */
536 	{ NULL, "None" },	/* serial number string */
537 	{ NULL, "None" },	/* asset tag string */
538 	{ NULL, "None" },	/* part number string */
539 	{ 0 }
540 };
541 
542 static int smbios_type17_initializer(
543     const struct smbios_structure *template_entry,
544     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
545     uint16_t *n);
546 
547 static const struct smbios_table_type19 smbios_type19_template = {
548 	{ SMBIOS_TYPE_MEMARRAYMAP, sizeof (struct smbios_table_type19),  0 },
549 	0xffffffff,	/* starting phys addr in kb (0xffffffff=use ext) */
550 	0xffffffff,	/* ending phys addr in kb (0xffffffff=use ext) */
551 	-1,		/* physical memory array handle */
552 	1,		/* number of devices that form a row */
553 	0,		/* extended starting phys addr in bytes (TDB) */
554 	0		/* extended ending phys addr in bytes (TDB) */
555 };
556 
557 static int smbios_type19_initializer(
558     const struct smbios_structure *template_entry,
559     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
560     uint16_t *n);
561 
562 static struct smbios_table_type32 smbios_type32_template = {
563 	{ SMBIOS_TYPE_BOOT, sizeof (struct smbios_table_type32),  0 },
564 	{ 0, 0, 0, 0, 0, 0 },
565 	SMBIOS_BOOT_NORMAL
566 };
567 
568 static const struct smbios_table_type127 smbios_type127_template = {
569 	{ SMBIOS_TYPE_EOT, sizeof (struct smbios_table_type127),  0 }
570 };
571 
572 static int smbios_generic_initializer(
573     const struct smbios_structure *template_entry,
574     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
575     uint16_t *n);
576 
577 static struct smbios_template_entry smbios_template[] = {
578 	{ (const struct smbios_structure *)&smbios_type0_template,
579 	  smbios_type0_strings,
580 	  smbios_generic_initializer },
581 	{ (const struct smbios_structure *)&smbios_type1_template,
582 	  smbios_type1_strings,
583 	  smbios_type1_initializer },
584 	{ (const struct smbios_structure *)&smbios_type2_template,
585 	  smbios_type2_strings,
586 	  smbios_generic_initializer },
587 	{ (const struct smbios_structure *)&smbios_type3_template,
588 	  smbios_type3_strings,
589 	  smbios_generic_initializer },
590 	{ (const struct smbios_structure *)&smbios_type4_template,
591 	  smbios_type4_strings,
592 	  smbios_type4_initializer },
593 	{ (const struct smbios_structure *)&smbios_type16_template,
594 	  NULL,
595 	  smbios_type16_initializer },
596 	{ (const struct smbios_structure *)&smbios_type17_template,
597 	  smbios_type17_strings,
598 	  smbios_type17_initializer },
599 	{ (const struct smbios_structure *)&smbios_type19_template,
600 	  NULL,
601 	  smbios_type19_initializer },
602 	{ (const struct smbios_structure *)&smbios_type32_template,
603 	  NULL,
604 	  smbios_generic_initializer },
605 	{ (const struct smbios_structure *)&smbios_type127_template,
606 	  NULL,
607 	  smbios_generic_initializer },
608 	{ NULL,NULL, NULL }
609 };
610 
611 static uint64_t guest_lomem, guest_himem;
612 static uint16_t type16_handle;
613 
614 static int
615 smbios_generic_initializer(const struct smbios_structure *template_entry,
616     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
617     uint16_t *n)
618 {
619 	struct smbios_structure *entry;
620 
621 	memcpy(curaddr, template_entry, template_entry->length);
622 	entry = (struct smbios_structure *)curaddr;
623 	entry->handle = *n + 1;
624 	curaddr += entry->length;
625 	if (template_strings != NULL) {
626 		int	i;
627 
628 		for (i = 0; template_strings[i].value != NULL; i++) {
629 			const char *string;
630 			int len;
631 
632 			if (template_strings[i].node == NULL) {
633 				string = template_strings[i].value;
634 			} else {
635 				set_config_value_if_unset(
636 				    template_strings[i].node,
637 				    template_strings[i].value);
638 				string = get_config_value(
639 				    template_strings[i].node);
640 			}
641 
642 			len = strlen(string) + 1;
643 			memcpy(curaddr, string, len);
644 			curaddr += len;
645 		}
646 		*curaddr = '\0';
647 		curaddr++;
648 	} else {
649 		/* Minimum string section is double nul */
650 		*curaddr = '\0';
651 		curaddr++;
652 		*curaddr = '\0';
653 		curaddr++;
654 	}
655 	(*n)++;
656 	*endaddr = curaddr;
657 
658 	return (0);
659 }
660 
661 static int
662 smbios_type1_initializer(const struct smbios_structure *template_entry,
663     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
664     uint16_t *n)
665 {
666 	struct smbios_table_type1 *type1;
667 	const char *guest_uuid_str;
668 
669 	smbios_generic_initializer(template_entry, template_strings,
670 	    curaddr, endaddr, n);
671 	type1 = (struct smbios_table_type1 *)curaddr;
672 
673 	guest_uuid_str = get_config_value("uuid");
674 	if (guest_uuid_str != NULL) {
675 		uuid_t		uuid;
676 		uint32_t	status;
677 
678 		uuid_from_string(guest_uuid_str, &uuid, &status);
679 		if (status != uuid_s_ok) {
680 			EPRINTLN("Invalid UUID");
681 			return (-1);
682 		}
683 
684 		uuid_enc_le(&type1->uuid, &uuid);
685 	} else {
686 		MD5_CTX		mdctx;
687 		u_char		digest[16];
688 		char		hostname[MAXHOSTNAMELEN];
689 		const char	*vmname;
690 
691 		/*
692 		 * Universally unique and yet reproducible are an
693 		 * oxymoron, however reproducible is desirable in
694 		 * this case.
695 		 */
696 		if (gethostname(hostname, sizeof(hostname)))
697 			return (-1);
698 
699 		MD5Init(&mdctx);
700 		vmname = get_config_value("name");
701 		MD5Update(&mdctx, vmname, strlen(vmname));
702 		MD5Update(&mdctx, hostname, sizeof(hostname));
703 		MD5Final(digest, &mdctx);
704 
705 		/*
706 		 * Set the variant and version number.
707 		 */
708 		digest[6] &= 0x0F;
709 		digest[6] |= 0x30;	/* version 3 */
710 		digest[8] &= 0x3F;
711 		digest[8] |= 0x80;
712 
713 		memcpy(&type1->uuid, digest, sizeof (digest));
714 	}
715 
716 	return (0);
717 }
718 
719 static int
720 smbios_type4_initializer(const struct smbios_structure *template_entry,
721     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
722     uint16_t *n)
723 {
724 	int i;
725 
726 	for (i = 0; i < cpu_sockets; i++) {
727 		struct smbios_table_type4 *type4;
728 		char *p;
729 		int nstrings, len;
730 
731 		smbios_generic_initializer(template_entry, template_strings,
732 		    curaddr, endaddr, n);
733 		type4 = (struct smbios_table_type4 *)curaddr;
734 		p = curaddr + sizeof (struct smbios_table_type4);
735 		nstrings = 0;
736 		while (p < *endaddr - 1) {
737 			if (*p++ == '\0')
738 				nstrings++;
739 		}
740 		len = sprintf(*endaddr - 1, "CPU #%d", i) + 1;
741 		*endaddr += len - 1;
742 		*(*endaddr) = '\0';
743 		(*endaddr)++;
744 		type4->socket = nstrings + 1;
745 		/* Revise cores and threads after update to smbios 3.0 */
746 		if (cpu_cores > 254)
747 			type4->cores = 0;
748 		else
749 			type4->cores = cpu_cores;
750 		/* This threads is total threads in a socket */
751 		if (cpu_cores * cpu_threads > 254)
752 			type4->threads = 0;
753 		else
754 			type4->threads = cpu_cores * cpu_threads;
755 		curaddr = *endaddr;
756 	}
757 
758 	return (0);
759 }
760 
761 static int
762 smbios_type16_initializer(const struct smbios_structure *template_entry,
763     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
764     uint16_t *n)
765 {
766 	struct smbios_table_type16 *type16;
767 
768 	type16_handle = *n;
769 	smbios_generic_initializer(template_entry, template_strings,
770 	    curaddr, endaddr, n);
771 	type16 = (struct smbios_table_type16 *)curaddr;
772 	type16->xsize = guest_lomem + guest_himem;
773 	type16->ndevs = guest_himem > 0 ? 2 : 1;
774 
775 	return (0);
776 }
777 
778 static int
779 smbios_type17_initializer(const struct smbios_structure *template_entry,
780     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
781     uint16_t *n)
782 {
783 	struct smbios_table_type17 *type17;
784 	uint64_t memsize, size_KB, size_MB;
785 
786 	smbios_generic_initializer(template_entry, template_strings,
787 	    curaddr, endaddr, n);
788 	type17 = (struct smbios_table_type17 *)curaddr;
789 	type17->arrayhand = type16_handle;
790 
791 	memsize = guest_lomem + guest_himem;
792 	size_KB = memsize / 1024;
793 	size_MB = memsize / MB;
794 
795 	/* A single Type 17 entry can't represent more than ~2PB RAM */
796 	if (size_MB > 0x7FFFFFFF) {
797 		printf("Warning: guest memory too big for SMBIOS Type 17 table: "
798 			"%luMB greater than max supported 2147483647MB\n", size_MB);
799 
800 		size_MB = 0x7FFFFFFF;
801 	}
802 
803 	/* See SMBIOS 2.7.0 section 7.18 - Memory Device (Type 17) */
804 	if (size_KB <= 0x7FFF) {
805 		/* Can represent up to 32767KB with the top bit set */
806 		type17->size = size_KB | (1 << 15);
807 	} else if (size_MB < 0x7FFF) {
808 		/* Can represent up to 32766MB with the top bit unset */
809 		type17->size = size_MB & 0x7FFF;
810 	} else {
811 		type17->size = 0x7FFF;
812 		/*
813 		 * Can represent up to 2147483647MB (~2PB)
814 		 * The top bit is reserved
815 		 */
816 		type17->xsize = size_MB & 0x7FFFFFFF;
817 	}
818 
819 	return (0);
820 }
821 
822 static int
823 smbios_type19_initializer(const struct smbios_structure *template_entry,
824     const struct smbios_string *template_strings, char *curaddr, char **endaddr,
825     uint16_t *n)
826 {
827 	struct smbios_table_type19 *type19;
828 
829 	smbios_generic_initializer(template_entry, template_strings,
830 	    curaddr, endaddr, n);
831 	type19 = (struct smbios_table_type19 *)curaddr;
832 	type19->arrayhand = type16_handle;
833 	type19->xsaddr = 0;
834 	type19->xeaddr = guest_lomem;
835 
836 	if (guest_himem > 0) {
837 		curaddr = *endaddr;
838 		smbios_generic_initializer(template_entry, template_strings,
839 		    curaddr, endaddr, n);
840 		type19 = (struct smbios_table_type19 *)curaddr;
841 		type19->arrayhand = type16_handle;
842 		type19->xsaddr = 4*GB;
843 		type19->xeaddr = type19->xsaddr + guest_himem;
844 	}
845 
846 	return (0);
847 }
848 
849 static void
850 smbios_ep_initializer(struct smbios_entry_point *smbios_ep, uint32_t staddr)
851 {
852 	memset(smbios_ep, 0, sizeof(*smbios_ep));
853 	memcpy(smbios_ep->eanchor, SMBIOS_ENTRY_EANCHOR,
854 	    SMBIOS_ENTRY_EANCHORLEN);
855 	smbios_ep->eplen = 0x1F;
856 	assert(sizeof (struct smbios_entry_point) == smbios_ep->eplen);
857 	smbios_ep->major = 2;
858 	smbios_ep->minor = 6;
859 	smbios_ep->revision = 0;
860 	memcpy(smbios_ep->ianchor, SMBIOS_ENTRY_IANCHOR,
861 	    SMBIOS_ENTRY_IANCHORLEN);
862 	smbios_ep->staddr = staddr;
863 	smbios_ep->bcdrev = (smbios_ep->major & 0xf) << 4 | (smbios_ep->minor & 0xf);
864 }
865 
866 static void
867 smbios_ep_finalizer(struct smbios_entry_point *smbios_ep, uint16_t len,
868     uint16_t num, uint16_t maxssize)
869 {
870 	uint8_t	checksum;
871 	int	i;
872 
873 	smbios_ep->maxssize = maxssize;
874 	smbios_ep->stlen = len;
875 	smbios_ep->stnum = num;
876 
877 	checksum = 0;
878 	for (i = 0x10; i < 0x1f; i++) {
879 		checksum -= ((uint8_t *)smbios_ep)[i];
880 	}
881 	smbios_ep->ichecksum = checksum;
882 
883 	checksum = 0;
884 	for (i = 0; i < 0x1f; i++) {
885 		checksum -= ((uint8_t *)smbios_ep)[i];
886 	}
887 	smbios_ep->echecksum = checksum;
888 }
889 
890 #ifndef __FreeBSD__
891 /*
892  * bhyve on illumos previously used configuration keys starting with 'smbios.'
893  * to control type 1 SMBIOS information. Since these may still be present in
894  * bhyve configuration files, the following table is used to translate them
895  * to their new key names.
896  */
897 static struct {
898 	const char *oldkey;
899 	const char *newkey;
900 } smbios_legacy_config_map[] = {
901 	{ "smbios.manufacturer",	"system.manufacturer" },
902 	{ "smbios.family",		"system.family_name" },
903 	{ "smbios.product",		"system.product_name" },
904 	{ "smbios.serial",		"system.serial_number" },
905 	{ "smbios.sku",			"system.sku" },
906 	{ "smbios.version",		"system.version" },
907 };
908 #endif
909 
910 int
911 smbios_build(struct vmctx *ctx)
912 {
913 	struct smbios_entry_point	*smbios_ep;
914 	uint16_t			n;
915 	uint16_t			maxssize;
916 	char				*curaddr, *startaddr, *ststartaddr;
917 	int				i;
918 	int				err;
919 
920 	guest_lomem = vm_get_lowmem_size(ctx);
921 	guest_himem = vm_get_highmem_size(ctx);
922 
923 	startaddr = paddr_guest2host(ctx, SMBIOS_BASE, SMBIOS_MAX_LENGTH);
924 	if (startaddr == NULL) {
925 		EPRINTLN("smbios table requires mapped mem");
926 		return (ENOMEM);
927 	}
928 
929 #ifndef __FreeBSD__
930 	/* Translate legacy illumos configuration keys */
931 	for (uint_t i = 0; i < ARRAY_SIZE(smbios_legacy_config_map); i++) {
932 		const char *v;
933 
934 		v = get_config_value(smbios_legacy_config_map[i].oldkey);
935 		if (v != NULL) {
936 			set_config_value_if_unset(
937 			    smbios_legacy_config_map[i].newkey, v);
938 		}
939 	}
940 #endif
941 
942 	curaddr = startaddr;
943 
944 	smbios_ep = (struct smbios_entry_point *)curaddr;
945 	smbios_ep_initializer(smbios_ep, SMBIOS_BASE +
946 	    sizeof(struct smbios_entry_point));
947 	curaddr += sizeof(struct smbios_entry_point);
948 	ststartaddr = curaddr;
949 
950 	n = 0;
951 	maxssize = 0;
952 	for (i = 0; smbios_template[i].entry != NULL; i++) {
953 		const struct smbios_structure	*entry;
954 		const struct smbios_string	*strings;
955 		initializer_func_t      initializer;
956 		char			*endaddr;
957 		size_t			size;
958 
959 		entry = smbios_template[i].entry;
960 		strings = smbios_template[i].strings;
961 		initializer = smbios_template[i].initializer;
962 
963 		err = (*initializer)(entry, strings, curaddr, &endaddr, &n);
964 		if (err != 0)
965 			return (err);
966 
967 		size = endaddr - curaddr;
968 		assert(size <= UINT16_MAX);
969 		if (size > maxssize)
970 			maxssize = (uint16_t)size;
971 		curaddr = endaddr;
972 	}
973 
974 	assert(curaddr - startaddr < SMBIOS_MAX_LENGTH);
975 	smbios_ep_finalizer(smbios_ep, curaddr - ststartaddr, n, maxssize);
976 
977 	return (0);
978 }
979 
980 #ifndef __FreeBSD__
981 static struct {
982 	uint_t type;
983 	const char *key;
984 	char *val;
985 } smbios_legacy_map[] = {
986 	{ 1, "product", "product_name" },
987 	{ 1, "serial", "serial_number" },
988 	{ 1, "family", "family_name" },
989 };
990 
991 static const struct smbios_string *smbios_tbl_map[] = {
992 	smbios_type0_strings,
993 	smbios_type1_strings,
994 	smbios_type2_strings,
995 	smbios_type3_strings,
996 };
997 
998 /*
999  * This function accepts an option of the form
1000  * 	type,[key=value][,key=value]...
1001  * and sets smbios data for the given type. Keys for type X are defined in the
1002  * smbios_typeX_strings tables above, but for type 1 there are also some
1003  * legacy values which were accepted in earlier versions of bhyve on illumos
1004  * which need to be mapped.
1005  */
1006 int
1007 smbios_parse(const char *opts)
1008 {
1009 	char *buf, *lasts, *token, *typekey = NULL;
1010 	const char *errstr;
1011 	const struct smbios_string *tbl;
1012 	nvlist_t *nvl;
1013 	uint_t i;
1014 	long type;
1015 
1016 	if ((buf = strdup(opts)) == NULL) {
1017 		(void) fprintf(stderr, "out of memory\n");
1018 		return (-1);
1019 	}
1020 
1021 	if ((token = strtok_r(buf, ",", &lasts)) == NULL) {
1022 		(void) fprintf(stderr, "too few fields\n");
1023 		goto fail;
1024 	}
1025 
1026 	type = strtonum(token, 0, 3, &errstr);
1027 	if (errstr != NULL) {
1028 		fprintf(stderr, "First token (type) is %s\n", errstr);
1029 		goto fail;
1030 	}
1031 
1032 	tbl = smbios_tbl_map[type];
1033 
1034 	/* Extract the config key for this type */
1035 	typekey = strdup(tbl[0].node);
1036 	if (typekey == NULL) {
1037 		(void) fprintf(stderr, "out of memory\n");
1038 		goto fail;
1039 	}
1040 
1041 	token = strchr(typekey, '.');
1042 	assert(token != NULL);
1043 	*token = '\0';
1044 
1045 	nvl = create_config_node(typekey);
1046 	if (nvl == NULL) {
1047 		(void) fprintf(stderr, "out of memory\n");
1048 		goto fail;
1049 	}
1050 
1051 	while ((token = strtok_r(NULL, ",", &lasts)) != NULL) {
1052 		char *val;
1053 
1054 		if ((val = strchr(token, '=')) == NULL || val[1] == '\0') {
1055 			(void) fprintf(stderr, "invalid key=value: '%s'\n",
1056 			    token);
1057 			goto fail;
1058 		}
1059 		*val++ = '\0';
1060 
1061 		/* UUID is a top-level config item, but -U takes priority */
1062 		if (strcmp(token, "uuid") == 0) {
1063 			set_config_value_if_unset(token, val);
1064 			continue;
1065 		}
1066 
1067 		/* Translate legacy keys */
1068 		for (i = 0; i < ARRAY_SIZE(smbios_legacy_map); i++) {
1069 			if (type == smbios_legacy_map[i].type &&
1070 			    strcmp(token, smbios_legacy_map[i].key) == 0) {
1071 				token = smbios_legacy_map[i].val;
1072 				break;
1073 			}
1074 		}
1075 
1076 		for (i = 0; tbl[i].value != NULL; i++) {
1077 			if (strcmp(tbl[i].node + strlen(typekey) + 1,
1078 			    token) == 0) {
1079 				/* Found match */
1080 				break;
1081 			}
1082 		}
1083 
1084 		if (tbl[i].value == NULL) {
1085 			(void) fprintf(stderr,
1086 			    "Unknown SMBIOS key %s for type %d\n", token, type);
1087 			goto fail;
1088 		}
1089 
1090 		set_config_value_node(nvl, token, val);
1091 	}
1092 
1093 	free(typekey);
1094 	return (0);
1095 
1096 fail:
1097 	free(buf);
1098 	free(typekey);
1099 	return (-1);
1100 }
1101 #endif
1102