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