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