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