smbiostbl.c revision 270074
1214478Srpaulo/*-
217680Spst * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
317680Spst * All rights reserved.
417680Spst *
517680Spst * Redistribution and use in source and binary forms, with or without
617680Spst * modification, are permitted provided that the following conditions
717680Spst * are met:
817680Spst * 1. Redistributions of source code must retain the above copyright
917680Spst *    notice, this list of conditions and the following disclaimer.
1017680Spst * 2. Redistributions in binary form must reproduce the above copyright
1117680Spst *    notice, this list of conditions and the following disclaimer in the
1217680Spst *    documentation and/or other materials provided with the distribution.
1317680Spst *
1417680Spst * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
1517680Spst * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1617680Spst * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1717680Spst * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1817680Spst * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1917680Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2017680Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2117680Spst * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2217680Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2317680Spst * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2475115Sfenner * SUCH DAMAGE.
2575115Sfenner */
2675115Sfenner
2775115Sfenner#include <sys/cdefs.h>
2817680Spst__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/smbiostbl.c 270074 2014-08-17 01:23:52Z grehan $");
2975115Sfenner
30214478Srpaulo#include <sys/param.h>
31214478Srpaulo
3217680Spst#include <assert.h>
3317680Spst#include <errno.h>
3417680Spst#include <md5.h>
3517680Spst#include <stdio.h>
3675115Sfenner#include <string.h>
3775115Sfenner#include <unistd.h>
3875115Sfenner#include <uuid.h>
3975115Sfenner
40235530Sdelphij#include <machine/vmm.h>
4117680Spst#include <vmmapi.h>
4217680Spst
4317680Spst#include "bhyverun.h"
4417680Spst#include "smbiostbl.h"
4517680Spst
4617680Spst#define	MB			(1024*1024)
4717680Spst#define	GB			(1024ULL*1024*1024)
48127668Sbms
49127668Sbms#define SMBIOS_BASE		0xF1000
5017680Spst
5117680Spst/* BHYVE_ACPI_BASE - SMBIOS_BASE) */
5217680Spst#define	SMBIOS_MAX_LENGTH	(0xF2400 - 0xF1000)
5317680Spst
5417680Spst#define	SMBIOS_TYPE_BIOS	0
5517680Spst#define	SMBIOS_TYPE_SYSTEM	1
5617680Spst#define	SMBIOS_TYPE_CHASSIS	3
5717680Spst#define	SMBIOS_TYPE_PROCESSOR	4
5817680Spst#define	SMBIOS_TYPE_MEMARRAY	16
5917680Spst#define	SMBIOS_TYPE_MEMDEVICE	17
6017680Spst#define	SMBIOS_TYPE_MEMARRAYMAP	19
6117680Spst#define	SMBIOS_TYPE_BOOT	32
6217680Spst#define	SMBIOS_TYPE_EOT		127
6317680Spst
6417680Spststruct smbios_structure {
6517680Spst	uint8_t		type;
6617680Spst	uint8_t		length;
6717680Spst	uint16_t	handle;
6875115Sfenner} __packed;
6975115Sfenner
7075115Sfennertypedef int (*initializer_func_t)(struct smbios_structure *template_entry,
7175115Sfenner    const char **template_strings, char *curaddr, char **endaddr,
7275115Sfenner    uint16_t *n, uint16_t *size);
7375115Sfenner
7475115Sfennerstruct smbios_template_entry {
7575115Sfenner	struct smbios_structure	*entry;
7675115Sfenner	const char		**strings;
7775115Sfenner	initializer_func_t	initializer;
7875115Sfenner};
7975115Sfenner
8075115Sfenner/*
8175115Sfenner * SMBIOS Structure Table Entry Point
8275115Sfenner */
8317680Spst#define	SMBIOS_ENTRY_EANCHOR	"_SM_"
8475115Sfenner#define	SMBIOS_ENTRY_EANCHORLEN	4
8575115Sfenner#define	SMBIOS_ENTRY_IANCHOR	"_DMI_"
8675115Sfenner#define	SMBIOS_ENTRY_IANCHORLEN	5
8775115Sfenner
8875115Sfennerstruct smbios_entry_point {
8956893Sfenner	char		eanchor[4];	/* anchor tag */
9075115Sfenner	uint8_t		echecksum;	/* checksum of entry point structure */
9175115Sfenner	uint8_t		eplen;		/* length in bytes of entry point */
9275115Sfenner	uint8_t		major;		/* major version of the SMBIOS spec */
9375115Sfenner	uint8_t		minor;		/* minor version of the SMBIOS spec */
9475115Sfenner	uint16_t	maxssize;	/* maximum size in bytes of a struct */
9575115Sfenner	uint8_t		revision;	/* entry point structure revision */
9675115Sfenner	uint8_t		format[5];	/* entry point rev-specific data */
9775115Sfenner	char		ianchor[5];	/* intermediate anchor tag */
9875115Sfenner	uint8_t		ichecksum;	/* intermediate checksum */
9975115Sfenner	uint16_t	stlen;		/* len in bytes of structure table */
10075115Sfenner	uint32_t	staddr;		/* physical addr of structure table */
10175115Sfenner	uint16_t	stnum;		/* number of structure table entries */
10275115Sfenner	uint8_t		bcdrev;		/* BCD value representing DMI ver */
10375115Sfenner} __packed;
10475115Sfenner
10575115Sfenner/*
10675115Sfenner * BIOS Information
10775115Sfenner */
10875115Sfenner#define	SMBIOS_FL_ISA		0x00000010	/* ISA is supported */
10975115Sfenner#define	SMBIOS_FL_PCI		0x00000080	/* PCI is supported */
11075115Sfenner#define	SMBIOS_FL_SHADOW	0x00001000	/* BIOS shadowing is allowed */
11175115Sfenner#define	SMBIOS_FL_CDBOOT	0x00008000	/* Boot from CD is supported */
11275115Sfenner#define	SMBIOS_FL_SELBOOT	0x00010000	/* Selectable Boot supported */
11375115Sfenner#define	SMBIOS_FL_EDD		0x00080000	/* EDD Spec is supported */
11475115Sfenner
11575115Sfenner#define	SMBIOS_XB1_FL_ACPI	0x00000001	/* ACPI is supported */
11675115Sfenner
11775115Sfenner#define	SMBIOS_XB2_FL_BBS	0x00000001	/* BIOS Boot Specification */
11875115Sfenner#define	SMBIOS_XB2_FL_VM	0x00000010	/* Virtual Machine */
11975115Sfenner
12075115Sfennerstruct smbios_table_type0 {
12175115Sfenner	struct smbios_structure	header;
12275115Sfenner	uint8_t			vendor;		/* vendor string */
12375115Sfenner	uint8_t			version;	/* version string */
12475115Sfenner	uint16_t		segment;	/* address segment location */
12575115Sfenner	uint8_t			rel_date;	/* release date */
12675115Sfenner	uint8_t			size;		/* rom size */
12775115Sfenner	uint64_t		cflags;		/* characteristics */
12875115Sfenner	uint8_t			xc_bytes[2];	/* characteristics ext bytes */
12975115Sfenner	uint8_t			sb_major_rel;	/* system bios version */
13075115Sfenner	uint8_t			sb_minor_rele;
13175115Sfenner	uint8_t			ecfw_major_rel;	/* embedded ctrl fw version */
13256893Sfenner	uint8_t			ecfw_minor_rel;
13375115Sfenner} __packed;
13475115Sfenner
13575115Sfenner/*
13675115Sfenner * System Information
13775115Sfenner */
13875115Sfenner#define	SMBIOS_WAKEUP_SWITCH	0x06	/* power switch */
13975115Sfenner
14075115Sfennerstruct smbios_table_type1 {
14175115Sfenner	struct smbios_structure	header;
14275115Sfenner	uint8_t			manufacturer;	/* manufacturer string */
14375115Sfenner	uint8_t			product;	/* product name string */
14475115Sfenner	uint8_t			version;	/* version string */
14575115Sfenner	uint8_t			serial;		/* serial number string */
14675115Sfenner	uint8_t			uuid[16];	/* uuid byte array */
14775115Sfenner	uint8_t			wakeup;		/* wake-up event */
14875115Sfenner	uint8_t			sku;		/* sku number string */
14975115Sfenner	uint8_t			family;		/* family name string */
15075115Sfenner} __packed;
15198524Sfenner
15298524Sfenner/*
15398524Sfenner * System Enclosure or Chassis
15475115Sfenner */
15575115Sfenner#define	SMBIOS_CHT_UNKNOWN	0x02	/* unknown */
15675115Sfenner
15775115Sfenner#define	SMBIOS_CHST_SAFE	0x03	/* safe */
15875115Sfenner
15975115Sfenner#define	SMBIOS_CHSC_NONE	0x03	/* none */
16075115Sfenner
16175115Sfennerstruct smbios_table_type3 {
16275115Sfenner	struct smbios_structure	header;
16375115Sfenner	uint8_t			manufacturer;	/* manufacturer string */
16475115Sfenner	uint8_t			type;		/* type */
165172683Smlaier	uint8_t			version;	/* version string */
166172683Smlaier	uint8_t			serial;		/* serial number string */
167172683Smlaier	uint8_t			asset;		/* asset tag string */
16875115Sfenner	uint8_t			bustate;	/* boot-up state */
16975115Sfenner	uint8_t			psstate;	/* power supply state */
17075115Sfenner	uint8_t			tstate;		/* thermal state */
17175115Sfenner	uint8_t			security;	/* security status */
17275115Sfenner	uint8_t			uheight;	/* height in 'u's */
17375115Sfenner	uint8_t			cords;		/* number of power cords */
17475115Sfenner	uint8_t			elems;		/* number of element records */
17575115Sfenner	uint8_t			elemlen;	/* length of records */
17675115Sfenner	uint8_t			sku;		/* sku number string */
17775115Sfenner} __packed;
17875115Sfenner
17975115Sfenner/*
18075115Sfenner * Processor Information
18175115Sfenner */
18275115Sfenner#define	SMBIOS_PRT_CENTRAL	0x03	/* central processor */
18375115Sfenner
18475115Sfenner#define	SMBIOS_PRF_OTHER	0x01	/* other */
18575115Sfenner
18675115Sfenner#define	SMBIOS_PRS_PRESENT	0x40	/* socket is populated */
18775115Sfenner#define	SMBIOS_PRS_ENABLED	0x1	/* enabled */
18875115Sfenner
18975115Sfenner#define	SMBIOS_PRU_NONE		0x06	/* none */
19075115Sfenner
19175115Sfenner#define	SMBIOS_PFL_64B	0x04	/* 64-bit capable */
19275115Sfenner
19375115Sfennerstruct smbios_table_type4 {
19417680Spst	struct smbios_structure	header;
19575115Sfenner	uint8_t			socket;		/* socket designation string */
19656893Sfenner	uint8_t			type;		/* processor type */
19756893Sfenner	uint8_t			family;		/* processor family */
19856893Sfenner	uint8_t			manufacturer;	/* manufacturer string */
19956893Sfenner	uint64_t		cpuid;		/* processor cpuid */
20056893Sfenner	uint8_t			version;	/* version string */
20156893Sfenner	uint8_t			voltage;	/* voltage */
20256893Sfenner	uint16_t		clkspeed;	/* ext clock speed in mhz */
20356893Sfenner	uint16_t		maxspeed;	/* maximum speed in mhz */
20456893Sfenner	uint16_t		curspeed;	/* current speed in mhz */
20517680Spst	uint8_t			status;		/* status */
20617680Spst	uint8_t			upgrade;	/* upgrade */
20717680Spst	uint16_t		l1handle;	/* l1 cache handle */
20817680Spst	uint16_t		l2handle;	/* l2 cache handle */
20917680Spst	uint16_t		l3handle;	/* l3 cache handle */
21017680Spst	uint8_t			serial;		/* serial number string */
21117680Spst	uint8_t			asset;		/* asset tag string */
21275115Sfenner	uint8_t			part;		/* part number string */
21317680Spst	uint8_t			cores;		/* cores per socket */
21417680Spst	uint8_t			ecores;		/* enabled cores */
21517680Spst	uint8_t			threads;	/* threads per socket */
21617680Spst	uint16_t		cflags;		/* processor characteristics */
21717680Spst	uint16_t		family2;	/* processor family 2 */
21817680Spst} __packed;
21975115Sfenner
220235530Sdelphij/*
22117680Spst * Physical Memory Array
22217680Spst */
22317680Spst#define	SMBIOS_MAL_SYSMB	0x03	/* system board or motherboard */
22417680Spst
225172683Smlaier#define	SMBIOS_MAU_SYSTEM	0x03	/* system memory */
226172683Smlaier
227172683Smlaier#define	SMBIOS_MAE_NONE		0x03	/* none */
228172683Smlaier
229172683Smlaierstruct smbios_table_type16 {
230172683Smlaier	struct smbios_structure	header;
231172683Smlaier	uint8_t			location;	/* physical device location */
232	uint8_t			use;		/* device functional purpose */
233	uint8_t			ecc;		/* err detect/correct method */
234	uint32_t		size;		/* max mem capacity in kb */
235	uint16_t		errhand;	/* handle of error (if any) */
236	uint16_t		ndevs;		/* num of slots or sockets */
237	uint64_t		xsize;		/* max mem capacity in bytes */
238} __packed;
239
240/*
241 * Memory Device
242 */
243#define	SMBIOS_MDFF_UNKNOWN	0x02	/* unknown */
244
245#define	SMBIOS_MDT_UNKNOWN	0x02	/* unknown */
246
247#define	SMBIOS_MDF_UNKNOWN	0x0004	/* unknown */
248
249struct smbios_table_type17 {
250	struct smbios_structure	header;
251	uint16_t		arrayhand;	/* handle of physl mem array */
252	uint16_t		errhand;	/* handle of mem error data */
253	uint16_t		twidth;		/* total width in bits */
254	uint16_t		dwidth;		/* data width in bits */
255	uint16_t		size;		/* size in bytes */
256	uint8_t			form;		/* form factor */
257	uint8_t			set;		/* set */
258	uint8_t			dloc;		/* device locator string */
259	uint8_t			bloc;		/* phys bank locator string */
260	uint8_t			type;		/* memory type */
261	uint16_t		flags;		/* memory characteristics */
262	uint16_t		maxspeed;	/* maximum speed in mhz */
263	uint8_t			manufacturer;	/* manufacturer string */
264	uint8_t			serial;		/* serial number string */
265	uint8_t			asset;		/* asset tag string */
266	uint8_t			part;		/* part number string */
267	uint8_t			attributes;	/* attributes */
268	uint32_t		xsize;		/* extended size in mbs */
269	uint16_t		curspeed;	/* current speed in mhz */
270	uint16_t		minvoltage;	/* minimum voltage */
271	uint16_t		maxvoltage;	/* maximum voltage */
272	uint16_t		curvoltage;	/* configured voltage */
273} __packed;
274
275/*
276 * Memory Array Mapped Address
277 */
278struct smbios_table_type19 {
279	struct smbios_structure	header;
280	uint32_t		saddr;		/* start phys addr in kb */
281	uint32_t		eaddr;		/* end phys addr in kb */
282	uint16_t		arrayhand;	/* physical mem array handle */
283	uint8_t			width;		/* num of dev in row */
284	uint64_t		xsaddr;		/* start phys addr in bytes */
285	uint64_t		xeaddr;		/* end phys addr in bytes */
286} __packed;
287
288/*
289 * System Boot Information
290 */
291#define	SMBIOS_BOOT_NORMAL	0	/* no errors detected */
292
293struct smbios_table_type32 {
294	struct smbios_structure	header;
295	uint8_t			reserved[6];
296	uint8_t			status;		/* boot status */
297} __packed;
298
299/*
300 * End-of-Table
301 */
302struct smbios_table_type127 {
303	struct smbios_structure	header;
304} __packed;
305
306struct smbios_table_type0 smbios_type0_template = {
307	{ SMBIOS_TYPE_BIOS, sizeof (struct smbios_table_type0), 0 },
308	1,	/* bios vendor string */
309	2,	/* bios version string */
310	0xF000,	/* bios address segment location */
311	3,	/* bios release date */
312	0x0,	/* bios size (64k * (n + 1) is the size in bytes) */
313	SMBIOS_FL_ISA | SMBIOS_FL_PCI | SMBIOS_FL_SHADOW |
314	    SMBIOS_FL_CDBOOT | SMBIOS_FL_EDD,
315	{ SMBIOS_XB1_FL_ACPI, SMBIOS_XB2_FL_BBS | SMBIOS_XB2_FL_VM },
316	0x0,	/* bios major release */
317	0x0,	/* bios minor release */
318	0xff,	/* embedded controller firmware major release */
319	0xff	/* embedded controller firmware minor release */
320};
321
322const char *smbios_type0_strings[] = {
323	"BHYVE",	/* vendor string */
324	__TIME__,	/* bios version string */
325	__DATE__,	/* bios release date string */
326	NULL
327};
328
329struct smbios_table_type1 smbios_type1_template = {
330	{ SMBIOS_TYPE_SYSTEM, sizeof (struct smbios_table_type1), 0 },
331	1,		/* manufacturer string */
332	2,		/* product string */
333	3,		/* version string */
334	4,		/* serial number string */
335	{ 0 },
336	SMBIOS_WAKEUP_SWITCH,
337	5,		/* sku string */
338	6		/* family string */
339};
340
341static int smbios_type1_initializer(struct smbios_structure *template_entry,
342    const char **template_strings, char *curaddr, char **endaddr,
343    uint16_t *n, uint16_t *size);
344
345const char *smbios_type1_strings[] = {
346	" ",		/* manufacturer string */
347	"BHYVE",	/* product name string */
348	"1.0",		/* version string */
349	"None",		/* serial number string */
350	"None",		/* sku string */
351	" ",		/* family name string */
352	NULL
353};
354
355struct smbios_table_type3 smbios_type3_template = {
356	{ SMBIOS_TYPE_CHASSIS, sizeof (struct smbios_table_type3), 0 },
357	1,		/* manufacturer string */
358	SMBIOS_CHT_UNKNOWN,
359	2,		/* version string */
360	3,		/* serial number string */
361	4,		/* asset tag string */
362	SMBIOS_CHST_SAFE,
363	SMBIOS_CHST_SAFE,
364	SMBIOS_CHST_SAFE,
365	SMBIOS_CHSC_NONE,
366	0,		/* height in 'u's (0=enclosure height unspecified) */
367	0,		/* number of power cords (0=number unspecified) */
368	0,		/* number of contained element records */
369	0,		/* length of records */
370	5		/* sku number string */
371};
372
373const char *smbios_type3_strings[] = {
374	" ",		/* manufacturer string */
375	"1.0",		/* version string */
376	"None",		/* serial number string */
377	"None",		/* asset tag string */
378	"None",		/* sku number string */
379	NULL
380};
381
382struct smbios_table_type4 smbios_type4_template = {
383	{ SMBIOS_TYPE_PROCESSOR, sizeof (struct smbios_table_type4), 0 },
384	1,		/* socket designation string */
385	SMBIOS_PRT_CENTRAL,
386	SMBIOS_PRF_OTHER,
387	2,		/* manufacturer string */
388	0,		/* cpuid */
389	3,		/* version string */
390	0,		/* voltage */
391	0,		/* external clock frequency in mhz (0=unknown) */
392	0,		/* maximum frequency in mhz (0=unknown) */
393	0,		/* current frequency in mhz (0=unknown) */
394	SMBIOS_PRS_PRESENT | SMBIOS_PRS_ENABLED,
395	SMBIOS_PRU_NONE,
396	-1,		/* l1 cache handle */
397	-1,		/* l2 cache handle */
398	-1,		/* l3 cache handle */
399	4,		/* serial number string */
400	5,		/* asset tag string */
401	6,		/* part number string */
402	0,		/* cores per socket (0=unknown) */
403	0,		/* enabled cores per socket (0=unknown) */
404	0,		/* threads per socket (0=unknown) */
405	SMBIOS_PFL_64B,
406	SMBIOS_PRF_OTHER
407};
408
409const char *smbios_type4_strings[] = {
410	" ",		/* socket designation string */
411	" ",		/* manufacturer string */
412	" ",		/* version string */
413	"None",		/* serial number string */
414	"None",		/* asset tag string */
415	"None",		/* part number string */
416	NULL
417};
418
419static int smbios_type4_initializer(struct smbios_structure *template_entry,
420    const char **template_strings, char *curaddr, char **endaddr,
421    uint16_t *n, uint16_t *size);
422
423struct smbios_table_type16 smbios_type16_template = {
424	{ SMBIOS_TYPE_MEMARRAY, sizeof (struct smbios_table_type16),  0 },
425	SMBIOS_MAL_SYSMB,
426	SMBIOS_MAU_SYSTEM,
427	SMBIOS_MAE_NONE,
428	0x80000000,	/* max mem capacity in kb (0x80000000=use extended) */
429	-1,		/* handle of error (if any) */
430	0,		/* number of slots or sockets (TBD) */
431	0		/* extended maximum memory capacity in bytes (TBD) */
432};
433
434static int smbios_type16_initializer(struct smbios_structure *template_entry,
435    const char **template_strings, char *curaddr, char **endaddr,
436    uint16_t *n, uint16_t *size);
437
438struct smbios_table_type17 smbios_type17_template = {
439	{ SMBIOS_TYPE_MEMDEVICE, sizeof (struct smbios_table_type17),  0 },
440	-1,		/* handle of physical memory array */
441	-1,		/* handle of memory error data */
442	64,		/* total width in bits including ecc */
443	64,		/* data width in bits */
444	0x7fff,		/* size in bytes (0x7fff=use extended)*/
445	SMBIOS_MDFF_UNKNOWN,
446	0,		/* set (0x00=none, 0xff=unknown) */
447	1,		/* device locator string */
448	2,		/* physical bank locator string */
449	SMBIOS_MDT_UNKNOWN,
450	SMBIOS_MDF_UNKNOWN,
451	0,		/* maximum memory speed in mhz (0=unknown) */
452	3,		/* manufacturer string */
453	4,		/* serial number string */
454	5,		/* asset tag string */
455	6,		/* part number string */
456	0,		/* attributes (0=unknown rank information) */
457	0,		/* extended size in mb (TBD) */
458	0,		/* current speed in mhz (0=unknown) */
459	0,		/* minimum voltage in mv (0=unknown) */
460	0,		/* maximum voltage in mv (0=unknown) */
461	0		/* configured voltage in mv (0=unknown) */
462};
463
464const char *smbios_type17_strings[] = {
465	" ",		/* device locator string */
466	" ",		/* physical bank locator string */
467	" ",		/* manufacturer string */
468	"None",		/* serial number string */
469	"None",		/* asset tag string */
470	"None",		/* part number string */
471	NULL
472};
473
474static int smbios_type17_initializer(struct smbios_structure *template_entry,
475    const char **template_strings, char *curaddr, char **endaddr,
476    uint16_t *n, uint16_t *size);
477
478struct smbios_table_type19 smbios_type19_template = {
479	{ SMBIOS_TYPE_MEMARRAYMAP, sizeof (struct smbios_table_type19),  0 },
480	0xffffffff,	/* starting phys addr in kb (0xffffffff=use ext) */
481	0xffffffff,	/* ending phys addr in kb (0xffffffff=use ext) */
482	-1,		/* physical memory array handle */
483	1,		/* number of devices that form a row */
484	0,		/* extended starting phys addr in bytes (TDB) */
485	0		/* extended ending phys addr in bytes (TDB) */
486};
487
488static int smbios_type19_initializer(struct smbios_structure *template_entry,
489    const char **template_strings, char *curaddr, char **endaddr,
490    uint16_t *n, uint16_t *size);
491
492struct smbios_table_type32 smbios_type32_template = {
493	{ SMBIOS_TYPE_BOOT, sizeof (struct smbios_table_type32),  0 },
494	{ 0, 0, 0, 0, 0, 0 },
495	SMBIOS_BOOT_NORMAL
496};
497
498struct smbios_table_type127 smbios_type127_template = {
499	{ SMBIOS_TYPE_EOT, sizeof (struct smbios_table_type127),  0 }
500};
501
502static int smbios_generic_initializer(struct smbios_structure *template_entry,
503    const char **template_strings, char *curaddr, char **endaddr,
504    uint16_t *n, uint16_t *size);
505
506static struct smbios_template_entry smbios_template[] = {
507	{ (struct smbios_structure *)&smbios_type0_template,
508	  smbios_type0_strings,
509	  smbios_generic_initializer },
510	{ (struct smbios_structure *)&smbios_type1_template,
511	  smbios_type1_strings,
512	  smbios_type1_initializer },
513	{ (struct smbios_structure *)&smbios_type3_template,
514	  smbios_type3_strings,
515	  smbios_generic_initializer },
516	{ (struct smbios_structure *)&smbios_type4_template,
517	  smbios_type4_strings,
518	  smbios_type4_initializer },
519	{ (struct smbios_structure *)&smbios_type16_template,
520	  NULL,
521	  smbios_type16_initializer },
522	{ (struct smbios_structure *)&smbios_type17_template,
523	  smbios_type17_strings,
524	  smbios_type17_initializer },
525	{ (struct smbios_structure *)&smbios_type19_template,
526	  NULL,
527	  smbios_type19_initializer },
528	{ (struct smbios_structure *)&smbios_type32_template,
529	  NULL,
530	  smbios_generic_initializer },
531	{ (struct smbios_structure *)&smbios_type127_template,
532	  NULL,
533	  smbios_generic_initializer },
534	{ NULL,NULL, NULL }
535};
536
537static uint64_t guest_lomem, guest_himem;
538static uint16_t type16_handle;
539
540static int
541smbios_generic_initializer(struct smbios_structure *template_entry,
542    const char **template_strings, char *curaddr, char **endaddr,
543    uint16_t *n, uint16_t *size)
544{
545	struct smbios_structure *entry;
546
547	memcpy(curaddr, template_entry, template_entry->length);
548	entry = (struct smbios_structure *)curaddr;
549	entry->handle = *n + 1;
550	curaddr += entry->length;
551	if (template_strings != NULL) {
552		int	i;
553
554		for (i = 0; template_strings[i] != NULL; i++) {
555			const char *string;
556			int len;
557
558			string = template_strings[i];
559			len = strlen(string) + 1;
560			memcpy(curaddr, string, len);
561			curaddr += len;
562		}
563		*curaddr = '\0';
564		curaddr++;
565	} else {
566		/* Minimum string section is double nul */
567		*curaddr = '\0';
568		curaddr++;
569		*curaddr = '\0';
570		curaddr++;
571	}
572	(*n)++;
573	*endaddr = curaddr;
574
575	return (0);
576}
577
578static int
579smbios_type1_initializer(struct smbios_structure *template_entry,
580    const char **template_strings, char *curaddr, char **endaddr,
581    uint16_t *n, uint16_t *size)
582{
583	struct smbios_table_type1 *type1;
584
585	smbios_generic_initializer(template_entry, template_strings,
586	    curaddr, endaddr, n, size);
587	type1 = (struct smbios_table_type1 *)curaddr;
588
589	if (guest_uuid_str != NULL) {
590		uuid_t		uuid;
591		uint32_t	status;
592
593		uuid_from_string(guest_uuid_str, &uuid, &status);
594		if (status != uuid_s_ok)
595			return (-1);
596
597		uuid_enc_le(&type1->uuid, &uuid);
598	} else {
599		MD5_CTX		mdctx;
600		u_char		digest[16];
601		char		hostname[MAXHOSTNAMELEN];
602
603		/*
604		 * Universally unique and yet reproducible are an
605		 * oxymoron, however reproducible is desirable in
606		 * this case.
607		 */
608		if (gethostname(hostname, sizeof(hostname)))
609			return (-1);
610
611		MD5Init(&mdctx);
612		MD5Update(&mdctx, vmname, strlen(vmname));
613		MD5Update(&mdctx, hostname, sizeof(hostname));
614		MD5Final(digest, &mdctx);
615
616		/*
617		 * Set the variant and version number.
618		 */
619		digest[6] &= 0x0F;
620		digest[6] |= 0x30;	/* version 3 */
621		digest[8] &= 0x3F;
622		digest[8] |= 0x80;
623
624		memcpy(&type1->uuid, digest, sizeof (digest));
625	}
626
627	return (0);
628}
629
630static int
631smbios_type4_initializer(struct smbios_structure *template_entry,
632    const char **template_strings, char *curaddr, char **endaddr,
633    uint16_t *n, uint16_t *size)
634{
635	int i;
636
637	for (i = 0; i < guest_ncpus; i++) {
638		struct smbios_table_type4 *type4;
639		char *p;
640		int nstrings, len;
641
642		smbios_generic_initializer(template_entry, template_strings,
643		    curaddr, endaddr, n, size);
644		type4 = (struct smbios_table_type4 *)curaddr;
645		p = curaddr + sizeof (struct smbios_table_type4);
646		nstrings = 0;
647		while (p < *endaddr - 1) {
648			if (*p++ == '\0')
649				nstrings++;
650		}
651		len = sprintf(*endaddr - 1, "CPU #%d", i) + 1;
652		*endaddr += len - 1;
653		*(*endaddr) = '\0';
654		(*endaddr)++;
655		type4->socket = nstrings + 1;
656		curaddr = *endaddr;
657	}
658
659	return (0);
660}
661
662static int
663smbios_type16_initializer(struct smbios_structure *template_entry,
664    const char **template_strings, char *curaddr, char **endaddr,
665    uint16_t *n, uint16_t *size)
666{
667	struct smbios_table_type16 *type16;
668
669	type16_handle = *n;
670	smbios_generic_initializer(template_entry, template_strings,
671	    curaddr, endaddr, n, size);
672	type16 = (struct smbios_table_type16 *)curaddr;
673	type16->xsize = guest_lomem + guest_himem;
674	type16->ndevs = guest_himem > 0 ? 2 : 1;
675
676	return (0);
677}
678
679static int
680smbios_type17_initializer(struct smbios_structure *template_entry,
681    const char **template_strings, char *curaddr, char **endaddr,
682    uint16_t *n, uint16_t *size)
683{
684	struct smbios_table_type17 *type17;
685
686	smbios_generic_initializer(template_entry, template_strings,
687	    curaddr, endaddr, n, size);
688	type17 = (struct smbios_table_type17 *)curaddr;
689	type17->arrayhand = type16_handle;
690	type17->xsize = guest_lomem;
691
692	if (guest_himem > 0) {
693		curaddr = *endaddr;
694		smbios_generic_initializer(template_entry, template_strings,
695		    curaddr, endaddr, n, size);
696		type17 = (struct smbios_table_type17 *)curaddr;
697		type17->arrayhand = type16_handle;
698		type17->xsize = guest_himem;
699	}
700
701	return (0);
702}
703
704static int
705smbios_type19_initializer(struct smbios_structure *template_entry,
706    const char **template_strings, char *curaddr, char **endaddr,
707    uint16_t *n, uint16_t *size)
708{
709	struct smbios_table_type19 *type19;
710
711	smbios_generic_initializer(template_entry, template_strings,
712	    curaddr, endaddr, n, size);
713	type19 = (struct smbios_table_type19 *)curaddr;
714	type19->arrayhand = type16_handle;
715	type19->xsaddr = 0;
716	type19->xeaddr = guest_lomem;
717
718	if (guest_himem > 0) {
719		curaddr = *endaddr;
720		smbios_generic_initializer(template_entry, template_strings,
721		    curaddr, endaddr, n, size);
722		type19 = (struct smbios_table_type19 *)curaddr;
723		type19->arrayhand = type16_handle;
724		type19->xsaddr = 4*GB;
725		type19->xeaddr = guest_himem;
726	}
727
728	return (0);
729}
730
731static void
732smbios_ep_initializer(struct smbios_entry_point *smbios_ep, uint32_t staddr)
733{
734	memset(smbios_ep, 0, sizeof(*smbios_ep));
735	memcpy(smbios_ep->eanchor, SMBIOS_ENTRY_EANCHOR,
736	    SMBIOS_ENTRY_EANCHORLEN);
737	smbios_ep->eplen = 0x1F;
738	assert(sizeof (struct smbios_entry_point) == smbios_ep->eplen);
739	smbios_ep->major = 2;
740	smbios_ep->minor = 4;
741	smbios_ep->revision = 0;
742	memcpy(smbios_ep->ianchor, SMBIOS_ENTRY_IANCHOR,
743	    SMBIOS_ENTRY_IANCHORLEN);
744	smbios_ep->staddr = staddr;
745	smbios_ep->bcdrev = 0x24;
746}
747
748static void
749smbios_ep_finalizer(struct smbios_entry_point *smbios_ep, uint16_t len,
750    uint16_t num, uint16_t maxssize)
751{
752	uint8_t	checksum;
753	int	i;
754
755	smbios_ep->maxssize = maxssize;
756	smbios_ep->stlen = len;
757	smbios_ep->stnum = num;
758
759	checksum = 0;
760	for (i = 0x10; i < 0x1f; i++) {
761		checksum -= ((uint8_t *)smbios_ep)[i];
762	}
763	smbios_ep->ichecksum = checksum;
764
765	checksum = 0;
766	for (i = 0; i < 0x1f; i++) {
767		checksum -= ((uint8_t *)smbios_ep)[i];
768	}
769	smbios_ep->echecksum = checksum;
770}
771
772int
773smbios_build(struct vmctx *ctx)
774{
775	struct smbios_entry_point	*smbios_ep;
776	uint16_t			n;
777	uint16_t			maxssize;
778	char				*curaddr, *startaddr, *ststartaddr;
779	int				i;
780	int				err;
781
782	guest_lomem = vm_get_lowmem_size(ctx);
783	guest_himem = vm_get_highmem_size(ctx);
784
785	startaddr = paddr_guest2host(ctx, SMBIOS_BASE, SMBIOS_MAX_LENGTH);
786	if (startaddr == NULL) {
787		fprintf(stderr, "smbios table requires mapped mem\n");
788		return (ENOMEM);
789	}
790
791	curaddr = startaddr;
792
793	smbios_ep = (struct smbios_entry_point *)curaddr;
794	smbios_ep_initializer(smbios_ep, SMBIOS_BASE +
795	    sizeof(struct smbios_entry_point));
796	curaddr += sizeof(struct smbios_entry_point);
797	ststartaddr = curaddr;
798
799	n = 0;
800	maxssize = 0;
801	for (i = 0; smbios_template[i].entry != NULL; i++) {
802		struct smbios_structure	*entry;
803		const char		**strings;
804		initializer_func_t      initializer;
805		char			*endaddr;
806		uint16_t		size;
807
808		entry = smbios_template[i].entry;
809		strings = smbios_template[i].strings;
810		initializer = smbios_template[i].initializer;
811
812		err = (*initializer)(entry, strings, curaddr, &endaddr,
813		    &n, &size);
814		if (err != 0)
815			return (err);
816
817		if (size > maxssize)
818			maxssize = size;
819
820		curaddr = endaddr;
821	}
822
823	assert(curaddr - startaddr < SMBIOS_MAX_LENGTH);
824	smbios_ep_finalizer(smbios_ep, curaddr - ststartaddr, n, maxssize);
825
826	return (0);
827}
828