xref: /openbsd/sys/arch/amd64/amd64/bios.c (revision 144bb7e0)
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