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