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