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
71struct smbios_structure {
72	uint8_t		type;
73	uint8_t		length;
74	uint16_t	handle;
75} __packed;
76
77typedef 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
81struct 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
95struct 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
127struct 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
147struct 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
168struct 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
200struct 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
236struct 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
256struct 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 */
285struct 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
300struct 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 */
309struct smbios_table_type127 {
310	struct smbios_structure	header;
311} __packed;
312
313struct 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
329const 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
336struct 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
348static 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
352const 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
362struct 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
380const 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
389struct 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
416const 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
426static 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
430struct 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
441static 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
445struct 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
471const 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
481static 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
485struct 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
495static 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
499struct 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
505struct smbios_table_type127 smbios_type127_template = {
506	{ SMBIOS_TYPE_EOT, sizeof (struct smbios_table_type127),  0 }
507};
508
509static 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
513static 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
544static uint64_t guest_lomem, guest_himem;
545static uint16_t type16_handle;
546
547static int
548smbios_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
585static int
586smbios_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
637static int
638smbios_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
679static int
680smbios_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
696static int
697smbios_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
740static int
741smbios_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
767static void
768smbios_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
784static void
785smbios_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
808int
809smbios_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