smbios.c revision 127135
1112553Smdodd/*-
2112553Smdodd * Copyright (c) 2003 Matthew N. Dodd <winter@jurai.net>
3112553Smdodd * All rights reserved.
4112553Smdodd *
5112553Smdodd * Redistribution and use in source and binary forms, with or without
6112553Smdodd * modification, are permitted provided that the following conditions
7112553Smdodd * are met:
8112553Smdodd * 1. Redistributions of source code must retain the above copyright
9112553Smdodd *    notice, this list of conditions and the following disclaimer.
10112553Smdodd * 2. Redistributions in binary form must reproduce the above copyright
11112553Smdodd *    notice, this list of conditions and the following disclaimer in the
12112553Smdodd *    documentation and/or other materials provided with the distribution.
13112553Smdodd *
14112553Smdodd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15112553Smdodd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16112553Smdodd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17112553Smdodd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18112553Smdodd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19112553Smdodd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20112553Smdodd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21112553Smdodd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22112553Smdodd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23112553Smdodd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24112553Smdodd * SUCH DAMAGE.
25112553Smdodd */
26112553Smdodd
27115679Sobrien#include <sys/cdefs.h>
28115679Sobrien__FBSDID("$FreeBSD: head/sys/i386/bios/smbios.c 127135 2004-03-17 17:50:55Z njl $");
29115679Sobrien
30112553Smdodd#include <sys/param.h>
31112553Smdodd#include <sys/systm.h>
32112553Smdodd#include <sys/kernel.h>
33112553Smdodd#include <sys/socket.h>
34112553Smdodd
35112553Smdodd#include <sys/module.h>
36112553Smdodd#include <sys/bus.h>
37112553Smdodd
38112553Smdodd#include <machine/bus.h>
39112553Smdodd#include <machine/resource.h>
40112553Smdodd#include <sys/rman.h>
41112553Smdodd
42112553Smdodd#include <vm/vm.h>
43112553Smdodd#include <vm/pmap.h>
44112553Smdodd#include <machine/md_var.h>
45112553Smdodd#include <machine/pc/bios.h>
46112553Smdodd
47112553Smdodd/*
48112553Smdodd * SMBIOS Entry Point Structure
49112553Smdodd */
50112553Smdoddstruct smbios_eps {
51112553Smdodd	u_int8_t	Anchor[4];		/* '_SM_' */
52112553Smdodd	u_int8_t	Checksum;
53112553Smdodd	u_int8_t	Length;
54112553Smdodd
55112553Smdodd	u_int8_t	SMBIOS_Major;
56112553Smdodd	u_int8_t	SMBIOS_Minor;
57112553Smdodd	u_int8_t	Max_Size;
58112553Smdodd	u_int8_t	Revision;
59112553Smdodd	u_int8_t	Formatted_Area;
60112553Smdodd
61112553Smdodd	u_int8_t	Intermediate_Anchor[5];	/* '_DMI_' */
62112553Smdodd	u_int8_t	Intermediate_Checksum;
63112553Smdodd
64112553Smdodd	u_int16_t	Structure_Table_Length;
65112553Smdodd	u_int32_t	Structure_Table_Address;
66112553Smdodd	u_int16_t	Structure_Count;
67112553Smdodd
68112553Smdodd	u_int8_t	SMBIOS_BCD_Revision;
69112553Smdodd} __packed;
70112553Smdodd
71112553Smdoddstruct smbios_softc {
72112553Smdodd	device_t		dev;
73112553Smdodd	struct resource *	res;
74112553Smdodd	int			rid;
75112553Smdodd
76112553Smdodd	struct smbios_eps *	eps;
77112553Smdodd};
78112553Smdodd
79112553Smdodd#define	SMBIOS_START	0xf0000
80112553Smdodd#define	SMBIOS_STEP	0x10
81112553Smdodd#define	SMBIOS_OFF	0
82112553Smdodd#define	SMBIOS_LEN	4
83112553Smdodd#define	SMBIOS_SIG	"_SM_"
84112553Smdodd
85112553Smdodd#define	RES2EPS(res)	((struct smbios_eps *)rman_get_virtual(res))
86112553Smdodd#define	ADDR2EPS(addr)  ((struct smbios_eps *)BIOS_PADDRTOVADDR(addr))
87112553Smdodd
88112553Smdoddstatic devclass_t	smbios_devclass;
89112553Smdodd
90112553Smdoddstatic void	smbios_identify	(driver_t *, device_t);
91112553Smdoddstatic int	smbios_probe	(device_t);
92112553Smdoddstatic int	smbios_attach	(device_t);
93112553Smdoddstatic int	smbios_detach	(device_t);
94112553Smdoddstatic int	smbios_modevent	(module_t, int, void *);
95112553Smdodd
96112553Smdoddstatic int	smbios_cksum	(struct smbios_eps *);
97112553Smdodd
98112553Smdoddstatic void
99112553Smdoddsmbios_identify (driver_t *driver, device_t parent)
100112553Smdodd{
101112553Smdodd	device_t child;
102112553Smdodd	u_int32_t addr;
103112553Smdodd	int length;
104112553Smdodd	int rid;
105112553Smdodd
106112553Smdodd	if (!device_is_alive(parent))
107112553Smdodd		return;
108112553Smdodd
109112553Smdodd	addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN,
110112553Smdodd			      SMBIOS_STEP, SMBIOS_OFF);
111112553Smdodd	if (addr != 0) {
112112553Smdodd		rid = 0;
113112553Smdodd		length = ADDR2EPS(addr)->Length;
114112553Smdodd
115112553Smdodd		child = BUS_ADD_CHILD(parent, 0, "smbios", -1);
116112553Smdodd		device_set_driver(child, driver);
117112553Smdodd		bus_set_resource(child, SYS_RES_MEMORY, rid, addr, length);
118112553Smdodd		device_set_desc(child, "System Management BIOS");
119112553Smdodd	}
120112553Smdodd
121112553Smdodd	return;
122112553Smdodd}
123112553Smdodd
124112553Smdoddstatic int
125112553Smdoddsmbios_probe (device_t dev)
126112553Smdodd{
127112553Smdodd	struct resource *res;
128112553Smdodd	int rid;
129112553Smdodd	int error;
130112553Smdodd
131112553Smdodd	error = 0;
132112553Smdodd	rid = 0;
133127135Snjl	res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
134112553Smdodd	if (res == NULL) {
135112553Smdodd		device_printf(dev, "Unable to allocate memory resource.\n");
136112553Smdodd		error = ENOMEM;
137112553Smdodd		goto bad;
138112553Smdodd	}
139112553Smdodd
140112553Smdodd	if (smbios_cksum(RES2EPS(res))) {
141112553Smdodd		device_printf(dev, "SMBIOS checksum failed.\n");
142112553Smdodd		error = ENXIO;
143112553Smdodd		goto bad;
144112553Smdodd	}
145112553Smdodd
146112553Smdoddbad:
147112553Smdodd	if (res)
148112553Smdodd		bus_release_resource(dev, SYS_RES_MEMORY, rid, res);
149112553Smdodd	return (error);
150112553Smdodd}
151112553Smdodd
152112553Smdoddstatic int
153112553Smdoddsmbios_attach (device_t dev)
154112553Smdodd{
155112553Smdodd	struct smbios_softc *sc;
156112553Smdodd	int error;
157112553Smdodd
158112553Smdodd	sc = device_get_softc(dev);
159112553Smdodd	error = 0;
160112553Smdodd
161112553Smdodd	sc->dev = dev;
162112553Smdodd	sc->rid = 0;
163127135Snjl	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
164127135Snjl		RF_ACTIVE);
165112553Smdodd	if (sc->res == NULL) {
166112553Smdodd		device_printf(dev, "Unable to allocate memory resource.\n");
167112553Smdodd		error = ENOMEM;
168112553Smdodd		goto bad;
169112553Smdodd	}
170112553Smdodd	sc->eps = RES2EPS(sc->res);
171112553Smdodd
172112553Smdodd	device_printf(dev, "Version: %d.%02d",
173112553Smdodd		sc->eps->SMBIOS_Major, sc->eps->SMBIOS_Minor);
174112553Smdodd	if (bcd2bin(sc->eps->SMBIOS_BCD_Revision))
175112553Smdodd		printf(", Revision: %d.%02d",
176112553Smdodd			bcd2bin(sc->eps->SMBIOS_BCD_Revision >> 4),
177112553Smdodd			bcd2bin(sc->eps->SMBIOS_BCD_Revision & 0x0f));
178112553Smdodd	printf("\n");
179112553Smdodd
180112553Smdodd	return (0);
181112553Smdoddbad:
182112553Smdodd	if (sc->res)
183112553Smdodd		bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
184112553Smdodd	return (error);
185112553Smdodd}
186112553Smdodd
187112553Smdoddstatic int
188112553Smdoddsmbios_detach (device_t dev)
189112553Smdodd{
190112553Smdodd	struct smbios_softc *sc;
191112553Smdodd
192112553Smdodd	sc = device_get_softc(dev);
193112553Smdodd
194112553Smdodd	if (sc->res)
195112553Smdodd		bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
196112553Smdodd
197112553Smdodd	return (0);
198112553Smdodd}
199112553Smdodd
200112553Smdoddstatic int
201112553Smdoddsmbios_modevent (mod, what, arg)
202112553Smdodd        module_t        mod;
203112553Smdodd        int             what;
204112553Smdodd        void *          arg;
205112553Smdodd{
206112553Smdodd	device_t *	devs;
207112553Smdodd	int		count;
208112553Smdodd	int		i;
209112553Smdodd
210112553Smdodd	switch (what) {
211112553Smdodd	case MOD_LOAD:
212112553Smdodd		break;
213112553Smdodd	case MOD_UNLOAD:
214112553Smdodd		devclass_get_devices(smbios_devclass, &devs, &count);
215112553Smdodd		for (i = 0; i < count; i++) {
216112553Smdodd			device_delete_child(device_get_parent(devs[i]), devs[i]);
217112553Smdodd		}
218112553Smdodd		break;
219112553Smdodd	default:
220112553Smdodd		break;
221112553Smdodd	}
222112553Smdodd
223112553Smdodd	return (0);
224112553Smdodd}
225112553Smdodd
226112553Smdoddstatic device_method_t smbios_methods[] = {
227112553Smdodd	/* Device interface */
228112553Smdodd	DEVMETHOD(device_identify,      smbios_identify),
229112553Smdodd	DEVMETHOD(device_probe,         smbios_probe),
230112553Smdodd	DEVMETHOD(device_attach,        smbios_attach),
231112553Smdodd	DEVMETHOD(device_detach,        smbios_detach),
232112553Smdodd	{ 0, 0 }
233112553Smdodd};
234112553Smdodd
235112553Smdoddstatic driver_t smbios_driver = {
236112553Smdodd	"smbios",
237112553Smdodd	smbios_methods,
238112553Smdodd	sizeof(struct smbios_softc),
239112553Smdodd};
240112553Smdodd
241112553SmdoddDRIVER_MODULE(smbios, nexus, smbios_driver, smbios_devclass, smbios_modevent, 0);
242112553SmdoddMODULE_VERSION(smbios, 1);
243112553Smdodd
244112553Smdoddstatic int
245112553Smdoddsmbios_cksum (struct smbios_eps *e)
246112553Smdodd{
247112553Smdodd	u_int8_t *ptr;
248112553Smdodd	u_int8_t cksum;
249112553Smdodd	int i;
250112553Smdodd
251112553Smdodd	ptr = (u_int8_t *)e;
252112553Smdodd	cksum = 0;
253112553Smdodd	for (i = 0; i < e->Length; i++) {
254112553Smdodd		cksum += ptr[i];
255112553Smdodd	}
256112553Smdodd
257112553Smdodd	return (cksum);
258112553Smdodd}
259