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