smbios.c revision 196403
1/*-
2 * Copyright (c) 2003 Matthew N. Dodd <winter@jurai.net>
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/i386/bios/smbios.c 196403 2009-08-20 19:17:53Z jhb $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/socket.h>
34
35#include <sys/module.h>
36#include <sys/bus.h>
37
38#include <machine/bus.h>
39#include <machine/resource.h>
40#include <sys/rman.h>
41
42#include <vm/vm.h>
43#include <vm/vm_param.h>
44#include <vm/pmap.h>
45#include <machine/md_var.h>
46#include <machine/pc/bios.h>
47
48/*
49 * System Management BIOS Reference Specification, v2.4 Final
50 * http://www.dmtf.org/standards/published_documents/DSP0134.pdf
51 */
52
53/*
54 * SMBIOS Entry Point Structure
55 */
56struct smbios_eps {
57	u_int8_t	Anchor[4];		/* '_SM_' */
58	u_int8_t	Checksum;
59	u_int8_t	Length;
60
61	u_int8_t	SMBIOS_Major;
62	u_int8_t	SMBIOS_Minor;
63	u_int16_t	Max_Size;
64	u_int8_t	Revision;
65	u_int8_t	Formatted_Area[5];
66
67	u_int8_t	Intermediate_Anchor[5];	/* '_DMI_' */
68	u_int8_t	Intermediate_Checksum;
69
70	u_int16_t	Structure_Table_Length;
71	u_int32_t	Structure_Table_Address;
72	u_int16_t	Structure_Count;
73
74	u_int8_t	SMBIOS_BCD_Revision;
75} __packed;
76
77struct smbios_softc {
78	device_t		dev;
79	struct resource *	res;
80	int			rid;
81
82	struct smbios_eps *	eps;
83};
84
85#define	SMBIOS_START	0xf0000
86#define	SMBIOS_STEP	0x10
87#define	SMBIOS_OFF	0
88#define	SMBIOS_LEN	4
89#define	SMBIOS_SIG	"_SM_"
90
91#define	RES2EPS(res)	((struct smbios_eps *)rman_get_virtual(res))
92#define	ADDR2EPS(addr)  ((struct smbios_eps *)BIOS_PADDRTOVADDR(addr))
93
94static devclass_t	smbios_devclass;
95
96static void	smbios_identify	(driver_t *, device_t);
97static int	smbios_probe	(device_t);
98static int	smbios_attach	(device_t);
99static int	smbios_detach	(device_t);
100static int	smbios_modevent	(module_t, int, void *);
101
102static int	smbios_cksum	(struct smbios_eps *);
103
104static void
105smbios_identify (driver_t *driver, device_t parent)
106{
107	device_t child;
108	u_int32_t addr;
109	int length;
110	int rid;
111
112	if (!device_is_alive(parent))
113		return;
114
115	addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN,
116			      SMBIOS_STEP, SMBIOS_OFF);
117	if (addr != 0) {
118		rid = 0;
119		length = ADDR2EPS(addr)->Length;
120
121		if (length != 0x1f) {
122			u_int8_t major, minor;
123
124			major = ADDR2EPS(addr)->SMBIOS_Major;
125			minor = ADDR2EPS(addr)->SMBIOS_Minor;
126
127			/* SMBIOS v2.1 implementation might use 0x1e. */
128			if (length == 0x1e && major == 2 && minor == 1)
129				length = 0x1f;
130			else
131				return;
132		}
133
134		child = BUS_ADD_CHILD(parent, 5, "smbios", -1);
135		device_set_driver(child, driver);
136		bus_set_resource(child, SYS_RES_MEMORY, rid, addr, length);
137		device_set_desc(child, "System Management BIOS");
138	}
139
140	return;
141}
142
143static int
144smbios_probe (device_t dev)
145{
146	struct resource *res;
147	int rid;
148	int error;
149
150	error = 0;
151	rid = 0;
152	res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
153	if (res == NULL) {
154		device_printf(dev, "Unable to allocate memory resource.\n");
155		error = ENOMEM;
156		goto bad;
157	}
158
159	if (smbios_cksum(RES2EPS(res))) {
160		device_printf(dev, "SMBIOS checksum failed.\n");
161		error = ENXIO;
162		goto bad;
163	}
164
165bad:
166	if (res)
167		bus_release_resource(dev, SYS_RES_MEMORY, rid, res);
168	return (error);
169}
170
171static int
172smbios_attach (device_t dev)
173{
174	struct smbios_softc *sc;
175	int error;
176
177	sc = device_get_softc(dev);
178	error = 0;
179
180	sc->dev = dev;
181	sc->rid = 0;
182	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
183		RF_ACTIVE);
184	if (sc->res == NULL) {
185		device_printf(dev, "Unable to allocate memory resource.\n");
186		error = ENOMEM;
187		goto bad;
188	}
189	sc->eps = RES2EPS(sc->res);
190
191	device_printf(dev, "Version: %u.%u",
192		sc->eps->SMBIOS_Major, sc->eps->SMBIOS_Minor);
193	if (bcd2bin(sc->eps->SMBIOS_BCD_Revision))
194		printf(", BCD Revision: %u.%u",
195			bcd2bin(sc->eps->SMBIOS_BCD_Revision >> 4),
196			bcd2bin(sc->eps->SMBIOS_BCD_Revision & 0x0f));
197	printf("\n");
198
199	return (0);
200bad:
201	if (sc->res)
202		bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
203	return (error);
204}
205
206static int
207smbios_detach (device_t dev)
208{
209	struct smbios_softc *sc;
210
211	sc = device_get_softc(dev);
212
213	if (sc->res)
214		bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
215
216	return (0);
217}
218
219static int
220smbios_modevent (mod, what, arg)
221        module_t        mod;
222        int             what;
223        void *          arg;
224{
225	device_t *	devs;
226	int		count;
227	int		i;
228
229	switch (what) {
230	case MOD_LOAD:
231		break;
232	case MOD_UNLOAD:
233		devclass_get_devices(smbios_devclass, &devs, &count);
234		for (i = 0; i < count; i++) {
235			device_delete_child(device_get_parent(devs[i]), devs[i]);
236		}
237		break;
238	default:
239		break;
240	}
241
242	return (0);
243}
244
245static device_method_t smbios_methods[] = {
246	/* Device interface */
247	DEVMETHOD(device_identify,      smbios_identify),
248	DEVMETHOD(device_probe,         smbios_probe),
249	DEVMETHOD(device_attach,        smbios_attach),
250	DEVMETHOD(device_detach,        smbios_detach),
251	{ 0, 0 }
252};
253
254static driver_t smbios_driver = {
255	"smbios",
256	smbios_methods,
257	sizeof(struct smbios_softc),
258};
259
260DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_devclass, smbios_modevent, 0);
261MODULE_VERSION(smbios, 1);
262
263static int
264smbios_cksum (struct smbios_eps *e)
265{
266	u_int8_t *ptr;
267	u_int8_t cksum;
268	int i;
269
270	ptr = (u_int8_t *)e;
271	cksum = 0;
272	for (i = 0; i < e->Length; i++) {
273		cksum += ptr[i];
274	}
275
276	return (cksum);
277}
278