xref: /freebsd/sys/dev/smbios/smbios.c (revision 637e4ef1a6bcd8677dabd8c08916df3091be5f64)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2003 Matthew N. Dodd <winter@jurai.net>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/socket.h>
35 #include <sys/efi.h>
36 
37 #include <sys/module.h>
38 #include <sys/bus.h>
39 
40 #include <machine/bus.h>
41 #include <machine/resource.h>
42 #include <sys/rman.h>
43 
44 #include <vm/vm.h>
45 #include <vm/vm_param.h>
46 #include <vm/pmap.h>
47 #include <machine/md_var.h>
48 #if defined(__amd64__) || defined(__i386__)
49 #include <machine/pc/bios.h>
50 #endif
51 #include <dev/smbios/smbios.h>
52 
53 /*
54  * System Management BIOS Reference Specification, v2.4 Final
55  * http://www.dmtf.org/standards/published_documents/DSP0134.pdf
56  */
57 
58 struct smbios_softc {
59 	device_t		dev;
60 	union {
61 		struct smbios_eps *	eps;
62 		struct smbios3_eps *	eps3;
63 	};
64 	bool is_eps3;
65 };
66 
67 static void	smbios_identify	(driver_t *, device_t);
68 static int	smbios_probe	(device_t);
69 static int	smbios_attach	(device_t);
70 static int	smbios_detach	(device_t);
71 static int	smbios_modevent	(module_t, int, void *);
72 
73 static int	smbios_cksum	(void *);
74 static bool	smbios_eps3	(void *);
75 
76 static void
77 smbios_identify (driver_t *driver, device_t parent)
78 {
79 #ifdef ARCH_MAY_USE_EFI
80 	struct uuid efi_smbios = EFI_TABLE_SMBIOS;
81 	struct uuid efi_smbios3 = EFI_TABLE_SMBIOS3;
82 	void *addr_efi;
83 #endif
84 	struct smbios_eps *eps;
85 	struct smbios3_eps *eps3;
86 	void *ptr;
87 	device_t child;
88 	vm_paddr_t addr = 0;
89 	size_t map_size = sizeof (*eps);
90 	int length;
91 
92 	if (!device_is_alive(parent))
93 		return;
94 
95 #ifdef ARCH_MAY_USE_EFI
96 	if (!efi_get_table(&efi_smbios3, &addr_efi)) {
97 		addr = (vm_paddr_t)addr_efi;
98 		map_size = sizeof (*eps3);
99 	} else if (!efi_get_table(&efi_smbios, &addr_efi)) {
100 		addr = (vm_paddr_t)addr_efi;
101 	}
102 
103 #endif
104 
105 #if defined(__amd64__) || defined(__i386__)
106 	if (addr == 0)
107 		addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN,
108 		    SMBIOS_STEP, SMBIOS_OFF);
109 #endif
110 
111 	if (addr != 0) {
112 		ptr = pmap_mapbios(addr, map_size);
113 		if (ptr == NULL)
114 			return;
115 		if (map_size == sizeof (*eps3)) {
116 			eps3 = ptr;
117 			length = eps3->length;
118 			if (memcmp(eps3->anchor_string,
119 			    SMBIOS3_SIG, SMBIOS3_LEN) != 0) {
120 				printf("smbios3: corrupt sig %s found\n",
121 				    eps3->anchor_string);
122 				return;
123 			}
124 		} else {
125 			eps = ptr;
126 			length = eps->length;
127 			if (memcmp(eps->anchor_string,
128 			    SMBIOS_SIG, SMBIOS_LEN) != 0) {
129 				printf("smbios: corrupt sig %s found\n",
130 				    eps->anchor_string);
131 				return;
132 			}
133 		}
134 		if (length != map_size) {
135 			u_int8_t major, minor;
136 
137 			major = eps->major_version;
138 			minor = eps->minor_version;
139 
140 			/* SMBIOS v2.1 implementation might use 0x1e. */
141 			if (length == 0x1e && major == 2 && minor == 1) {
142 				length = 0x1f;
143 			} else {
144 				pmap_unmapbios(eps, map_size);
145 				return;
146 			}
147 		}
148 
149 		child = BUS_ADD_CHILD(parent, 5, "smbios", -1);
150 		device_set_driver(child, driver);
151 
152 		/* smuggle the phys addr into probe and attach */
153 		bus_set_resource(child, SYS_RES_MEMORY, 0, addr, length);
154 		device_set_desc(child, "System Management BIOS");
155 		pmap_unmapbios(ptr, map_size);
156 	}
157 
158 	return;
159 }
160 
161 static int
162 smbios_probe (device_t dev)
163 {
164 	vm_paddr_t pa;
165 	vm_size_t size;
166 	void *va;
167 	int error;
168 
169 	error = 0;
170 
171 	pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
172 	size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0);
173 	va = pmap_mapbios(pa, size);
174 	if (va == NULL) {
175 		device_printf(dev, "Unable to map memory.\n");
176 		return (ENOMEM);
177 	}
178 
179 	if (smbios_cksum(va)) {
180 		device_printf(dev, "SMBIOS checksum failed.\n");
181 		error = ENXIO;
182 	}
183 
184 	pmap_unmapbios(va, size);
185 	return (error);
186 }
187 
188 static int
189 smbios_attach (device_t dev)
190 {
191 	struct smbios_softc *sc;
192 	void *va;
193 	vm_paddr_t pa;
194 	vm_size_t size;
195 
196 	sc = device_get_softc(dev);
197 	sc->dev = dev;
198 	pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
199 	size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0);
200 	va = pmap_mapbios(pa, size);
201 	if (va == NULL) {
202 		device_printf(dev, "Unable to map memory.\n");
203 		return (ENOMEM);
204 	}
205 	sc->is_eps3 = smbios_eps3(va);
206 
207 	if (sc->is_eps3) {
208 		sc->eps3 = va;
209 		device_printf(dev, "Version: %u.%u",
210 		    sc->eps3->major_version, sc->eps3->minor_version);
211 	} else {
212 		sc->eps = va;
213 		device_printf(dev, "Version: %u.%u",
214 		    sc->eps->major_version, sc->eps->minor_version);
215 		if (bcd2bin(sc->eps->BCD_revision))
216 			printf(", BCD Revision: %u.%u",
217 			    bcd2bin(sc->eps->BCD_revision >> 4),
218 			    bcd2bin(sc->eps->BCD_revision & 0x0f));
219 	}
220 	printf("\n");
221 	return (0);
222 }
223 
224 static int
225 smbios_detach (device_t dev)
226 {
227 	struct smbios_softc *sc;
228 	vm_size_t size;
229 	void *va;
230 
231 	sc = device_get_softc(dev);
232 	va = (sc->is_eps3 ? (void *)sc->eps3 : (void *)sc->eps);
233 	if (sc->is_eps3)
234 		va = sc->eps3;
235 	else
236 		va = sc->eps;
237 	size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0);
238 
239 	if (va != NULL)
240 		pmap_unmapbios(va, size);
241 
242 	return (0);
243 }
244 
245 static int
246 smbios_modevent (module_t mod, int what, void *arg)
247 {
248 	device_t *	devs;
249 	int		count;
250 	int		i;
251 
252 	switch (what) {
253 	case MOD_LOAD:
254 		break;
255 	case MOD_UNLOAD:
256 		devclass_get_devices(devclass_find("smbios"), &devs, &count);
257 		for (i = 0; i < count; i++) {
258 			device_delete_child(device_get_parent(devs[i]), devs[i]);
259 		}
260 		free(devs, M_TEMP);
261 		break;
262 	default:
263 		break;
264 	}
265 
266 	return (0);
267 }
268 
269 static device_method_t smbios_methods[] = {
270 	/* Device interface */
271 	DEVMETHOD(device_identify,      smbios_identify),
272 	DEVMETHOD(device_probe,         smbios_probe),
273 	DEVMETHOD(device_attach,        smbios_attach),
274 	DEVMETHOD(device_detach,        smbios_detach),
275 	{ 0, 0 }
276 };
277 
278 static driver_t smbios_driver = {
279 	"smbios",
280 	smbios_methods,
281 	sizeof(struct smbios_softc),
282 };
283 
284 DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_modevent, NULL);
285 #ifdef ARCH_MAY_USE_EFI
286 MODULE_DEPEND(smbios, efirt, 1, 1, 1);
287 #endif
288 MODULE_VERSION(smbios, 1);
289 
290 
291 static bool
292 smbios_eps3 (void *v)
293 {
294 	struct smbios3_eps *e;
295 
296 	e = (struct smbios3_eps *)v;
297 	return (memcmp(e->anchor_string, SMBIOS3_SIG, SMBIOS3_LEN) == 0);
298 }
299 
300 static int
301 smbios_cksum (void *v)
302 {
303 	struct smbios3_eps *eps3;
304 	struct smbios_eps *eps;
305 	u_int8_t *ptr;
306 	u_int8_t cksum;
307 	u_int8_t length;
308 	int i;
309 
310 	if (smbios_eps3(v)) {
311 		eps3 = (struct smbios3_eps *)v;
312 		length = eps3->length;
313 	} else {
314 		eps = (struct smbios_eps *)v;
315 		length = eps->length;
316 	}
317 	ptr = (u_int8_t *)v;
318 	cksum = 0;
319 	for (i = 0; i < length; i++) {
320 		cksum += ptr[i];
321 	}
322 
323 	return (cksum);
324 }
325