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