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