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