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