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