smbios.c revision 156712
1250199Sgrehan/*-
2298446Ssephe * Copyright (c) 2005, 2006 Jung-uk Kim <jkim@FreeBSD.org>
3250199Sgrehan * All rights reserved.
4250199Sgrehan *
5250199Sgrehan * Redistribution and use in source and binary forms, with or without
6250199Sgrehan * modification, are permitted provided that the following conditions
7250199Sgrehan * are met:
8250199Sgrehan * 1. Redistributions of source code must retain the above copyright
9250199Sgrehan *	notice, this list of conditions and the following disclaimer.
10250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11250199Sgrehan *	notice, this list of conditions and the following disclaimer in the
12250199Sgrehan *	documentation and/or other materials provided with the distribution.
13250199Sgrehan *
14250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15250199Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16250199Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17250199Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18250199Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19250199Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20250199Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21250199Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22250199Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23250199Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24250199Sgrehan * SUCH DAMAGE.
25250199Sgrehan */
26250199Sgrehan
27250199Sgrehan#include <sys/cdefs.h>
28250199Sgrehan__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/smbios.c 156712 2006-03-14 19:02:00Z jkim $");
29250199Sgrehan
30296028Ssephe#include <stand.h>
31250199Sgrehan#include <bootstrap.h>
32250199Sgrehan
33250199Sgrehan#include "btxv86.h"
34250199Sgrehan#include "libi386.h"
35250199Sgrehan
36299927Ssephe/*
37250199Sgrehan * Detect SMBIOS and export information about the SMBIOS into the
38250199Sgrehan * environment.
39250199Sgrehan *
40250199Sgrehan * System Management BIOS Reference Specification, v2.4 Final
41300102Ssephe * http://www.dmtf.org/standards/published_documents/DSP0134.pdf
42300102Ssephe */
43250199Sgrehan
44250199Sgrehan/*
45250199Sgrehan * Spec. 2.1.1 SMBIOS Structure Table Entry Point
46250199Sgrehan *
47250199Sgrehan * 'The SMBIOS Entry Point structure, described below, can be located by
48250199Sgrehan * application software by searching for the anchor-string on paragraph
49250199Sgrehan * (16-byte) boundaries within the physical memory address range
50250199Sgrehan * 000F0000h to 000FFFFFh.'
51282212Swhu */
52282212Swhu#define	SMBIOS_START		0xf0000
53282212Swhu#define	SMBIOS_LENGTH		0x10000
54282212Swhu#define	SMBIOS_STEP		0x10
55282212Swhu#define	SMBIOS_SIG		"_SM_"
56282212Swhu#define	SMBIOS_DMI_SIG		"_DMI_"
57282212Swhu
58282212Swhustatic uint8_t	smbios_enabled_sockets = 0;
59282212Swhustatic uint8_t	smbios_populated_sockets = 0;
60282212Swhu
61282212Swhustatic uint8_t	*smbios_parse_table(const uint8_t *dmi);
62282212Swhustatic void	smbios_setenv(const char *name, const uint8_t *dmi,
63282212Swhu		    const int offset);
64282212Swhustatic uint8_t	smbios_checksum(const caddr_t addr, const uint8_t len);
65282212Swhustatic uint8_t	*smbios_sigsearch(const caddr_t addr, const uint32_t len);
66282212Swhu
67282212Swhu#ifdef SMBIOS_SERIAL_NUMBERS
68282212Swhustatic void	smbios_setuuid(const char *name, const uint8_t *dmi,
69282212Swhu		    const int offset);
70282212Swhu#endif
71282212Swhu
72250199Sgrehanvoid
73282212Swhusmbios_detect(void)
74282212Swhu{
75282212Swhu	uint8_t		*smbios, *dmi, *addr;
76282212Swhu	uint16_t	i, length, count;
77282212Swhu	uint32_t	paddr;
78282212Swhu	char		buf[4];
79282212Swhu
80282212Swhu	/* locate and validate the SMBIOS */
81282212Swhu	smbios = smbios_sigsearch(PTOV(SMBIOS_START), SMBIOS_LENGTH);
82282212Swhu	if (smbios == NULL)
83282212Swhu		return;
84282212Swhu
85282212Swhu	length = *(uint16_t *)(smbios + 0x16);	/* Structure Table Length */
86282212Swhu	paddr = *(uint32_t *)(smbios + 0x18);	/* Structure Table Address */
87282212Swhu	count = *(uint16_t *)(smbios + 0x1c);	/* No of SMBIOS Structures */
88282212Swhu
89282212Swhu	for (dmi = addr = PTOV(paddr), i = 0;
90282212Swhu	     dmi - addr < length && i < count; i++)
91282212Swhu		dmi = smbios_parse_table(dmi);
92295309Ssephe	sprintf(buf, "%d", smbios_enabled_sockets);
93282212Swhu	setenv("smbios.socket.enabled", buf, 1);
94295308Ssephe	sprintf(buf, "%d", smbios_populated_sockets);
95295309Ssephe	setenv("smbios.socket.populated", buf, 1);
96282212Swhu}
97282212Swhu
98282212Swhustatic uint8_t *
99282212Swhusmbios_parse_table(const uint8_t *dmi)
100282212Swhu{
101297635Ssephe	uint8_t		*dp;
102282212Swhu
103282212Swhu	switch(dmi[0]) {
104282212Swhu	case 0:		/* Type 0: BIOS */
105282212Swhu		smbios_setenv("smbios.bios.vendor", dmi, 0x04);
106282212Swhu		smbios_setenv("smbios.bios.version", dmi, 0x05);
107282212Swhu		smbios_setenv("smbios.bios.reldate", dmi, 0x08);
108297635Ssephe		break;
109282212Swhu
110282212Swhu	case 1:		/* Type 1: System */
111282212Swhu		smbios_setenv("smbios.system.maker", dmi, 0x04);
112282212Swhu		smbios_setenv("smbios.system.product", dmi, 0x05);
113282212Swhu		smbios_setenv("smbios.system.version", dmi, 0x06);
114282212Swhu#ifdef SMBIOS_SERIAL_NUMBERS
115297635Ssephe		smbios_setenv("smbios.system.serial", dmi, 0x07);
116282212Swhu		smbios_setuuid("smbios.system.uuid", dmi, 0x08);
117282212Swhu#endif
118282212Swhu		break;
119282212Swhu
120297635Ssephe	case 2:		/* Type 2: Base Board (or Module) */
121282212Swhu		smbios_setenv("smbios.planar.maker", dmi, 0x04);
122282212Swhu		smbios_setenv("smbios.planar.product", dmi, 0x05);
123282212Swhu		smbios_setenv("smbios.planar.version", dmi, 0x06);
124282212Swhu#ifdef SMBIOS_SERIAL_NUMBERS
125282212Swhu		smbios_setenv("smbios.planar.serial", dmi, 0x07);
126282212Swhu#endif
127296028Ssephe		break;
128282212Swhu
129297635Ssephe	case 3:		/* Type 3: System Enclosure or Chassis */
130282212Swhu		smbios_setenv("smbios.chassis.maker", dmi, 0x04);
131282212Swhu		smbios_setenv("smbios.chassis.version", dmi, 0x06);
132282212Swhu#ifdef SMBIOS_SERIAL_NUMBERS
133282212Swhu		smbios_setenv("smbios.chassis.serial", dmi, 0x07);
134297635Ssephe		smbios_setenv("smbios.chassis.tag", dmi, 0x08);
135282212Swhu#endif
136282212Swhu		break;
137282212Swhu
138282212Swhu	case 4:		/* Type 4: Processor Information */
139282212Swhu		/*
140282212Swhu		 * Offset 18h: Processor Status
141282212Swhu		 *
142282212Swhu		 * Bit 7	Reserved, must be 0
143282212Swhu		 * Bit 6	CPU Socket Populated
144282212Swhu		 *		1 - CPU Socket Populated
145282212Swhu		 *		0 - CPU Socket Unpopulated
146282212Swhu		 * Bit 5:3	Reserved, must be zero
147282212Swhu		 * Bit 2:0	CPU Status
148282212Swhu		 *		0h - Unknown
149250199Sgrehan		 *		1h - CPU Enabled
150250199Sgrehan		 *		2h - CPU Disabled by User via BIOS Setup
151250199Sgrehan		 *		3h - CPU Disabled by BIOS (POST Error)
152250199Sgrehan		 *		4h - CPU is Idle, waiting to be enabled
153250199Sgrehan		 *		5-6h - Reserved
154282212Swhu		 *		7h - Other
155250199Sgrehan		 */
156250199Sgrehan		if ((dmi[0x18] & 0x07) == 1)
157250199Sgrehan			smbios_enabled_sockets++;
158250199Sgrehan		if (dmi[0x18] & 0x40)
159250199Sgrehan			smbios_populated_sockets++;
160250199Sgrehan		break;
161250199Sgrehan
162250199Sgrehan	default: /* skip other types */
163250199Sgrehan		break;
164250199Sgrehan	}
165250199Sgrehan
166250199Sgrehan	/* find structure terminator */
167250199Sgrehan	dp = __DECONST(uint8_t *, dmi + dmi[1]);
168250199Sgrehan	while (dp[0] != 0 || dp[1] != 0)
169250199Sgrehan		dp++;
170250199Sgrehan
171297635Ssephe	return(dp + 2);
172250199Sgrehan}
173250199Sgrehan
174250199Sgrehanstatic void
175282212Swhusmbios_setenv(const char *name, const uint8_t *dmi, const int offset)
176250199Sgrehan{
177250199Sgrehan	char		*cp = __DECONST(char *, dmi + dmi[1]);
178250199Sgrehan	int		i;
179250199Sgrehan
180250199Sgrehan	/* skip undefined string */
181295309Ssephe	if (dmi[offset] == 0)
182250199Sgrehan		return;
183295309Ssephe
184250199Sgrehan	for (i = 0; i < dmi[offset] - 1; i++)
185250199Sgrehan		cp += strlen(cp) + 1;
186250199Sgrehan	setenv(name, cp, 1);
187250199Sgrehan}
188250199Sgrehan
189250199Sgrehanstatic uint8_t
190250199Sgrehansmbios_checksum(const caddr_t addr, const uint8_t len)
191250199Sgrehan{
192250199Sgrehan	const uint8_t	*cp = addr;
193250199Sgrehan	uint8_t		sum;
194250199Sgrehan	int		i;
195250199Sgrehan
196295309Ssephe	for (sum = 0, i = 0; i < len; i++)
197295309Ssephe		sum += cp[i];
198250199Sgrehan
199295309Ssephe	return(sum);
200295309Ssephe}
201250199Sgrehan
202295309Ssephestatic uint8_t *
203295309Ssephesmbios_sigsearch(const caddr_t addr, const uint32_t len)
204250199Sgrehan{
205250199Sgrehan	caddr_t		cp;
206250199Sgrehan
207250199Sgrehan	/* search on 16-byte boundaries */
208295308Ssephe	for (cp = addr; cp < addr + len; cp += SMBIOS_STEP) {
209250199Sgrehan		/* compare signature, validate checksum */
210294553Ssephe		if (!strncmp(cp, SMBIOS_SIG, 4)) {
211294553Ssephe			if (smbios_checksum(cp, *(uint8_t *)(cp + 0x05)))
212294553Ssephe				continue;
213282212Swhu			if (strncmp(cp + 0x10, SMBIOS_DMI_SIG, 5))
214282212Swhu				continue;
215250199Sgrehan			if (smbios_checksum(cp + 0x10, 0x0f))
216282212Swhu				continue;
217250199Sgrehan
218282212Swhu			return(cp);
219282212Swhu		}
220282212Swhu	}
221282212Swhu
222282212Swhu	return(NULL);
223282212Swhu}
224282212Swhu
225282212Swhu#ifdef SMBIOS_SERIAL_NUMBERS
226250199Sgrehanstatic void
227282212Swhusmbios_setuuid(const char *name, const uint8_t *dmi, const int offset)
228282212Swhu{
229250199Sgrehan	const uint8_t	*idp = dmi + offset;
230282212Swhu	int		i, f = 0, z = 0;
231282212Swhu	char		uuid[37];
232250199Sgrehan
233282212Swhu	for (i = 0; i < 16; i++) {
234282212Swhu		if (idp[i] == 0xff)
235293870Ssephe			f++;
236282212Swhu		else if (idp[i] == 0x00)
237250199Sgrehan			z++;
238250199Sgrehan		else
239250199Sgrehan			break;
240250199Sgrehan	}
241250199Sgrehan	if (f != 16 && z != 16) {
242250199Sgrehan		sprintf(uuid, "%02X%02X%02X%02X-"
243250199Sgrehan		    "%02X%02X-%02X%02X-%02X%02X-"
244250199Sgrehan		    "%02X%02X%02X%02X%02X%02X",
245250199Sgrehan		    idp[0], idp[1], idp[2], idp[3],
246250199Sgrehan		    idp[4], idp[5], idp[6], idp[7], idp[8], idp[9],
247250199Sgrehan		    idp[10], idp[11], idp[12], idp[13], idp[14], idp[15]);
248250199Sgrehan		setenv(name, uuid, 1);
249250199Sgrehan	}
250250199Sgrehan}
251250199Sgrehan#endif
252250199Sgrehan