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