1262744Stychon/*-
2262744Stychon * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3262744Stychon * All rights reserved.
4262744Stychon *
5262744Stychon * Redistribution and use in source and binary forms, with or without
6262744Stychon * modification, are permitted provided that the following conditions
7262744Stychon * are met:
8262744Stychon * 1. Redistributions of source code must retain the above copyright
9262744Stychon *    notice, this list of conditions and the following disclaimer.
10262744Stychon * 2. Redistributions in binary form must reproduce the above copyright
11262744Stychon *    notice, this list of conditions and the following disclaimer in the
12262744Stychon *    documentation and/or other materials provided with the distribution.
13262744Stychon *
14262744Stychon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15262744Stychon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16262744Stychon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17262744Stychon * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18262744Stychon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19262744Stychon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20262744Stychon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21262744Stychon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22262744Stychon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23262744Stychon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24262744Stychon * SUCH DAMAGE.
25262744Stychon */
26262744Stychon
27262744Stychon#include <sys/cdefs.h>
28262744Stychon__FBSDID("$FreeBSD$");
29262744Stychon
30262744Stychon#include <sys/param.h>
31262744Stychon
32262744Stychon#include <assert.h>
33262744Stychon#include <errno.h>
34262744Stychon#include <md5.h>
35262744Stychon#include <stdio.h>
36262744Stychon#include <string.h>
37262744Stychon#include <unistd.h>
38262744Stychon#include <uuid.h>
39262744Stychon
40262744Stychon#include <machine/vmm.h>
41262744Stychon#include <vmmapi.h>
42262744Stychon
43262744Stychon#include "bhyverun.h"
44262744Stychon#include "smbiostbl.h"
45262744Stychon
46262744Stychon#define	MB			(1024*1024)
47262744Stychon#define	GB			(1024ULL*1024*1024)
48262744Stychon
49262744Stychon#define SMBIOS_BASE		0xF1000
50262744Stychon
51262744Stychon/* BHYVE_ACPI_BASE - SMBIOS_BASE) */
52262744Stychon#define	SMBIOS_MAX_LENGTH	(0xF2400 - 0xF1000)
53262744Stychon
54262744Stychon#define	SMBIOS_TYPE_BIOS	0
55262744Stychon#define	SMBIOS_TYPE_SYSTEM	1
56262744Stychon#define	SMBIOS_TYPE_CHASSIS	3
57262744Stychon#define	SMBIOS_TYPE_PROCESSOR	4
58262744Stychon#define	SMBIOS_TYPE_MEMARRAY	16
59262744Stychon#define	SMBIOS_TYPE_MEMDEVICE	17
60262744Stychon#define	SMBIOS_TYPE_MEMARRAYMAP	19
61262744Stychon#define	SMBIOS_TYPE_BOOT	32
62262744Stychon#define	SMBIOS_TYPE_EOT		127
63262744Stychon
64262744Stychonstruct smbios_structure {
65262744Stychon	uint8_t		type;
66262744Stychon	uint8_t		length;
67262744Stychon	uint16_t	handle;
68262744Stychon} __packed;
69262744Stychon
70262744Stychontypedef int (*initializer_func_t)(struct smbios_structure *template_entry,
71262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
72262744Stychon    uint16_t *n, uint16_t *size);
73262744Stychon
74262744Stychonstruct smbios_template_entry {
75262744Stychon	struct smbios_structure	*entry;
76262744Stychon	const char		**strings;
77262744Stychon	initializer_func_t	initializer;
78262744Stychon};
79262744Stychon
80262744Stychon/*
81262744Stychon * SMBIOS Structure Table Entry Point
82262744Stychon */
83262744Stychon#define	SMBIOS_ENTRY_EANCHOR	"_SM_"
84262744Stychon#define	SMBIOS_ENTRY_EANCHORLEN	4
85262744Stychon#define	SMBIOS_ENTRY_IANCHOR	"_DMI_"
86262744Stychon#define	SMBIOS_ENTRY_IANCHORLEN	5
87262744Stychon
88262744Stychonstruct smbios_entry_point {
89262744Stychon	char		eanchor[4];	/* anchor tag */
90262744Stychon	uint8_t		echecksum;	/* checksum of entry point structure */
91262744Stychon	uint8_t		eplen;		/* length in bytes of entry point */
92262744Stychon	uint8_t		major;		/* major version of the SMBIOS spec */
93262744Stychon	uint8_t		minor;		/* minor version of the SMBIOS spec */
94262744Stychon	uint16_t	maxssize;	/* maximum size in bytes of a struct */
95262744Stychon	uint8_t		revision;	/* entry point structure revision */
96262744Stychon	uint8_t		format[5];	/* entry point rev-specific data */
97262744Stychon	char		ianchor[5];	/* intermediate anchor tag */
98262744Stychon	uint8_t		ichecksum;	/* intermediate checksum */
99262744Stychon	uint16_t	stlen;		/* len in bytes of structure table */
100262744Stychon	uint32_t	staddr;		/* physical addr of structure table */
101262744Stychon	uint16_t	stnum;		/* number of structure table entries */
102262744Stychon	uint8_t		bcdrev;		/* BCD value representing DMI ver */
103262744Stychon} __packed;
104262744Stychon
105262744Stychon/*
106262744Stychon * BIOS Information
107262744Stychon */
108262744Stychon#define	SMBIOS_FL_ISA		0x00000010	/* ISA is supported */
109262744Stychon#define	SMBIOS_FL_PCI		0x00000080	/* PCI is supported */
110262744Stychon#define	SMBIOS_FL_SHADOW	0x00001000	/* BIOS shadowing is allowed */
111262744Stychon#define	SMBIOS_FL_CDBOOT	0x00008000	/* Boot from CD is supported */
112262744Stychon#define	SMBIOS_FL_SELBOOT	0x00010000	/* Selectable Boot supported */
113262744Stychon#define	SMBIOS_FL_EDD		0x00080000	/* EDD Spec is supported */
114262744Stychon
115262744Stychon#define	SMBIOS_XB1_FL_ACPI	0x00000001	/* ACPI is supported */
116262744Stychon
117262744Stychon#define	SMBIOS_XB2_FL_BBS	0x00000001	/* BIOS Boot Specification */
118262744Stychon#define	SMBIOS_XB2_FL_VM	0x00000010	/* Virtual Machine */
119262744Stychon
120262744Stychonstruct smbios_table_type0 {
121262744Stychon	struct smbios_structure	header;
122262744Stychon	uint8_t			vendor;		/* vendor string */
123262744Stychon	uint8_t			version;	/* version string */
124262744Stychon	uint16_t		segment;	/* address segment location */
125262744Stychon	uint8_t			rel_date;	/* release date */
126262744Stychon	uint8_t			size;		/* rom size */
127262744Stychon	uint64_t		cflags;		/* characteristics */
128262744Stychon	uint8_t			xc_bytes[2];	/* characteristics ext bytes */
129262744Stychon	uint8_t			sb_major_rel;	/* system bios version */
130262744Stychon	uint8_t			sb_minor_rele;
131262744Stychon	uint8_t			ecfw_major_rel;	/* embedded ctrl fw version */
132262744Stychon	uint8_t			ecfw_minor_rel;
133262744Stychon} __packed;
134262744Stychon
135262744Stychon/*
136262744Stychon * System Information
137262744Stychon */
138262744Stychon#define	SMBIOS_WAKEUP_SWITCH	0x06	/* power switch */
139262744Stychon
140262744Stychonstruct smbios_table_type1 {
141262744Stychon	struct smbios_structure	header;
142262744Stychon	uint8_t			manufacturer;	/* manufacturer string */
143262744Stychon	uint8_t			product;	/* product name string */
144262744Stychon	uint8_t			version;	/* version string */
145262744Stychon	uint8_t			serial;		/* serial number string */
146262744Stychon	uint8_t			uuid[16];	/* uuid byte array */
147262744Stychon	uint8_t			wakeup;		/* wake-up event */
148262744Stychon	uint8_t			sku;		/* sku number string */
149262744Stychon	uint8_t			family;		/* family name string */
150262744Stychon} __packed;
151262744Stychon
152262744Stychon/*
153262744Stychon * System Enclosure or Chassis
154262744Stychon */
155262744Stychon#define	SMBIOS_CHT_UNKNOWN	0x02	/* unknown */
156262744Stychon
157262744Stychon#define	SMBIOS_CHST_SAFE	0x03	/* safe */
158262744Stychon
159262744Stychon#define	SMBIOS_CHSC_NONE	0x03	/* none */
160262744Stychon
161262744Stychonstruct smbios_table_type3 {
162262744Stychon	struct smbios_structure	header;
163262744Stychon	uint8_t			manufacturer;	/* manufacturer string */
164262744Stychon	uint8_t			type;		/* type */
165262744Stychon	uint8_t			version;	/* version string */
166262744Stychon	uint8_t			serial;		/* serial number string */
167262744Stychon	uint8_t			asset;		/* asset tag string */
168262744Stychon	uint8_t			bustate;	/* boot-up state */
169262744Stychon	uint8_t			psstate;	/* power supply state */
170262744Stychon	uint8_t			tstate;		/* thermal state */
171262744Stychon	uint8_t			security;	/* security status */
172262744Stychon	uint8_t			uheight;	/* height in 'u's */
173262744Stychon	uint8_t			cords;		/* number of power cords */
174262744Stychon	uint8_t			elems;		/* number of element records */
175262744Stychon	uint8_t			elemlen;	/* length of records */
176262744Stychon	uint8_t			sku;		/* sku number string */
177262744Stychon} __packed;
178262744Stychon
179262744Stychon/*
180262744Stychon * Processor Information
181262744Stychon */
182262744Stychon#define	SMBIOS_PRT_CENTRAL	0x03	/* central processor */
183262744Stychon
184262744Stychon#define	SMBIOS_PRF_OTHER	0x01	/* other */
185262744Stychon
186262744Stychon#define	SMBIOS_PRS_PRESENT	0x40	/* socket is populated */
187262744Stychon#define	SMBIOS_PRS_ENABLED	0x1	/* enabled */
188262744Stychon
189262744Stychon#define	SMBIOS_PRU_NONE		0x06	/* none */
190262744Stychon
191262744Stychon#define	SMBIOS_PFL_64B	0x04	/* 64-bit capable */
192262744Stychon
193262744Stychonstruct smbios_table_type4 {
194262744Stychon	struct smbios_structure	header;
195262744Stychon	uint8_t			socket;		/* socket designation string */
196262744Stychon	uint8_t			type;		/* processor type */
197262744Stychon	uint8_t			family;		/* processor family */
198262744Stychon	uint8_t			manufacturer;	/* manufacturer string */
199262744Stychon	uint64_t		cpuid;		/* processor cpuid */
200262744Stychon	uint8_t			version;	/* version string */
201262744Stychon	uint8_t			voltage;	/* voltage */
202262744Stychon	uint16_t		clkspeed;	/* ext clock speed in mhz */
203262744Stychon	uint16_t		maxspeed;	/* maximum speed in mhz */
204262744Stychon	uint16_t		curspeed;	/* current speed in mhz */
205262744Stychon	uint8_t			status;		/* status */
206262744Stychon	uint8_t			upgrade;	/* upgrade */
207262744Stychon	uint16_t		l1handle;	/* l1 cache handle */
208262744Stychon	uint16_t		l2handle;	/* l2 cache handle */
209262744Stychon	uint16_t		l3handle;	/* l3 cache handle */
210262744Stychon	uint8_t			serial;		/* serial number string */
211262744Stychon	uint8_t			asset;		/* asset tag string */
212262744Stychon	uint8_t			part;		/* part number string */
213262744Stychon	uint8_t			cores;		/* cores per socket */
214262744Stychon	uint8_t			ecores;		/* enabled cores */
215262744Stychon	uint8_t			threads;	/* threads per socket */
216262744Stychon	uint16_t		cflags;		/* processor characteristics */
217262744Stychon	uint16_t		family2;	/* processor family 2 */
218262744Stychon} __packed;
219262744Stychon
220262744Stychon/*
221262744Stychon * Physical Memory Array
222262744Stychon */
223262744Stychon#define	SMBIOS_MAL_SYSMB	0x03	/* system board or motherboard */
224262744Stychon
225262744Stychon#define	SMBIOS_MAU_SYSTEM	0x03	/* system memory */
226262744Stychon
227262744Stychon#define	SMBIOS_MAE_NONE		0x03	/* none */
228262744Stychon
229262744Stychonstruct smbios_table_type16 {
230262744Stychon	struct smbios_structure	header;
231262744Stychon	uint8_t			location;	/* physical device location */
232262744Stychon	uint8_t			use;		/* device functional purpose */
233262744Stychon	uint8_t			ecc;		/* err detect/correct method */
234262744Stychon	uint32_t		size;		/* max mem capacity in kb */
235262744Stychon	uint16_t		errhand;	/* handle of error (if any) */
236262744Stychon	uint16_t		ndevs;		/* num of slots or sockets */
237262744Stychon	uint64_t		xsize;		/* max mem capacity in bytes */
238262744Stychon} __packed;
239262744Stychon
240262744Stychon/*
241262744Stychon * Memory Device
242262744Stychon */
243262744Stychon#define	SMBIOS_MDFF_UNKNOWN	0x02	/* unknown */
244262744Stychon
245262744Stychon#define	SMBIOS_MDT_UNKNOWN	0x02	/* unknown */
246262744Stychon
247262744Stychon#define	SMBIOS_MDF_UNKNOWN	0x0004	/* unknown */
248262744Stychon
249262744Stychonstruct smbios_table_type17 {
250262744Stychon	struct smbios_structure	header;
251262744Stychon	uint16_t		arrayhand;	/* handle of physl mem array */
252262744Stychon	uint16_t		errhand;	/* handle of mem error data */
253262744Stychon	uint16_t		twidth;		/* total width in bits */
254262744Stychon	uint16_t		dwidth;		/* data width in bits */
255262744Stychon	uint16_t		size;		/* size in bytes */
256262744Stychon	uint8_t			form;		/* form factor */
257262744Stychon	uint8_t			set;		/* set */
258262744Stychon	uint8_t			dloc;		/* device locator string */
259262744Stychon	uint8_t			bloc;		/* phys bank locator string */
260262744Stychon	uint8_t			type;		/* memory type */
261262744Stychon	uint16_t		flags;		/* memory characteristics */
262262744Stychon	uint16_t		maxspeed;	/* maximum speed in mhz */
263262744Stychon	uint8_t			manufacturer;	/* manufacturer string */
264262744Stychon	uint8_t			serial;		/* serial number string */
265262744Stychon	uint8_t			asset;		/* asset tag string */
266262744Stychon	uint8_t			part;		/* part number string */
267262744Stychon	uint8_t			attributes;	/* attributes */
268262744Stychon	uint32_t		xsize;		/* extended size in mbs */
269262744Stychon	uint16_t		curspeed;	/* current speed in mhz */
270262744Stychon	uint16_t		minvoltage;	/* minimum voltage */
271262744Stychon	uint16_t		maxvoltage;	/* maximum voltage */
272262744Stychon	uint16_t		curvoltage;	/* configured voltage */
273262744Stychon} __packed;
274262744Stychon
275262744Stychon/*
276262744Stychon * Memory Array Mapped Address
277262744Stychon */
278262744Stychonstruct smbios_table_type19 {
279262744Stychon	struct smbios_structure	header;
280262744Stychon	uint32_t		saddr;		/* start phys addr in kb */
281262744Stychon	uint32_t		eaddr;		/* end phys addr in kb */
282262744Stychon	uint16_t		arrayhand;	/* physical mem array handle */
283262744Stychon	uint8_t			width;		/* num of dev in row */
284262744Stychon	uint64_t		xsaddr;		/* start phys addr in bytes */
285262744Stychon	uint64_t		xeaddr;		/* end phys addr in bytes */
286262744Stychon} __packed;
287262744Stychon
288262744Stychon/*
289262744Stychon * System Boot Information
290262744Stychon */
291262744Stychon#define	SMBIOS_BOOT_NORMAL	0	/* no errors detected */
292262744Stychon
293262744Stychonstruct smbios_table_type32 {
294262744Stychon	struct smbios_structure	header;
295262744Stychon	uint8_t			reserved[6];
296262744Stychon	uint8_t			status;		/* boot status */
297262744Stychon} __packed;
298262744Stychon
299262744Stychon/*
300262744Stychon * End-of-Table
301262744Stychon */
302262744Stychonstruct smbios_table_type127 {
303262744Stychon	struct smbios_structure	header;
304262744Stychon} __packed;
305262744Stychon
306262744Stychonstruct smbios_table_type0 smbios_type0_template = {
307262744Stychon	{ SMBIOS_TYPE_BIOS, sizeof (struct smbios_table_type0), 0 },
308262744Stychon	1,	/* bios vendor string */
309262744Stychon	2,	/* bios version string */
310262744Stychon	0xF000,	/* bios address segment location */
311262744Stychon	3,	/* bios release date */
312262744Stychon	0x0,	/* bios size (64k * (n + 1) is the size in bytes) */
313262744Stychon	SMBIOS_FL_ISA | SMBIOS_FL_PCI | SMBIOS_FL_SHADOW |
314262744Stychon	    SMBIOS_FL_CDBOOT | SMBIOS_FL_EDD,
315262744Stychon	{ SMBIOS_XB1_FL_ACPI, SMBIOS_XB2_FL_BBS | SMBIOS_XB2_FL_VM },
316262744Stychon	0x0,	/* bios major release */
317262744Stychon	0x0,	/* bios minor release */
318262744Stychon	0xff,	/* embedded controller firmware major release */
319262744Stychon	0xff	/* embedded controller firmware minor release */
320262744Stychon};
321262744Stychon
322262744Stychonconst char *smbios_type0_strings[] = {
323262744Stychon	"BHYVE",	/* vendor string */
324270159Sgrehan	"1.00",		/* bios version string */
325270159Sgrehan	"03/14/2014",	/* bios release date string */
326262744Stychon	NULL
327262744Stychon};
328262744Stychon
329262744Stychonstruct smbios_table_type1 smbios_type1_template = {
330262744Stychon	{ SMBIOS_TYPE_SYSTEM, sizeof (struct smbios_table_type1), 0 },
331262744Stychon	1,		/* manufacturer string */
332262744Stychon	2,		/* product string */
333262744Stychon	3,		/* version string */
334262744Stychon	4,		/* serial number string */
335262744Stychon	{ 0 },
336262744Stychon	SMBIOS_WAKEUP_SWITCH,
337262744Stychon	5,		/* sku string */
338262744Stychon	6		/* family string */
339262744Stychon};
340262744Stychon
341262744Stychonstatic int smbios_type1_initializer(struct smbios_structure *template_entry,
342262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
343262744Stychon    uint16_t *n, uint16_t *size);
344262744Stychon
345262744Stychonconst char *smbios_type1_strings[] = {
346262744Stychon	" ",		/* manufacturer string */
347262744Stychon	"BHYVE",	/* product name string */
348262744Stychon	"1.0",		/* version string */
349262744Stychon	"None",		/* serial number string */
350262744Stychon	"None",		/* sku string */
351262744Stychon	" ",		/* family name string */
352262744Stychon	NULL
353262744Stychon};
354262744Stychon
355262744Stychonstruct smbios_table_type3 smbios_type3_template = {
356262744Stychon	{ SMBIOS_TYPE_CHASSIS, sizeof (struct smbios_table_type3), 0 },
357262744Stychon	1,		/* manufacturer string */
358262744Stychon	SMBIOS_CHT_UNKNOWN,
359262744Stychon	2,		/* version string */
360262744Stychon	3,		/* serial number string */
361262744Stychon	4,		/* asset tag string */
362262744Stychon	SMBIOS_CHST_SAFE,
363262744Stychon	SMBIOS_CHST_SAFE,
364262744Stychon	SMBIOS_CHST_SAFE,
365262744Stychon	SMBIOS_CHSC_NONE,
366262744Stychon	0,		/* height in 'u's (0=enclosure height unspecified) */
367262744Stychon	0,		/* number of power cords (0=number unspecified) */
368262744Stychon	0,		/* number of contained element records */
369262744Stychon	0,		/* length of records */
370262744Stychon	5		/* sku number string */
371262744Stychon};
372262744Stychon
373262744Stychonconst char *smbios_type3_strings[] = {
374262744Stychon	" ",		/* manufacturer string */
375262744Stychon	"1.0",		/* version string */
376262744Stychon	"None",		/* serial number string */
377262744Stychon	"None",		/* asset tag string */
378262744Stychon	"None",		/* sku number string */
379262744Stychon	NULL
380262744Stychon};
381262744Stychon
382262744Stychonstruct smbios_table_type4 smbios_type4_template = {
383262744Stychon	{ SMBIOS_TYPE_PROCESSOR, sizeof (struct smbios_table_type4), 0 },
384262744Stychon	1,		/* socket designation string */
385262744Stychon	SMBIOS_PRT_CENTRAL,
386262744Stychon	SMBIOS_PRF_OTHER,
387262744Stychon	2,		/* manufacturer string */
388262744Stychon	0,		/* cpuid */
389262744Stychon	3,		/* version string */
390262744Stychon	0,		/* voltage */
391262744Stychon	0,		/* external clock frequency in mhz (0=unknown) */
392262744Stychon	0,		/* maximum frequency in mhz (0=unknown) */
393262744Stychon	0,		/* current frequency in mhz (0=unknown) */
394262744Stychon	SMBIOS_PRS_PRESENT | SMBIOS_PRS_ENABLED,
395262744Stychon	SMBIOS_PRU_NONE,
396262744Stychon	-1,		/* l1 cache handle */
397262744Stychon	-1,		/* l2 cache handle */
398262744Stychon	-1,		/* l3 cache handle */
399262744Stychon	4,		/* serial number string */
400262744Stychon	5,		/* asset tag string */
401262744Stychon	6,		/* part number string */
402262744Stychon	0,		/* cores per socket (0=unknown) */
403262744Stychon	0,		/* enabled cores per socket (0=unknown) */
404262744Stychon	0,		/* threads per socket (0=unknown) */
405262744Stychon	SMBIOS_PFL_64B,
406262744Stychon	SMBIOS_PRF_OTHER
407262744Stychon};
408262744Stychon
409262744Stychonconst char *smbios_type4_strings[] = {
410262744Stychon	" ",		/* socket designation string */
411262744Stychon	" ",		/* manufacturer string */
412262744Stychon	" ",		/* version string */
413262744Stychon	"None",		/* serial number string */
414262744Stychon	"None",		/* asset tag string */
415262744Stychon	"None",		/* part number string */
416262744Stychon	NULL
417262744Stychon};
418262744Stychon
419262744Stychonstatic int smbios_type4_initializer(struct smbios_structure *template_entry,
420262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
421262744Stychon    uint16_t *n, uint16_t *size);
422262744Stychon
423262744Stychonstruct smbios_table_type16 smbios_type16_template = {
424262744Stychon	{ SMBIOS_TYPE_MEMARRAY, sizeof (struct smbios_table_type16),  0 },
425262744Stychon	SMBIOS_MAL_SYSMB,
426262744Stychon	SMBIOS_MAU_SYSTEM,
427262744Stychon	SMBIOS_MAE_NONE,
428262744Stychon	0x80000000,	/* max mem capacity in kb (0x80000000=use extended) */
429262744Stychon	-1,		/* handle of error (if any) */
430262744Stychon	0,		/* number of slots or sockets (TBD) */
431262744Stychon	0		/* extended maximum memory capacity in bytes (TBD) */
432262744Stychon};
433262744Stychon
434262744Stychonstatic int smbios_type16_initializer(struct smbios_structure *template_entry,
435262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
436262744Stychon    uint16_t *n, uint16_t *size);
437262744Stychon
438262744Stychonstruct smbios_table_type17 smbios_type17_template = {
439262744Stychon	{ SMBIOS_TYPE_MEMDEVICE, sizeof (struct smbios_table_type17),  0 },
440262744Stychon	-1,		/* handle of physical memory array */
441262744Stychon	-1,		/* handle of memory error data */
442262744Stychon	64,		/* total width in bits including ecc */
443262744Stychon	64,		/* data width in bits */
444262744Stychon	0x7fff,		/* size in bytes (0x7fff=use extended)*/
445262744Stychon	SMBIOS_MDFF_UNKNOWN,
446262744Stychon	0,		/* set (0x00=none, 0xff=unknown) */
447262744Stychon	1,		/* device locator string */
448262744Stychon	2,		/* physical bank locator string */
449262744Stychon	SMBIOS_MDT_UNKNOWN,
450262744Stychon	SMBIOS_MDF_UNKNOWN,
451262744Stychon	0,		/* maximum memory speed in mhz (0=unknown) */
452262744Stychon	3,		/* manufacturer string */
453262744Stychon	4,		/* serial number string */
454262744Stychon	5,		/* asset tag string */
455262744Stychon	6,		/* part number string */
456262744Stychon	0,		/* attributes (0=unknown rank information) */
457262744Stychon	0,		/* extended size in mb (TBD) */
458262744Stychon	0,		/* current speed in mhz (0=unknown) */
459262744Stychon	0,		/* minimum voltage in mv (0=unknown) */
460262744Stychon	0,		/* maximum voltage in mv (0=unknown) */
461262744Stychon	0		/* configured voltage in mv (0=unknown) */
462262744Stychon};
463262744Stychon
464262744Stychonconst char *smbios_type17_strings[] = {
465262744Stychon	" ",		/* device locator string */
466262744Stychon	" ",		/* physical bank locator string */
467262744Stychon	" ",		/* manufacturer string */
468262744Stychon	"None",		/* serial number string */
469262744Stychon	"None",		/* asset tag string */
470262744Stychon	"None",		/* part number string */
471262744Stychon	NULL
472262744Stychon};
473262744Stychon
474262744Stychonstatic int smbios_type17_initializer(struct smbios_structure *template_entry,
475262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
476262744Stychon    uint16_t *n, uint16_t *size);
477262744Stychon
478262744Stychonstruct smbios_table_type19 smbios_type19_template = {
479262744Stychon	{ SMBIOS_TYPE_MEMARRAYMAP, sizeof (struct smbios_table_type19),  0 },
480262744Stychon	0xffffffff,	/* starting phys addr in kb (0xffffffff=use ext) */
481262744Stychon	0xffffffff,	/* ending phys addr in kb (0xffffffff=use ext) */
482262744Stychon	-1,		/* physical memory array handle */
483262744Stychon	1,		/* number of devices that form a row */
484262744Stychon	0,		/* extended starting phys addr in bytes (TDB) */
485262744Stychon	0		/* extended ending phys addr in bytes (TDB) */
486262744Stychon};
487262744Stychon
488262744Stychonstatic int smbios_type19_initializer(struct smbios_structure *template_entry,
489262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
490262744Stychon    uint16_t *n, uint16_t *size);
491262744Stychon
492262744Stychonstruct smbios_table_type32 smbios_type32_template = {
493262744Stychon	{ SMBIOS_TYPE_BOOT, sizeof (struct smbios_table_type32),  0 },
494262744Stychon	{ 0, 0, 0, 0, 0, 0 },
495262744Stychon	SMBIOS_BOOT_NORMAL
496262744Stychon};
497262744Stychon
498262744Stychonstruct smbios_table_type127 smbios_type127_template = {
499262744Stychon	{ SMBIOS_TYPE_EOT, sizeof (struct smbios_table_type127),  0 }
500262744Stychon};
501262744Stychon
502262744Stychonstatic int smbios_generic_initializer(struct smbios_structure *template_entry,
503262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
504262744Stychon    uint16_t *n, uint16_t *size);
505262744Stychon
506262744Stychonstatic struct smbios_template_entry smbios_template[] = {
507262744Stychon	{ (struct smbios_structure *)&smbios_type0_template,
508262744Stychon	  smbios_type0_strings,
509262744Stychon	  smbios_generic_initializer },
510262744Stychon	{ (struct smbios_structure *)&smbios_type1_template,
511262744Stychon	  smbios_type1_strings,
512262744Stychon	  smbios_type1_initializer },
513262744Stychon	{ (struct smbios_structure *)&smbios_type3_template,
514262744Stychon	  smbios_type3_strings,
515262744Stychon	  smbios_generic_initializer },
516262744Stychon	{ (struct smbios_structure *)&smbios_type4_template,
517262744Stychon	  smbios_type4_strings,
518262744Stychon	  smbios_type4_initializer },
519262744Stychon	{ (struct smbios_structure *)&smbios_type16_template,
520262744Stychon	  NULL,
521262744Stychon	  smbios_type16_initializer },
522262744Stychon	{ (struct smbios_structure *)&smbios_type17_template,
523262744Stychon	  smbios_type17_strings,
524262744Stychon	  smbios_type17_initializer },
525262744Stychon	{ (struct smbios_structure *)&smbios_type19_template,
526262744Stychon	  NULL,
527262744Stychon	  smbios_type19_initializer },
528262744Stychon	{ (struct smbios_structure *)&smbios_type32_template,
529262744Stychon	  NULL,
530262744Stychon	  smbios_generic_initializer },
531262744Stychon	{ (struct smbios_structure *)&smbios_type127_template,
532262744Stychon	  NULL,
533262744Stychon	  smbios_generic_initializer },
534262744Stychon	{ NULL,NULL, NULL }
535262744Stychon};
536262744Stychon
537262744Stychonstatic uint64_t guest_lomem, guest_himem;
538262744Stychonstatic uint16_t type16_handle;
539262744Stychon
540262744Stychonstatic int
541262744Stychonsmbios_generic_initializer(struct smbios_structure *template_entry,
542262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
543262744Stychon    uint16_t *n, uint16_t *size)
544262744Stychon{
545262744Stychon	struct smbios_structure *entry;
546262744Stychon
547262744Stychon	memcpy(curaddr, template_entry, template_entry->length);
548262744Stychon	entry = (struct smbios_structure *)curaddr;
549262744Stychon	entry->handle = *n + 1;
550262744Stychon	curaddr += entry->length;
551262744Stychon	if (template_strings != NULL) {
552262744Stychon		int	i;
553262744Stychon
554262744Stychon		for (i = 0; template_strings[i] != NULL; i++) {
555262744Stychon			const char *string;
556262744Stychon			int len;
557262744Stychon
558262744Stychon			string = template_strings[i];
559262744Stychon			len = strlen(string) + 1;
560262744Stychon			memcpy(curaddr, string, len);
561262744Stychon			curaddr += len;
562262744Stychon		}
563262744Stychon		*curaddr = '\0';
564262744Stychon		curaddr++;
565262744Stychon	} else {
566262744Stychon		/* Minimum string section is double nul */
567262744Stychon		*curaddr = '\0';
568262744Stychon		curaddr++;
569262744Stychon		*curaddr = '\0';
570262744Stychon		curaddr++;
571262744Stychon	}
572262744Stychon	(*n)++;
573262744Stychon	*endaddr = curaddr;
574262744Stychon
575262744Stychon	return (0);
576262744Stychon}
577262744Stychon
578262744Stychonstatic int
579262744Stychonsmbios_type1_initializer(struct smbios_structure *template_entry,
580262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
581262744Stychon    uint16_t *n, uint16_t *size)
582262744Stychon{
583262744Stychon	struct smbios_table_type1 *type1;
584262744Stychon
585262744Stychon	smbios_generic_initializer(template_entry, template_strings,
586262744Stychon	    curaddr, endaddr, n, size);
587262744Stychon	type1 = (struct smbios_table_type1 *)curaddr;
588262744Stychon
589262744Stychon	if (guest_uuid_str != NULL) {
590262744Stychon		uuid_t		uuid;
591262744Stychon		uint32_t	status;
592262744Stychon
593262744Stychon		uuid_from_string(guest_uuid_str, &uuid, &status);
594262744Stychon		if (status != uuid_s_ok)
595262744Stychon			return (-1);
596262744Stychon
597262744Stychon		uuid_enc_le(&type1->uuid, &uuid);
598262744Stychon	} else {
599262744Stychon		MD5_CTX		mdctx;
600262744Stychon		u_char		digest[16];
601262744Stychon		char		hostname[MAXHOSTNAMELEN];
602262744Stychon
603262744Stychon		/*
604262744Stychon		 * Universally unique and yet reproducible are an
605262744Stychon		 * oxymoron, however reproducible is desirable in
606262744Stychon		 * this case.
607262744Stychon		 */
608262744Stychon		if (gethostname(hostname, sizeof(hostname)))
609262744Stychon			return (-1);
610262744Stychon
611262744Stychon		MD5Init(&mdctx);
612262744Stychon		MD5Update(&mdctx, vmname, strlen(vmname));
613262744Stychon		MD5Update(&mdctx, hostname, sizeof(hostname));
614262744Stychon		MD5Final(digest, &mdctx);
615262744Stychon
616262744Stychon		/*
617262744Stychon		 * Set the variant and version number.
618262744Stychon		 */
619262744Stychon		digest[6] &= 0x0F;
620262744Stychon		digest[6] |= 0x30;	/* version 3 */
621262744Stychon		digest[8] &= 0x3F;
622262744Stychon		digest[8] |= 0x80;
623262744Stychon
624262744Stychon		memcpy(&type1->uuid, digest, sizeof (digest));
625262744Stychon	}
626262744Stychon
627262744Stychon	return (0);
628262744Stychon}
629262744Stychon
630262744Stychonstatic int
631262744Stychonsmbios_type4_initializer(struct smbios_structure *template_entry,
632262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
633262744Stychon    uint16_t *n, uint16_t *size)
634262744Stychon{
635262744Stychon	int i;
636262744Stychon
637262744Stychon	for (i = 0; i < guest_ncpus; i++) {
638262744Stychon		struct smbios_table_type4 *type4;
639262744Stychon		char *p;
640262744Stychon		int nstrings, len;
641262744Stychon
642262744Stychon		smbios_generic_initializer(template_entry, template_strings,
643262744Stychon		    curaddr, endaddr, n, size);
644262744Stychon		type4 = (struct smbios_table_type4 *)curaddr;
645262744Stychon		p = curaddr + sizeof (struct smbios_table_type4);
646262744Stychon		nstrings = 0;
647262744Stychon		while (p < *endaddr - 1) {
648262744Stychon			if (*p++ == '\0')
649262744Stychon				nstrings++;
650262744Stychon		}
651262744Stychon		len = sprintf(*endaddr - 1, "CPU #%d", i) + 1;
652262744Stychon		*endaddr += len - 1;
653262744Stychon		*(*endaddr) = '\0';
654262744Stychon		(*endaddr)++;
655262744Stychon		type4->socket = nstrings + 1;
656262744Stychon		curaddr = *endaddr;
657262744Stychon	}
658262744Stychon
659262744Stychon	return (0);
660262744Stychon}
661262744Stychon
662262744Stychonstatic int
663262744Stychonsmbios_type16_initializer(struct smbios_structure *template_entry,
664262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
665262744Stychon    uint16_t *n, uint16_t *size)
666262744Stychon{
667262744Stychon	struct smbios_table_type16 *type16;
668262744Stychon
669262744Stychon	type16_handle = *n;
670262744Stychon	smbios_generic_initializer(template_entry, template_strings,
671262744Stychon	    curaddr, endaddr, n, size);
672262744Stychon	type16 = (struct smbios_table_type16 *)curaddr;
673262744Stychon	type16->xsize = guest_lomem + guest_himem;
674262744Stychon	type16->ndevs = guest_himem > 0 ? 2 : 1;
675262744Stychon
676262744Stychon	return (0);
677262744Stychon}
678262744Stychon
679262744Stychonstatic int
680262744Stychonsmbios_type17_initializer(struct smbios_structure *template_entry,
681262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
682262744Stychon    uint16_t *n, uint16_t *size)
683262744Stychon{
684262744Stychon	struct smbios_table_type17 *type17;
685262744Stychon
686262744Stychon	smbios_generic_initializer(template_entry, template_strings,
687262744Stychon	    curaddr, endaddr, n, size);
688262744Stychon	type17 = (struct smbios_table_type17 *)curaddr;
689262744Stychon	type17->arrayhand = type16_handle;
690262744Stychon	type17->xsize = guest_lomem;
691262744Stychon
692262744Stychon	if (guest_himem > 0) {
693262744Stychon		curaddr = *endaddr;
694262744Stychon		smbios_generic_initializer(template_entry, template_strings,
695262744Stychon		    curaddr, endaddr, n, size);
696262744Stychon		type17 = (struct smbios_table_type17 *)curaddr;
697262744Stychon		type17->arrayhand = type16_handle;
698262744Stychon		type17->xsize = guest_himem;
699262744Stychon	}
700262744Stychon
701262744Stychon	return (0);
702262744Stychon}
703262744Stychon
704262744Stychonstatic int
705262744Stychonsmbios_type19_initializer(struct smbios_structure *template_entry,
706262744Stychon    const char **template_strings, char *curaddr, char **endaddr,
707262744Stychon    uint16_t *n, uint16_t *size)
708262744Stychon{
709262744Stychon	struct smbios_table_type19 *type19;
710262744Stychon
711262744Stychon	smbios_generic_initializer(template_entry, template_strings,
712262744Stychon	    curaddr, endaddr, n, size);
713262744Stychon	type19 = (struct smbios_table_type19 *)curaddr;
714262744Stychon	type19->arrayhand = type16_handle;
715262744Stychon	type19->xsaddr = 0;
716262744Stychon	type19->xeaddr = guest_lomem;
717262744Stychon
718262744Stychon	if (guest_himem > 0) {
719262744Stychon		curaddr = *endaddr;
720262744Stychon		smbios_generic_initializer(template_entry, template_strings,
721262744Stychon		    curaddr, endaddr, n, size);
722262744Stychon		type19 = (struct smbios_table_type19 *)curaddr;
723262744Stychon		type19->arrayhand = type16_handle;
724262744Stychon		type19->xsaddr = 4*GB;
725262744Stychon		type19->xeaddr = guest_himem;
726262744Stychon	}
727262744Stychon
728262744Stychon	return (0);
729262744Stychon}
730262744Stychon
731262744Stychonstatic void
732262744Stychonsmbios_ep_initializer(struct smbios_entry_point *smbios_ep, uint32_t staddr)
733262744Stychon{
734262744Stychon	memset(smbios_ep, 0, sizeof(*smbios_ep));
735262744Stychon	memcpy(smbios_ep->eanchor, SMBIOS_ENTRY_EANCHOR,
736262744Stychon	    SMBIOS_ENTRY_EANCHORLEN);
737262744Stychon	smbios_ep->eplen = 0x1F;
738262744Stychon	assert(sizeof (struct smbios_entry_point) == smbios_ep->eplen);
739262744Stychon	smbios_ep->major = 2;
740272147Sgrehan	smbios_ep->minor = 6;
741262744Stychon	smbios_ep->revision = 0;
742262744Stychon	memcpy(smbios_ep->ianchor, SMBIOS_ENTRY_IANCHOR,
743262744Stychon	    SMBIOS_ENTRY_IANCHORLEN);
744262744Stychon	smbios_ep->staddr = staddr;
745262744Stychon	smbios_ep->bcdrev = 0x24;
746262744Stychon}
747262744Stychon
748262744Stychonstatic void
749262744Stychonsmbios_ep_finalizer(struct smbios_entry_point *smbios_ep, uint16_t len,
750262744Stychon    uint16_t num, uint16_t maxssize)
751262744Stychon{
752262744Stychon	uint8_t	checksum;
753262744Stychon	int	i;
754262744Stychon
755262744Stychon	smbios_ep->maxssize = maxssize;
756262744Stychon	smbios_ep->stlen = len;
757262744Stychon	smbios_ep->stnum = num;
758262744Stychon
759262744Stychon	checksum = 0;
760262744Stychon	for (i = 0x10; i < 0x1f; i++) {
761262744Stychon		checksum -= ((uint8_t *)smbios_ep)[i];
762262744Stychon	}
763262744Stychon	smbios_ep->ichecksum = checksum;
764262744Stychon
765262744Stychon	checksum = 0;
766262744Stychon	for (i = 0; i < 0x1f; i++) {
767262744Stychon		checksum -= ((uint8_t *)smbios_ep)[i];
768262744Stychon	}
769262744Stychon	smbios_ep->echecksum = checksum;
770262744Stychon}
771262744Stychon
772262744Stychonint
773262744Stychonsmbios_build(struct vmctx *ctx)
774262744Stychon{
775262744Stychon	struct smbios_entry_point	*smbios_ep;
776262744Stychon	uint16_t			n;
777262744Stychon	uint16_t			maxssize;
778262744Stychon	char				*curaddr, *startaddr, *ststartaddr;
779262744Stychon	int				i;
780262744Stychon	int				err;
781262744Stychon
782270074Sgrehan	guest_lomem = vm_get_lowmem_size(ctx);
783270074Sgrehan	guest_himem = vm_get_highmem_size(ctx);
784262744Stychon
785262744Stychon	startaddr = paddr_guest2host(ctx, SMBIOS_BASE, SMBIOS_MAX_LENGTH);
786262744Stychon	if (startaddr == NULL) {
787262744Stychon		fprintf(stderr, "smbios table requires mapped mem\n");
788262744Stychon		return (ENOMEM);
789262744Stychon	}
790262744Stychon
791262744Stychon	curaddr = startaddr;
792262744Stychon
793262744Stychon	smbios_ep = (struct smbios_entry_point *)curaddr;
794262744Stychon	smbios_ep_initializer(smbios_ep, SMBIOS_BASE +
795262744Stychon	    sizeof(struct smbios_entry_point));
796262744Stychon	curaddr += sizeof(struct smbios_entry_point);
797262744Stychon	ststartaddr = curaddr;
798262744Stychon
799262744Stychon	n = 0;
800262744Stychon	maxssize = 0;
801262744Stychon	for (i = 0; smbios_template[i].entry != NULL; i++) {
802262744Stychon		struct smbios_structure	*entry;
803262744Stychon		const char		**strings;
804262744Stychon		initializer_func_t      initializer;
805262744Stychon		char			*endaddr;
806262744Stychon		uint16_t		size;
807262744Stychon
808262744Stychon		entry = smbios_template[i].entry;
809262744Stychon		strings = smbios_template[i].strings;
810262744Stychon		initializer = smbios_template[i].initializer;
811262744Stychon
812262744Stychon		err = (*initializer)(entry, strings, curaddr, &endaddr,
813262744Stychon		    &n, &size);
814262744Stychon		if (err != 0)
815262744Stychon			return (err);
816262744Stychon
817262744Stychon		if (size > maxssize)
818262744Stychon			maxssize = size;
819262744Stychon
820262744Stychon		curaddr = endaddr;
821262744Stychon	}
822262744Stychon
823262744Stychon	assert(curaddr - startaddr < SMBIOS_MAX_LENGTH);
824262744Stychon	smbios_ep_finalizer(smbios_ep, curaddr - ststartaddr, n, maxssize);
825262744Stychon
826262744Stychon	return (0);
827262744Stychon}
828