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