smbios.c revision 148006
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 148006 2005-07-14 19:52:22Z 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_parse_table(const u_int8_t *dmi);
58static void	smbios_setenv(const char *env, const u_int8_t *dmi,
59		    const int offset);
60static u_int8_t	smbios_checksum(const u_int8_t *addr, const u_int8_t len);
61static u_int8_t	*smbios_sigsearch(const caddr_t addr, const u_int32_t len);
62
63void
64smbios_detect(void)
65{
66	u_int8_t	*smbios, *dmi, *addr;
67	u_int16_t	i, length, count;
68	u_int32_t	paddr;
69
70	/* locate and validate the SMBIOS */
71	smbios = smbios_sigsearch(PTOV(SMBIOS_START), SMBIOS_LENGTH);
72	if (smbios == NULL)
73		return;
74
75	/* export values from the SMBIOS */
76	setenv("hint.smbios.0.enabled", "YES", 1);
77
78	length = *(u_int16_t *)(smbios + 0x16);	/* Structure Table Length */
79	paddr = *(u_int32_t *)(smbios + 0x18);	/* Structure Table Address */
80	count = *(u_int16_t *)(smbios + 0x1c);	/* No of SMBIOS Structures */
81
82	for (dmi = addr = PTOV(paddr), i = 0;
83	     dmi - addr < length && i < count; i++)
84		dmi = smbios_parse_table(dmi);
85}
86
87static u_int8_t *
88smbios_parse_table(const u_int8_t *dmi)
89{
90	u_int8_t	*dp;
91
92	switch(dmi[0]) {
93	case 0:		/* Type 0: BIOS */
94		smbios_setenv("hint.smbios.0.bios.vendor", dmi, 0x04);
95		smbios_setenv("hint.smbios.0.bios.version", dmi, 0x05);
96		smbios_setenv("hint.smbios.0.bios.reldate", dmi, 0x08);
97		break;
98
99	case 1:		/* Type 1: System */
100		smbios_setenv("hint.smbios.0.system.maker", dmi, 0x04);
101		smbios_setenv("hint.smbios.0.system.product", dmi, 0x05);
102		smbios_setenv("hint.smbios.0.system.version", dmi, 0x06);
103		break;
104
105	case 2:		/* Type 2: Base Board (or Module) */
106		smbios_setenv("hint.smbios.0.planar.maker", dmi, 0x04);
107		smbios_setenv("hint.smbios.0.planar.product", dmi, 0x05);
108		smbios_setenv("hint.smbios.0.planar.version", dmi, 0x06);
109		break;
110
111	case 3:		/* Type 3: System Enclosure or Chassis */
112		smbios_setenv("hint.smbios.0.chassis.maker", dmi, 0x04);
113		smbios_setenv("hint.smbios.0.chassis.version", dmi, 0x06);
114		break;
115
116	default: /* skip other types */
117		break;
118	}
119
120	/* find structure terminator */
121	dp = (u_int8_t *)(dmi + dmi[1]);
122	while (dp[0] != 0 || dp[1] != 0)
123		dp++;
124
125	return(dp + 2);
126}
127
128static void
129smbios_setenv(const char *str, const u_int8_t *dmi, const int offset)
130{
131	char		*cp;
132	int		i;
133
134	/* skip undefined string */
135	if (dmi[offset] == 0)
136		return;
137
138	for (cp = (char *)(dmi + dmi[1]), i = 0; i < dmi[offset] - 1; i++)
139		cp += strlen(cp) + 1;
140	setenv(str, cp, 1);
141}
142
143static u_int8_t
144smbios_checksum(const u_int8_t *addr, const u_int8_t len)
145{
146	u_int8_t	sum;
147	int		i;
148
149	for (sum = 0, i = 0; i < len; i++)
150		sum += addr[i];
151
152	return(sum);
153}
154
155static u_int8_t *
156smbios_sigsearch(const caddr_t addr, const u_int32_t len)
157{
158	caddr_t		cp;
159
160	/* search on 16-byte boundaries */
161	for (cp = addr; cp - addr < len; cp += SMBIOS_STEP) {
162		/* compare signature, validate checksum */
163		if (!strncmp(cp, SMBIOS_SIG, 4)) {
164			if (smbios_checksum(cp, *(cp + 0x05)))
165				continue;
166			if (strncmp(cp + 0x10, SMBIOS_DMI_SIG, 5))
167				continue;
168			if (smbios_checksum(cp + 0x10, 0x0f))
169				continue;
170
171			return(cp);
172		}
173	}
174
175	return(NULL);
176}
177