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