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