xref: /freebsd/sys/dev/smbios/smbios.c (revision 6366114c716f9dca099a4090269a700b94a6173f)
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/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/socket.h>
34 #include <sys/efi.h>
35 
36 #include <sys/module.h>
37 #include <sys/bus.h>
38 
39 #include <machine/bus.h>
40 #include <machine/resource.h>
41 #include <sys/rman.h>
42 
43 #include <vm/vm.h>
44 #include <vm/vm_param.h>
45 #include <vm/pmap.h>
46 #include <machine/md_var.h>
47 #if defined(__amd64__) || defined(__i386__)
48 #include <machine/pc/bios.h>
49 #endif
50 #include <dev/smbios/smbios.h>
51 
52 /*
53  * System Management BIOS Reference Specification, v2.4 Final
54  * http://www.dmtf.org/standards/published_documents/DSP0134.pdf
55  */
56 
57 struct smbios_softc {
58 	device_t		dev;
59 	union {
60 		struct smbios_eps *	eps;
61 		struct smbios3_eps *	eps3;
62 	};
63 	bool is_eps3;
64 };
65 
66 static void	smbios_identify	(driver_t *, device_t);
67 static int	smbios_probe	(device_t);
68 static int	smbios_attach	(device_t);
69 static int	smbios_detach	(device_t);
70 static int	smbios_modevent	(module_t, int, void *);
71 
72 static int	smbios_cksum	(void *);
73 static bool	smbios_eps3	(void *);
74 
75 static void
smbios_identify(driver_t * driver,device_t parent)76 smbios_identify (driver_t *driver, device_t parent)
77 {
78 #ifdef ARCH_MAY_USE_EFI
79 	efi_guid_t efi_smbios = EFI_TABLE_SMBIOS;
80 	efi_guid_t efi_smbios3 = EFI_TABLE_SMBIOS3;
81 	void *addr_efi;
82 #endif
83 	struct smbios_eps *eps;
84 	struct smbios3_eps *eps3;
85 	void *ptr;
86 	device_t child;
87 	vm_paddr_t addr = 0;
88 	size_t map_size = sizeof(*eps);
89 	uint8_t length;
90 
91 	if (!device_is_alive(parent))
92 		return;
93 
94 #ifdef ARCH_MAY_USE_EFI
95 	if (!efi_get_table(&efi_smbios3, &addr_efi)) {
96 		addr = (vm_paddr_t)addr_efi;
97 		map_size = sizeof(*eps3);
98 	} else if (!efi_get_table(&efi_smbios, &addr_efi)) {
99 		addr = (vm_paddr_t)addr_efi;
100 	}
101 
102 #endif
103 
104 #if defined(__amd64__) || defined(__i386__)
105 	if (addr == 0) {
106 		addr = bios_sigsearch(SMBIOS_START, SMBIOS3_SIG, SMBIOS3_LEN,
107 		    SMBIOS_STEP, SMBIOS_OFF);
108 		if (addr != 0)
109 			map_size = sizeof(*eps3);
110 		else
111 			addr = bios_sigsearch(SMBIOS_START,
112 			    SMBIOS_SIG, SMBIOS_LEN, SMBIOS_STEP, SMBIOS_OFF);
113 	}
114 #endif
115 
116 	if (addr == 0)
117 		return;
118 
119 	ptr = pmap_mapbios(addr, map_size);
120 	if (ptr == NULL) {
121 		printf("smbios: Unable to map memory.\n");
122 		return;
123 	}
124 	if (map_size == sizeof(*eps3)) {
125 		eps3 = ptr;
126 		length = eps3->length;
127 		if (memcmp(eps3->anchor_string, SMBIOS3_SIG, SMBIOS3_LEN) != 0)
128 			goto corrupt_sig;
129 	} else {
130 		eps = ptr;
131 		length = eps->length;
132 		if (memcmp(eps->anchor_string, SMBIOS_SIG, SMBIOS_LEN) != 0)
133 			goto corrupt_sig;
134 	}
135 	if (length != map_size) {
136 		/*
137 		 * SMBIOS v2.1 implementations might use 0x1e because the
138 		 * standard was then erroneous.
139 		 */
140 		if (length == 0x1e && map_size == sizeof(*eps) &&
141 		    eps->major_version == 2 && eps->minor_version == 1)
142 			length = map_size;
143 		else {
144 			printf("smbios: %s-bit Entry Point: Invalid length: "
145 			    "Got %hhu, expected %zu\n",
146 			    map_size == sizeof(*eps3) ? "64" : "32",
147 			    length, map_size);
148 			goto unmap_return;
149 		}
150 	}
151 
152 	child = BUS_ADD_CHILD(parent, 5, "smbios", DEVICE_UNIT_ANY);
153 	device_set_driver(child, driver);
154 
155 	/* smuggle the phys addr into probe and attach */
156 	bus_set_resource(child, SYS_RES_MEMORY, 0, addr, length);
157 	device_set_desc(child, "System Management BIOS");
158 
159 unmap_return:
160 	pmap_unmapbios(ptr, map_size);
161 	return;
162 
163 corrupt_sig:
164 	{
165 		const char *sig;
166 	        const char *table_ver_str;
167 		size_t i, end;
168 
169 		if (map_size == sizeof(*eps3)) {
170 			sig = eps3->anchor_string;
171 			table_ver_str = "64";
172 			end = SMBIOS3_LEN;
173 		} else {
174 			sig = eps->anchor_string;
175 			table_ver_str = "32";
176 			end = SMBIOS_LEN;
177 		}
178 
179 		/* Space after ':' printed by the loop. */
180 		printf("smbios: %s-bit Entry Point: Corrupt signature (hex):",
181 		    table_ver_str);
182 		for (i = 0; i < end; ++i)
183 			printf(" %02hhx", sig[i]);
184 		printf("\n");
185 	}
186 	goto unmap_return;
187 }
188 
189 static int
smbios_probe(device_t dev)190 smbios_probe (device_t dev)
191 {
192 	vm_paddr_t pa;
193 	vm_size_t size;
194 	void *va;
195 	int error;
196 
197 	error = 0;
198 
199 	pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
200 	size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0);
201 	va = pmap_mapbios(pa, size);
202 	if (va == NULL) {
203 		device_printf(dev, "Unable to map memory.\n");
204 		return (ENOMEM);
205 	}
206 
207 	if (smbios_cksum(va)) {
208 		device_printf(dev, "SMBIOS checksum failed.\n");
209 		error = ENXIO;
210 	}
211 
212 	pmap_unmapbios(va, size);
213 	return (error);
214 }
215 
216 static int
smbios_attach(device_t dev)217 smbios_attach (device_t dev)
218 {
219 	struct smbios_softc *sc;
220 	void *va;
221 	vm_paddr_t pa;
222 	vm_size_t size;
223 
224 	sc = device_get_softc(dev);
225 	sc->dev = dev;
226 	pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
227 	size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0);
228 	va = pmap_mapbios(pa, size);
229 	if (va == NULL) {
230 		device_printf(dev, "Unable to map memory.\n");
231 		return (ENOMEM);
232 	}
233 	sc->is_eps3 = smbios_eps3(va);
234 
235 	if (sc->is_eps3) {
236 		sc->eps3 = va;
237 		device_printf(dev, "Entry point: v3 (64-bit), Version: %u.%u\n",
238 		    sc->eps3->major_version, sc->eps3->minor_version);
239 		if (bootverbose)
240 			device_printf(dev,
241 			    "Docrev: %u, Entry Point Revision: %u\n",
242 			    sc->eps3->docrev, sc->eps3->entry_point_revision);
243 	} else {
244 		const struct smbios_eps *const eps = va;
245 		const uint8_t bcd = eps->BCD_revision;
246 
247 		sc->eps = va;
248 		device_printf(dev, "Entry point: v2.1 (32-bit), Version: %u.%u",
249 		    eps->major_version, eps->minor_version);
250 		if (bcd < LIBKERN_LEN_BCD2BIN && bcd2bin(bcd) != 0)
251 			printf(", BCD Revision: %u.%u\n",
252 			    bcd2bin(bcd >> 4), bcd2bin(bcd & 0x0f));
253 		else
254 			printf("\n");
255 		if (bootverbose)
256 			device_printf(dev, "Entry Point Revision: %u\n",
257 			    eps->entry_point_revision);
258 	}
259 	return (0);
260 }
261 
262 static int
smbios_detach(device_t dev)263 smbios_detach (device_t dev)
264 {
265 	struct smbios_softc *sc;
266 	vm_size_t size;
267 	void *va;
268 
269 	sc = device_get_softc(dev);
270 	va = (sc->is_eps3 ? (void *)sc->eps3 : (void *)sc->eps);
271 	if (sc->is_eps3)
272 		va = sc->eps3;
273 	else
274 		va = sc->eps;
275 	size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0);
276 
277 	if (va != NULL)
278 		pmap_unmapbios(va, size);
279 
280 	return (0);
281 }
282 
283 static int
smbios_modevent(module_t mod,int what,void * arg)284 smbios_modevent (module_t mod, int what, void *arg)
285 {
286 	device_t *	devs;
287 	int		count;
288 	int		i;
289 
290 	switch (what) {
291 	case MOD_LOAD:
292 		break;
293 	case MOD_UNLOAD:
294 		devclass_get_devices(devclass_find("smbios"), &devs, &count);
295 		for (i = 0; i < count; i++) {
296 			device_delete_child(device_get_parent(devs[i]), devs[i]);
297 		}
298 		free(devs, M_TEMP);
299 		break;
300 	default:
301 		break;
302 	}
303 
304 	return (0);
305 }
306 
307 static device_method_t smbios_methods[] = {
308 	/* Device interface */
309 	DEVMETHOD(device_identify,      smbios_identify),
310 	DEVMETHOD(device_probe,         smbios_probe),
311 	DEVMETHOD(device_attach,        smbios_attach),
312 	DEVMETHOD(device_detach,        smbios_detach),
313 	{ 0, 0 }
314 };
315 
316 static driver_t smbios_driver = {
317 	"smbios",
318 	smbios_methods,
319 	sizeof(struct smbios_softc),
320 };
321 
322 DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_modevent, NULL);
323 #ifdef ARCH_MAY_USE_EFI
324 MODULE_DEPEND(smbios, efirt, 1, 1, 1);
325 #endif
326 MODULE_VERSION(smbios, 1);
327 
328 
329 static bool
smbios_eps3(void * v)330 smbios_eps3 (void *v)
331 {
332 	struct smbios3_eps *e;
333 
334 	e = (struct smbios3_eps *)v;
335 	return (memcmp(e->anchor_string, SMBIOS3_SIG, SMBIOS3_LEN) == 0);
336 }
337 
338 static int
smbios_cksum(void * v)339 smbios_cksum (void *v)
340 {
341 	const u_int8_t *ptr;
342 	u_int8_t cksum;
343 	u_int8_t length;
344 	int i;
345 
346 	if (smbios_eps3(v)) {
347 		const struct smbios3_eps *eps3 = v;
348 
349 		length = eps3->length;
350 	} else {
351 		const struct smbios_eps *eps = v;
352 
353 		length = eps->length;
354 	}
355 	ptr = v;
356 	cksum = 0;
357 	for (i = 0; i < length; i++)
358 		cksum += ptr[i];
359 
360 	return (cksum);
361 }
362