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