xref: /freebsd/sys/dev/smbios/smbios.c (revision a05a680469a7ac77b195021fed74e3aa58152dd7)
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__)
105a29bff7aSGreg V 	if (addr == 0)
106d0673fe1SAllan Jude 		addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN,
107d0673fe1SAllan Jude 		    SMBIOS_STEP, SMBIOS_OFF);
108d0673fe1SAllan Jude #endif
109d0673fe1SAllan Jude 
110d0673fe1SAllan Jude 	if (addr != 0) {
111ba0e4d79SAndrew Gallatin 		ptr = pmap_mapbios(addr, map_size);
112ba0e4d79SAndrew Gallatin 		if (ptr == NULL)
113ba0e4d79SAndrew Gallatin 			return;
114ba0e4d79SAndrew Gallatin 		if (map_size == sizeof (*eps3)) {
115ba0e4d79SAndrew Gallatin 			eps3 = ptr;
116ba0e4d79SAndrew Gallatin 			length = eps3->length;
117ba0e4d79SAndrew Gallatin 			if (memcmp(eps3->anchor_string,
118ba0e4d79SAndrew Gallatin 			    SMBIOS3_SIG, SMBIOS3_LEN) != 0) {
119ba0e4d79SAndrew Gallatin 				printf("smbios3: corrupt sig %s found\n",
120ba0e4d79SAndrew Gallatin 				    eps3->anchor_string);
121ba0e4d79SAndrew Gallatin 				return;
122ba0e4d79SAndrew Gallatin 			}
123ba0e4d79SAndrew Gallatin 		} else {
124ba0e4d79SAndrew Gallatin 			eps = ptr;
125d0673fe1SAllan Jude 			length = eps->length;
126ba0e4d79SAndrew Gallatin 			if (memcmp(eps->anchor_string,
127ba0e4d79SAndrew Gallatin 			    SMBIOS_SIG, SMBIOS_LEN) != 0) {
128ba0e4d79SAndrew Gallatin 				printf("smbios: corrupt sig %s found\n",
129ba0e4d79SAndrew Gallatin 				    eps->anchor_string);
130ba0e4d79SAndrew Gallatin 				return;
131ba0e4d79SAndrew Gallatin 			}
132ba0e4d79SAndrew Gallatin 		}
133ba0e4d79SAndrew Gallatin 		if (length != map_size) {
134d0673fe1SAllan Jude 			u_int8_t major, minor;
135d0673fe1SAllan Jude 
136d0673fe1SAllan Jude 			major = eps->major_version;
137d0673fe1SAllan Jude 			minor = eps->minor_version;
138d0673fe1SAllan Jude 
139d0673fe1SAllan Jude 			/* SMBIOS v2.1 implementation might use 0x1e. */
140ba0e4d79SAndrew Gallatin 			if (length == 0x1e && major == 2 && minor == 1) {
141d0673fe1SAllan Jude 				length = 0x1f;
142ba0e4d79SAndrew Gallatin 			} else {
143ba0e4d79SAndrew Gallatin 				pmap_unmapbios(eps, map_size);
144d0673fe1SAllan Jude 				return;
145d0673fe1SAllan Jude 			}
146ba0e4d79SAndrew Gallatin 		}
147d0673fe1SAllan Jude 
148*a05a6804SWarner Losh 		child = BUS_ADD_CHILD(parent, 5, "smbios", DEVICE_UNIT_ANY);
149d0673fe1SAllan Jude 		device_set_driver(child, driver);
150ba0e4d79SAndrew Gallatin 
151ba0e4d79SAndrew Gallatin 		/* smuggle the phys addr into probe and attach */
152ba0e4d79SAndrew Gallatin 		bus_set_resource(child, SYS_RES_MEMORY, 0, addr, length);
153d0673fe1SAllan Jude 		device_set_desc(child, "System Management BIOS");
154ba0e4d79SAndrew Gallatin 		pmap_unmapbios(ptr, map_size);
155d0673fe1SAllan Jude 	}
156d0673fe1SAllan Jude 
157d0673fe1SAllan Jude 	return;
158d0673fe1SAllan Jude }
159d0673fe1SAllan Jude 
160d0673fe1SAllan Jude static int
161d0673fe1SAllan Jude smbios_probe (device_t dev)
162d0673fe1SAllan Jude {
163ba0e4d79SAndrew Gallatin 	vm_paddr_t pa;
164ba0e4d79SAndrew Gallatin 	vm_size_t size;
165ba0e4d79SAndrew Gallatin 	void *va;
166d0673fe1SAllan Jude 	int error;
167d0673fe1SAllan Jude 
168d0673fe1SAllan Jude 	error = 0;
169ba0e4d79SAndrew Gallatin 
170ba0e4d79SAndrew Gallatin 	pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
171ba0e4d79SAndrew Gallatin 	size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0);
172ba0e4d79SAndrew Gallatin 	va = pmap_mapbios(pa, size);
173ba0e4d79SAndrew Gallatin 	if (va == NULL) {
174ba0e4d79SAndrew Gallatin 		device_printf(dev, "Unable to map memory.\n");
175ba0e4d79SAndrew Gallatin 		return (ENOMEM);
176d0673fe1SAllan Jude 	}
177d0673fe1SAllan Jude 
178ba0e4d79SAndrew Gallatin 	if (smbios_cksum(va)) {
179d0673fe1SAllan Jude 		device_printf(dev, "SMBIOS checksum failed.\n");
180d0673fe1SAllan Jude 		error = ENXIO;
181d0673fe1SAllan Jude 	}
182d0673fe1SAllan Jude 
183ba0e4d79SAndrew Gallatin 	pmap_unmapbios(va, size);
184d0673fe1SAllan Jude 	return (error);
185d0673fe1SAllan Jude }
186d0673fe1SAllan Jude 
187d0673fe1SAllan Jude static int
188d0673fe1SAllan Jude smbios_attach (device_t dev)
189d0673fe1SAllan Jude {
190d0673fe1SAllan Jude 	struct smbios_softc *sc;
191ba0e4d79SAndrew Gallatin 	void *va;
192ba0e4d79SAndrew Gallatin 	vm_paddr_t pa;
193ba0e4d79SAndrew Gallatin 	vm_size_t size;
194d0673fe1SAllan Jude 
195d0673fe1SAllan Jude 	sc = device_get_softc(dev);
196d0673fe1SAllan Jude 	sc->dev = dev;
197ba0e4d79SAndrew Gallatin 	pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
198ba0e4d79SAndrew Gallatin 	size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0);
199ba0e4d79SAndrew Gallatin 	va = pmap_mapbios(pa, size);
200ba0e4d79SAndrew Gallatin 	if (va == NULL) {
201ba0e4d79SAndrew Gallatin 		device_printf(dev, "Unable to map memory.\n");
202ba0e4d79SAndrew Gallatin 		return (ENOMEM);
203d0673fe1SAllan Jude 	}
204ba0e4d79SAndrew Gallatin 	sc->is_eps3 = smbios_eps3(va);
205d0673fe1SAllan Jude 
206ba0e4d79SAndrew Gallatin 	if (sc->is_eps3) {
207ba0e4d79SAndrew Gallatin 		sc->eps3 = va;
208ba0e4d79SAndrew Gallatin 		device_printf(dev, "Version: %u.%u",
209ba0e4d79SAndrew Gallatin 		    sc->eps3->major_version, sc->eps3->minor_version);
210ba0e4d79SAndrew Gallatin 	} else {
211ba0e4d79SAndrew Gallatin 		sc->eps = va;
212d0673fe1SAllan Jude 		device_printf(dev, "Version: %u.%u",
213d0673fe1SAllan Jude 		    sc->eps->major_version, sc->eps->minor_version);
214d0673fe1SAllan Jude 		if (bcd2bin(sc->eps->BCD_revision))
215d0673fe1SAllan Jude 			printf(", BCD Revision: %u.%u",
216d0673fe1SAllan Jude 			    bcd2bin(sc->eps->BCD_revision >> 4),
217d0673fe1SAllan Jude 			    bcd2bin(sc->eps->BCD_revision & 0x0f));
218ba0e4d79SAndrew Gallatin 	}
219d0673fe1SAllan Jude 	printf("\n");
220d0673fe1SAllan Jude 	return (0);
221d0673fe1SAllan Jude }
222d0673fe1SAllan Jude 
223d0673fe1SAllan Jude static int
224d0673fe1SAllan Jude smbios_detach (device_t dev)
225d0673fe1SAllan Jude {
226d0673fe1SAllan Jude 	struct smbios_softc *sc;
227ba0e4d79SAndrew Gallatin 	vm_size_t size;
228ba0e4d79SAndrew Gallatin 	void *va;
229d0673fe1SAllan Jude 
230d0673fe1SAllan Jude 	sc = device_get_softc(dev);
231ba0e4d79SAndrew Gallatin 	va = (sc->is_eps3 ? (void *)sc->eps3 : (void *)sc->eps);
232ba0e4d79SAndrew Gallatin 	if (sc->is_eps3)
233ba0e4d79SAndrew Gallatin 		va = sc->eps3;
234ba0e4d79SAndrew Gallatin 	else
235ba0e4d79SAndrew Gallatin 		va = sc->eps;
236ba0e4d79SAndrew Gallatin 	size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0);
237d0673fe1SAllan Jude 
238ba0e4d79SAndrew Gallatin 	if (va != NULL)
239ba0e4d79SAndrew Gallatin 		pmap_unmapbios(va, size);
240d0673fe1SAllan Jude 
241d0673fe1SAllan Jude 	return (0);
242d0673fe1SAllan Jude }
243d0673fe1SAllan Jude 
244d0673fe1SAllan Jude static int
245489e8f24SJohn Baldwin smbios_modevent (module_t mod, int what, void *arg)
246d0673fe1SAllan Jude {
247d0673fe1SAllan Jude 	device_t *	devs;
248d0673fe1SAllan Jude 	int		count;
249d0673fe1SAllan Jude 	int		i;
250d0673fe1SAllan Jude 
251d0673fe1SAllan Jude 	switch (what) {
252d0673fe1SAllan Jude 	case MOD_LOAD:
253d0673fe1SAllan Jude 		break;
254d0673fe1SAllan Jude 	case MOD_UNLOAD:
255489e8f24SJohn Baldwin 		devclass_get_devices(devclass_find("smbios"), &devs, &count);
256d0673fe1SAllan Jude 		for (i = 0; i < count; i++) {
257d0673fe1SAllan Jude 			device_delete_child(device_get_parent(devs[i]), devs[i]);
258d0673fe1SAllan Jude 		}
259d0673fe1SAllan Jude 		free(devs, M_TEMP);
260d0673fe1SAllan Jude 		break;
261d0673fe1SAllan Jude 	default:
262d0673fe1SAllan Jude 		break;
263d0673fe1SAllan Jude 	}
264d0673fe1SAllan Jude 
265d0673fe1SAllan Jude 	return (0);
266d0673fe1SAllan Jude }
267d0673fe1SAllan Jude 
268d0673fe1SAllan Jude static device_method_t smbios_methods[] = {
269d0673fe1SAllan Jude 	/* Device interface */
270d0673fe1SAllan Jude 	DEVMETHOD(device_identify,      smbios_identify),
271d0673fe1SAllan Jude 	DEVMETHOD(device_probe,         smbios_probe),
272d0673fe1SAllan Jude 	DEVMETHOD(device_attach,        smbios_attach),
273d0673fe1SAllan Jude 	DEVMETHOD(device_detach,        smbios_detach),
274d0673fe1SAllan Jude 	{ 0, 0 }
275d0673fe1SAllan Jude };
276d0673fe1SAllan Jude 
277d0673fe1SAllan Jude static driver_t smbios_driver = {
278d0673fe1SAllan Jude 	"smbios",
279d0673fe1SAllan Jude 	smbios_methods,
280d0673fe1SAllan Jude 	sizeof(struct smbios_softc),
281d0673fe1SAllan Jude };
282d0673fe1SAllan Jude 
283b8b05cc2SJohn Baldwin DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_modevent, NULL);
284a29bff7aSGreg V #ifdef ARCH_MAY_USE_EFI
285a29bff7aSGreg V MODULE_DEPEND(smbios, efirt, 1, 1, 1);
286a29bff7aSGreg V #endif
287d0673fe1SAllan Jude MODULE_VERSION(smbios, 1);
288d0673fe1SAllan Jude 
289ba0e4d79SAndrew Gallatin 
290ba0e4d79SAndrew Gallatin static bool
291ba0e4d79SAndrew Gallatin smbios_eps3 (void *v)
292d0673fe1SAllan Jude {
293ba0e4d79SAndrew Gallatin 	struct smbios3_eps *e;
294ba0e4d79SAndrew Gallatin 
295ba0e4d79SAndrew Gallatin 	e = (struct smbios3_eps *)v;
296ba0e4d79SAndrew Gallatin 	return (memcmp(e->anchor_string, SMBIOS3_SIG, SMBIOS3_LEN) == 0);
297ba0e4d79SAndrew Gallatin }
298ba0e4d79SAndrew Gallatin 
299ba0e4d79SAndrew Gallatin static int
300ba0e4d79SAndrew Gallatin smbios_cksum (void *v)
301ba0e4d79SAndrew Gallatin {
302ba0e4d79SAndrew Gallatin 	struct smbios3_eps *eps3;
303ba0e4d79SAndrew Gallatin 	struct smbios_eps *eps;
304d0673fe1SAllan Jude 	u_int8_t *ptr;
305d0673fe1SAllan Jude 	u_int8_t cksum;
306ba0e4d79SAndrew Gallatin 	u_int8_t length;
307d0673fe1SAllan Jude 	int i;
308d0673fe1SAllan Jude 
309ba0e4d79SAndrew Gallatin 	if (smbios_eps3(v)) {
310ba0e4d79SAndrew Gallatin 		eps3 = (struct smbios3_eps *)v;
311ba0e4d79SAndrew Gallatin 		length = eps3->length;
312ba0e4d79SAndrew Gallatin 	} else {
313ba0e4d79SAndrew Gallatin 		eps = (struct smbios_eps *)v;
314ba0e4d79SAndrew Gallatin 		length = eps->length;
315ba0e4d79SAndrew Gallatin 	}
316ba0e4d79SAndrew Gallatin 	ptr = (u_int8_t *)v;
317d0673fe1SAllan Jude 	cksum = 0;
318ba0e4d79SAndrew Gallatin 	for (i = 0; i < length; i++) {
319d0673fe1SAllan Jude 		cksum += ptr[i];
320d0673fe1SAllan Jude 	}
321d0673fe1SAllan Jude 
322d0673fe1SAllan Jude 	return (cksum);
323d0673fe1SAllan Jude }
324