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