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