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