1 /* $OpenBSD: bios.c,v 1.47 2023/03/15 08:20:52 jsg Exp $ */
2 /*
3 * Copyright (c) 2006 Gordon Willem Klok <gklok@cogeco.ca>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22
23 #include <uvm/uvm_extern.h>
24
25 #include <machine/biosvar.h>
26 #include <machine/mpbiosvar.h>
27 #include <machine/smbiosvar.h>
28
29 #include <dev/isa/isareg.h>
30 #include <amd64/include/isa_machdep.h>
31
32 #include "acpi.h"
33 #include "efi.h"
34 #include "mpbios.h"
35 #include "pci.h"
36
37 struct bios_softc {
38 struct device sc_dev;
39 };
40
41 struct smbhdr *smbios_find(uint8_t *);
42 void smbios_info(char *);
43 int bios_match(struct device *, void *, void *);
44 void bios_attach(struct device *, struct device *, void *);
45 int bios_print(void *, const char *);
46 char *fixstring(char *);
47
48 const struct cfattach bios_ca = {
49 sizeof(struct bios_softc), bios_match, bios_attach
50 };
51
52 struct cfdriver bios_cd = {
53 NULL, "bios", DV_DULL
54 };
55
56 struct smbios_entry smbios_entry;
57
58 const char *smbios_uninfo[] = {
59 "System",
60 "Not ",
61 "To be",
62 "SYS-"
63 };
64
65 char smbios_bios_date[64];
66 char smbios_bios_version[64];
67 char smbios_board_vendor[64];
68 char smbios_board_prod[64];
69 char smbios_board_serial[64];
70
71 int
bios_match(struct device * parent,void * match,void * aux)72 bios_match(struct device *parent, void *match , void *aux)
73 {
74 struct bios_attach_args *bia = aux;
75
76 /* only one */
77 if (bios_cd.cd_ndevs || strcmp(bia->ba_name, bios_cd.cd_name))
78 return 0;
79 return 1;
80 }
81
82 void
bios_attach(struct device * parent,struct device * self,void * aux)83 bios_attach(struct device *parent, struct device *self, void *aux)
84 {
85 struct bios_softc *sc = (struct bios_softc *)self;
86 struct smbios_struct_bios *sb;
87 struct smbtable bios;
88 char scratch[64];
89 vaddr_t va;
90 paddr_t pa, end;
91 uint8_t *p;
92 int smbiosrev = 0;
93 struct smbhdr *hdr = NULL;
94 char *sminfop;
95
96 if (bios_efiinfo != NULL && bios_efiinfo->config_smbios != 0)
97 hdr = smbios_find(PMAP_DIRECT_MAP(
98 (uint8_t *)bios_efiinfo->config_smbios));
99
100 if (hdr == NULL) {
101 /* see if we have SMBIOS extensions */
102 for (p = ISA_HOLE_VADDR(SMBIOS_START);
103 p < (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END); p+= 16) {
104 hdr = smbios_find(p);
105 if (hdr != NULL)
106 break;
107 }
108 }
109
110 if (hdr != NULL) {
111 pa = trunc_page(hdr->addr);
112 end = round_page(hdr->addr + hdr->size);
113 va = (vaddr_t)km_alloc(end - pa, &kv_any, &kp_none, &kd_nowait);
114 if (va == 0)
115 goto out;
116
117 smbios_entry.addr = (uint8_t *)(va + (hdr->addr & PGOFSET));
118 smbios_entry.len = hdr->size;
119 smbios_entry.mjr = hdr->majrev;
120 smbios_entry.min = hdr->minrev;
121 smbios_entry.count = hdr->count;
122
123 for (; pa < end; pa+= NBPG, va+= NBPG)
124 pmap_kenter_pa(va, pa, PROT_READ);
125
126 printf(": SMBIOS rev. %d.%d @ 0x%x (%d entries)",
127 hdr->majrev, hdr->minrev, hdr->addr, hdr->count);
128
129 smbiosrev = hdr->majrev * 100 + hdr->minrev;
130 if (hdr->minrev < 10)
131 smbiosrev = hdr->majrev * 100 + hdr->minrev * 10;
132
133 bios.cookie = 0;
134 if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) {
135 sb = bios.tblhdr;
136 printf("\n%s:", sc->sc_dev.dv_xname);
137 if ((smbios_get_string(&bios, sb->vendor,
138 scratch, sizeof(scratch))) != NULL)
139 printf(" vendor %s",
140 fixstring(scratch));
141 if ((smbios_get_string(&bios, sb->version,
142 scratch, sizeof(scratch))) != NULL) {
143 sminfop = fixstring(scratch);
144 if (sminfop != NULL) {
145 strlcpy(smbios_bios_version,
146 sminfop,
147 sizeof(smbios_bios_version));
148 printf(" version \"%s\"", sminfop);
149 }
150 }
151 if ((smbios_get_string(&bios, sb->release,
152 scratch, sizeof(scratch))) != NULL) {
153 sminfop = fixstring(scratch);
154 if (sminfop != NULL) {
155 strlcpy(smbios_bios_date,
156 sminfop,
157 sizeof(smbios_bios_date));
158 printf(" date %s", sminfop);
159 }
160 }
161 }
162
163 smbios_info(sc->sc_dev.dv_xname);
164 }
165 out:
166 printf("\n");
167
168 /* No SMBIOS extensions, go looking for Soekris comBIOS */
169 if (smbiosrev == 0) {
170 const char *signature = "Soekris Engineering";
171
172 for (p = ISA_HOLE_VADDR(SMBIOS_START);
173 p <= (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END -
174 (strlen(signature) - 1)); p++)
175 if (!memcmp(p, signature, strlen(signature))) {
176 hw_vendor = malloc(strlen(signature) + 1,
177 M_DEVBUF, M_NOWAIT);
178 if (hw_vendor)
179 strlcpy(hw_vendor, signature,
180 strlen(signature) + 1);
181 p += strlen(signature);
182 break;
183 }
184
185 for (; hw_vendor &&
186 p <= (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END - 6); p++)
187 /*
188 * Search only for "net6501" in the comBIOS as that's
189 * the only Soekris platform that can run amd64
190 */
191 if (!memcmp(p, "net6501", 7)) {
192 hw_prod = malloc(8, M_DEVBUF, M_NOWAIT);
193 if (hw_prod) {
194 memcpy(hw_prod, p, 7);
195 hw_prod[7] = '\0';
196 }
197 break;
198 }
199 }
200
201 #if NEFI > 0
202 if (bios_efiinfo != NULL) {
203 struct bios_attach_args ba;
204
205 memset(&ba, 0, sizeof(ba));
206 ba.ba_name = "efi";
207 ba.ba_memt = X86_BUS_SPACE_MEM;
208
209 config_found(self, &ba, bios_print);
210 }
211 #endif
212
213 #if NACPI > 0
214 {
215 struct bios_attach_args ba;
216
217 memset(&ba, 0, sizeof(ba));
218 ba.ba_name = "acpi";
219 ba.ba_iot = X86_BUS_SPACE_IO;
220 ba.ba_memt = X86_BUS_SPACE_MEM;
221
222 if (bios_efiinfo != NULL)
223 ba.ba_acpipbase = bios_efiinfo->config_acpi;
224
225 config_found(self, &ba, bios_print);
226 }
227 #endif
228
229 #if NMPBIOS > 0
230 if (mpbios_probe(self)) {
231 struct bios_attach_args ba;
232
233 memset(&ba, 0, sizeof(ba));
234 ba.ba_name = "mpbios";
235 ba.ba_iot = X86_BUS_SPACE_IO;
236 ba.ba_memt = X86_BUS_SPACE_MEM;
237
238 config_found(self, &ba, bios_print);
239 }
240 #endif
241 }
242
243 struct smbhdr *
smbios_find(uint8_t * p)244 smbios_find(uint8_t *p)
245 {
246 struct smbhdr *hdr = (struct smbhdr *)p;
247 uint8_t chksum;
248 int i;
249
250 if (hdr->sig != SMBIOS_SIGNATURE)
251 return (NULL);
252 i = hdr->len;
253 for (chksum = 0; i--; chksum += p[i])
254 ;
255 if (chksum != 0)
256 return (NULL);
257 p += 0x10;
258 if (!(p[0] == '_' && p[1] == 'D' && p[2] == 'M' && p[3] == 'I' &&
259 p[4] == '_'))
260 return (NULL);
261 for (chksum = 0, i = 0xf; i--; chksum += p[i])
262 ;
263 if (chksum != 0)
264 return (NULL);
265
266 return (hdr);
267 }
268
269 /*
270 * smbios_find_table() takes a caller supplied smbios struct type and
271 * a pointer to a handle (struct smbtable) returning one if the structure
272 * is successfully located and zero otherwise. Callers should take care
273 * to initialize the cookie field of the smbtable structure to zero before
274 * the first invocation of this function.
275 * Multiple tables of the same type can be located by repeatedly calling
276 * smbios_find_table with the same arguments.
277 */
278 int
smbios_find_table(uint8_t type,struct smbtable * st)279 smbios_find_table(uint8_t type, struct smbtable *st)
280 {
281 uint8_t *va, *end;
282 struct smbtblhdr *hdr;
283 int ret = 0, tcount = 1;
284
285 va = smbios_entry.addr;
286 end = va + smbios_entry.len;
287
288 /*
289 * The cookie field of the smtable structure is used to locate
290 * multiple instances of a table of an arbitrary type. Following the
291 * successful location of a table, the type is encoded as bits 0:7 of
292 * the cookie value, the offset in terms of the number of structures
293 * preceding that referenced by the handle is encoded in bits 15:31.
294 */
295 if ((st->cookie & 0xfff) == type && st->cookie >> 16) {
296 if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) {
297 hdr = st->hdr;
298 if (hdr->type == type) {
299 va = (uint8_t *)hdr + hdr->size;
300 for (; va + 1 < end; va++)
301 if (*va == 0 && *(va + 1) == 0)
302 break;
303 va += 2;
304 tcount = st->cookie >> 16;
305 }
306 }
307 }
308 for (; va + sizeof(struct smbtblhdr) < end &&
309 tcount <= smbios_entry.count; tcount++) {
310 hdr = (struct smbtblhdr *)va;
311 if (hdr->type == type) {
312 ret = 1;
313 st->hdr = hdr;
314 st->tblhdr = va + sizeof(struct smbtblhdr);
315 st->cookie = (tcount + 1) << 16 | type;
316 break;
317 }
318 if (hdr->type == SMBIOS_TYPE_EOT)
319 break;
320 va += hdr->size;
321 for (; va + 1 < end; va++)
322 if (*va == 0 && *(va + 1) == 0)
323 break;
324 va += 2;
325 }
326 return ret;
327 }
328
329 char *
smbios_get_string(struct smbtable * st,uint8_t indx,char * dest,size_t len)330 smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len)
331 {
332 uint8_t *va, *end;
333 char *ret = NULL;
334 int i;
335
336 va = (uint8_t *)st->hdr + st->hdr->size;
337 end = smbios_entry.addr + smbios_entry.len;
338 for (i = 1; va < end && i < indx && *va; i++)
339 while (*va++)
340 ;
341 if (i == indx) {
342 if (va + len < end) {
343 ret = dest;
344 memcpy(ret, va, len);
345 ret[len - 1] = '\0';
346 }
347 }
348
349 return ret;
350 }
351
352 char *
fixstring(char * s)353 fixstring(char *s)
354 {
355 char *p, *e;
356 int i;
357
358 for (i = 0; i < nitems(smbios_uninfo); i++)
359 if ((strncasecmp(s, smbios_uninfo[i],
360 strlen(smbios_uninfo[i]))) == 0)
361 return NULL;
362 /*
363 * Remove leading and trailing whitespace
364 */
365 for (p = s; *p == ' '; p++)
366 ;
367 /*
368 * Special case entire string is whitespace
369 */
370 if (p == s + strlen(s))
371 return NULL;
372 for (e = s + strlen(s) - 1; e > s && *e == ' '; e--)
373 ;
374 if (p > s || e < s + strlen(s) - 1) {
375 memmove(s, p, e - p + 1);
376 s[e - p + 1] = '\0';
377 }
378
379 return s;
380 }
381
382 void
smbios_info(char * str)383 smbios_info(char *str)
384 {
385 char *sminfop, sminfo[64];
386 struct smbtable stbl, btbl;
387 struct smbios_sys *sys;
388 struct smbios_board *board;
389 int i, infolen, uuidf, havebb;
390 char *p;
391
392 if (smbios_entry.mjr < 2)
393 return;
394 /*
395 * According to the spec the system table among others is required,
396 * if it is not we do not bother with this smbios implementation.
397 */
398 stbl.cookie = btbl.cookie = 0;
399 if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl))
400 return;
401 havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl);
402
403 sys = (struct smbios_sys *)stbl.tblhdr;
404 if (havebb) {
405 board = (struct smbios_board *)btbl.tblhdr;
406
407 sminfop = NULL;
408 if ((p = smbios_get_string(&btbl, board->vendor,
409 sminfo, sizeof(sminfo))) != NULL)
410 sminfop = fixstring(p);
411 if (sminfop)
412 strlcpy(smbios_board_vendor, sminfop,
413 sizeof(smbios_board_vendor));
414
415 sminfop = NULL;
416 if ((p = smbios_get_string(&btbl, board->product,
417 sminfo, sizeof(sminfo))) != NULL)
418 sminfop = fixstring(p);
419 if (sminfop)
420 strlcpy(smbios_board_prod, sminfop,
421 sizeof(smbios_board_prod));
422
423 sminfop = NULL;
424 if ((p = smbios_get_string(&btbl, board->serial,
425 sminfo, sizeof(sminfo))) != NULL)
426 sminfop = fixstring(p);
427 if (sminfop)
428 strlcpy(smbios_board_serial, sminfop,
429 sizeof(smbios_board_serial));
430 }
431 /*
432 * Some smbios implementations have no system vendor or
433 * product strings, some have very uninformative data which is
434 * harder to work around and we must rely upon various
435 * heuristics to detect this. In both cases we attempt to fall
436 * back on the base board information in the perhaps naive
437 * belief that motherboard vendors will supply this
438 * information.
439 */
440 sminfop = NULL;
441 if ((p = smbios_get_string(&stbl, sys->vendor, sminfo,
442 sizeof(sminfo))) != NULL)
443 sminfop = fixstring(p);
444 if (sminfop == NULL) {
445 if (havebb) {
446 if ((p = smbios_get_string(&btbl, board->vendor,
447 sminfo, sizeof(sminfo))) != NULL)
448 sminfop = fixstring(p);
449 }
450 }
451 if (sminfop) {
452 infolen = strlen(sminfop) + 1;
453 hw_vendor = malloc(infolen, M_DEVBUF, M_NOWAIT);
454 if (hw_vendor)
455 strlcpy(hw_vendor, sminfop, infolen);
456 sminfop = NULL;
457 }
458 if ((p = smbios_get_string(&stbl, sys->product, sminfo,
459 sizeof(sminfo))) != NULL)
460 sminfop = fixstring(p);
461 if (sminfop == NULL) {
462 if (havebb) {
463 if ((p = smbios_get_string(&btbl, board->product,
464 sminfo, sizeof(sminfo))) != NULL)
465 sminfop = fixstring(p);
466 }
467 }
468 if (sminfop) {
469 infolen = strlen(sminfop) + 1;
470 hw_prod = malloc(infolen, M_DEVBUF, M_NOWAIT);
471 if (hw_prod)
472 strlcpy(hw_prod, sminfop, infolen);
473 sminfop = NULL;
474 }
475 if (hw_vendor != NULL && hw_prod != NULL)
476 printf("\n%s: %s %s", str, hw_vendor, hw_prod);
477 if ((p = smbios_get_string(&stbl, sys->version, sminfo,
478 sizeof(sminfo))) != NULL)
479 sminfop = fixstring(p);
480 if (sminfop) {
481 infolen = strlen(sminfop) + 1;
482 hw_ver = malloc(infolen, M_DEVBUF, M_NOWAIT);
483 if (hw_ver)
484 strlcpy(hw_ver, sminfop, infolen);
485 sminfop = NULL;
486 }
487 if ((p = smbios_get_string(&stbl, sys->serial, sminfo,
488 sizeof(sminfo))) != NULL)
489 sminfop = fixstring(p);
490 if (sminfop) {
491 infolen = strlen(sminfop) + 1;
492 for (i = 0; i < infolen - 1; i++)
493 enqueue_randomness(sminfop[i]);
494 hw_serial = malloc(infolen, M_DEVBUF, M_NOWAIT);
495 if (hw_serial)
496 strlcpy(hw_serial, sminfop, infolen);
497 }
498 if (smbios_entry.mjr > 2 || (smbios_entry.mjr == 2 &&
499 smbios_entry.min >= 1)) {
500 /*
501 * If the uuid value is all 0xff the uuid is present but not
502 * set, if its all 0 then the uuid isn't present at all.
503 */
504 uuidf = SMBIOS_UUID_NPRESENT|SMBIOS_UUID_NSET;
505 for (i = 0; i < sizeof(sys->uuid); i++) {
506 if (sys->uuid[i] != 0xff)
507 uuidf &= ~SMBIOS_UUID_NSET;
508 if (sys->uuid[i] != 0)
509 uuidf &= ~SMBIOS_UUID_NPRESENT;
510 }
511
512 if (uuidf & SMBIOS_UUID_NPRESENT)
513 hw_uuid = NULL;
514 else if (uuidf & SMBIOS_UUID_NSET)
515 hw_uuid = "Not Set";
516 else {
517 for (i = 0; i < sizeof(sys->uuid); i++)
518 enqueue_randomness(sys->uuid[i]);
519 hw_uuid = malloc(SMBIOS_UUID_REPLEN, M_DEVBUF,
520 M_NOWAIT);
521 if (hw_uuid) {
522 snprintf(hw_uuid, SMBIOS_UUID_REPLEN,
523 SMBIOS_UUID_REP,
524 sys->uuid[0], sys->uuid[1], sys->uuid[2],
525 sys->uuid[3], sys->uuid[4], sys->uuid[5],
526 sys->uuid[6], sys->uuid[7], sys->uuid[8],
527 sys->uuid[9], sys->uuid[10], sys->uuid[11],
528 sys->uuid[12], sys->uuid[13], sys->uuid[14],
529 sys->uuid[15]);
530 }
531 }
532 }
533 }
534
535 int
bios_print(void * aux,const char * pnp)536 bios_print(void *aux, const char *pnp)
537 {
538 struct bios_attach_args *ba = aux;
539
540 if (pnp)
541 printf("%s at %s", ba->ba_name, pnp);
542 return (UNCONF);
543 }
544