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