smbios.c revision 151452
1/*-
2 * Copyright (c) 2005 Jung-uk Kim <jkim@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *	notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *	notice, this list of conditions and the following disclaimer in the
12 *	documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/smbios.c 151452 2005-10-18 20:03:31Z jkim $");
29
30#include <stand.h>
31#include <bootstrap.h>
32
33#include "btxv86.h"
34
35/*
36 * Detect SMBIOS and export information about the SMBIOS into the
37 * environment.
38 *
39 * System Management BIOS Reference Specification, v2.4 Final
40 * http://www.dmtf.org/standards/published_documents/DSP0134.pdf
41 */
42
43/*
44 * Spec. 2.1.1 SMBIOS Structure Table Entry Point
45 *
46 * 'The SMBIOS Entry Point structure, described below, can be located by
47 * application software by searching for the anchor-string on paragraph
48 * (16-byte) boundaries within the physical memory address range
49 * 000F0000h to 000FFFFFh.'
50 */
51#define	SMBIOS_START		0xf0000
52#define	SMBIOS_LENGTH		0x10000
53#define	SMBIOS_STEP		0x10
54#define	SMBIOS_SIG		"_SM_"
55#define	SMBIOS_DMI_SIG		"_DMI_"
56
57static u_int8_t	smbios_enabled_sockets = 0;
58static u_int8_t	smbios_populated_sockets = 0;
59
60static u_int8_t	*smbios_parse_table(const u_int8_t *dmi);
61static void	smbios_setenv(const char *env, const u_int8_t *dmi,
62		    const int offset);
63static u_int8_t	smbios_checksum(const u_int8_t *addr, const u_int8_t len);
64static u_int8_t	*smbios_sigsearch(const caddr_t addr, const u_int32_t len);
65
66void
67smbios_detect(void)
68{
69	u_int8_t	*smbios, *dmi, *addr;
70	u_int16_t	i, length, count;
71	u_int32_t	paddr;
72	char		buf[4];
73
74	/* locate and validate the SMBIOS */
75	smbios = smbios_sigsearch(PTOV(SMBIOS_START), SMBIOS_LENGTH);
76	if (smbios == NULL)
77		return;
78
79	length = *(u_int16_t *)(smbios + 0x16);	/* Structure Table Length */
80	paddr = *(u_int32_t *)(smbios + 0x18);	/* Structure Table Address */
81	count = *(u_int16_t *)(smbios + 0x1c);	/* No of SMBIOS Structures */
82
83	for (dmi = addr = PTOV(paddr), i = 0;
84	     dmi - addr < length && i < count; i++)
85		dmi = smbios_parse_table(dmi);
86	sprintf(buf, "%d", smbios_enabled_sockets);
87	setenv("smbios.socket.enabled", buf, 1);
88	sprintf(buf, "%d", smbios_populated_sockets);
89	setenv("smbios.socket.populated", buf, 1);
90}
91
92static u_int8_t *
93smbios_parse_table(const u_int8_t *dmi)
94{
95	u_int8_t	*dp;
96
97	switch(dmi[0]) {
98	case 0:		/* Type 0: BIOS */
99		smbios_setenv("smbios.bios.vendor", dmi, 0x04);
100		smbios_setenv("smbios.bios.version", dmi, 0x05);
101		smbios_setenv("smbios.bios.reldate", dmi, 0x08);
102		break;
103
104	case 1:		/* Type 1: System */
105		smbios_setenv("smbios.system.maker", dmi, 0x04);
106		smbios_setenv("smbios.system.product", dmi, 0x05);
107		smbios_setenv("smbios.system.version", dmi, 0x06);
108		break;
109
110	case 2:		/* Type 2: Base Board (or Module) */
111		smbios_setenv("smbios.planar.maker", dmi, 0x04);
112		smbios_setenv("smbios.planar.product", dmi, 0x05);
113		smbios_setenv("smbios.planar.version", dmi, 0x06);
114		break;
115
116	case 3:		/* Type 3: System Enclosure or Chassis */
117		smbios_setenv("smbios.chassis.maker", dmi, 0x04);
118		smbios_setenv("smbios.chassis.version", dmi, 0x06);
119		break;
120
121	case 4:		/* Type 4: Processor Information */
122		/*
123		 * Offset 18h: Processor Status
124		 *
125		 * Bit 7	Reserved, must be 0
126		 * Bit 6	CPU Socket Populated
127		 *		1 - CPU Socket Populated
128		 *		0 - CPU Socket Unpopulated
129		 * Bit 5:3	Reserved, must be zero
130		 * Bit 2:0	CPU Status
131		 *		0h - Unknown
132		 *		1h - CPU Enabled
133		 *		2h - CPU Disabled by User via BIOS Setup
134		 *		3h - CPU Disabled by BIOS (POST Error)
135		 *		4h - CPU is Idle, waiting to be enabled
136		 *		5-6h - Reserved
137		 *		7h - Other
138		 */
139		if ((dmi[0x18] & 0x07) == 1)
140			smbios_enabled_sockets++;
141		if (dmi[0x18] & 0x40)
142			smbios_populated_sockets++;
143		break;
144
145	default: /* skip other types */
146		break;
147	}
148
149	/* find structure terminator */
150	dp = (u_int8_t *)(dmi + dmi[1]);
151	while (dp[0] != 0 || dp[1] != 0)
152		dp++;
153
154	return(dp + 2);
155}
156
157static void
158smbios_setenv(const char *str, const u_int8_t *dmi, const int offset)
159{
160	char		*cp;
161	int		i;
162
163	/* skip undefined string */
164	if (dmi[offset] == 0)
165		return;
166
167	for (cp = (char *)(dmi + dmi[1]), i = 0; i < dmi[offset] - 1; i++)
168		cp += strlen(cp) + 1;
169	setenv(str, cp, 1);
170}
171
172static u_int8_t
173smbios_checksum(const u_int8_t *addr, const u_int8_t len)
174{
175	u_int8_t	sum;
176	int		i;
177
178	for (sum = 0, i = 0; i < len; i++)
179		sum += addr[i];
180
181	return(sum);
182}
183
184static u_int8_t *
185smbios_sigsearch(const caddr_t addr, const u_int32_t len)
186{
187	caddr_t		cp;
188
189	/* search on 16-byte boundaries */
190	for (cp = addr; cp - addr < len; cp += SMBIOS_STEP) {
191		/* compare signature, validate checksum */
192		if (!strncmp(cp, SMBIOS_SIG, 4)) {
193			if (smbios_checksum(cp, *(cp + 0x05)))
194				continue;
195			if (strncmp(cp + 0x10, SMBIOS_DMI_SIG, 5))
196				continue;
197			if (smbios_checksum(cp + 0x10, 0x0f))
198				continue;
199
200			return(cp);
201		}
202	}
203
204	return(NULL);
205}
206