1/*	$OpenBSD: bios.c,v 1.129 2023/03/15 08:20:52 jsg Exp $	*/
2
3/*
4 * Copyright (c) 1997-2001 Michael Shalayeff
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 ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/* #define BIOS_DEBUG */
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/device.h>
34#include <sys/errno.h>
35#include <sys/malloc.h>
36#include <sys/reboot.h>
37#include <sys/extent.h>
38
39#include <uvm/uvm_extern.h>
40#include <sys/sysctl.h>
41
42#include <dev/cons.h>
43#include <stand/boot/bootarg.h>
44
45#include <machine/conf.h>
46#include <machine/gdt.h>
47#include <machine/biosvar.h>
48#include <machine/mpbiosvar.h>
49#include <machine/smbiosvar.h>
50
51#include <dev/isa/isareg.h>
52#include <i386/isa/isa_machdep.h>
53
54#include <dev/pci/pcivar.h>
55
56#include "apm.h"
57#include "acpi.h"
58#include "mpbios.h"
59#include "pcibios.h"
60#include "pci.h"
61
62#include "com.h"
63#if NCOM > 0
64#include <sys/termios.h>
65#include <dev/ic/comvar.h>
66#endif
67
68#include "softraid.h"
69#if NSOFTRAID > 0
70#include <dev/softraidvar.h>
71#endif
72
73struct bios_softc {
74	struct	device sc_dev;
75	vaddr_t bios32_service_va;
76};
77
78int biosprobe(struct device *, void *, void *);
79void biosattach(struct device *, struct device *, void *);
80int bios_print(void *, const char *);
81char *fixstring(char *);
82
83const struct cfattach bios_ca = {
84	sizeof(struct bios_softc), biosprobe, biosattach, NULL,
85	config_activate_children
86};
87
88struct cfdriver bios_cd = {
89	NULL, "bios", DV_DULL
90};
91
92extern dev_t bootdev;
93
94#if NAPM > 0 || defined(DEBUG)
95bios_apminfo_t *apm;
96#endif
97#if NPCI > 0
98bios_pciinfo_t *bios_pciinfo;
99#endif
100bios_diskinfo_t *bios_diskinfo;
101bios_memmap_t  *bios_memmap;
102struct bios_softc *bios_softc;
103uint32_t	bios_cksumlen;
104struct bios32_entry bios32_entry;
105struct smbios_entry smbios_entry;
106#ifdef MULTIPROCESSOR
107void		*bios_smpinfo;
108#endif
109bios_bootmac_t	*bios_bootmac;
110#ifdef DDB
111extern int	db_console;
112#endif
113bios_ucode_t	*bios_ucode;
114
115void		smbios_info(char*);
116
117bios_diskinfo_t *bios_getdiskinfo(dev_t);
118
119const char *smbios_uninfo[] = {
120	"System",
121	"Not ",
122	"To be",
123	"SYS-"
124};
125
126
127char smbios_bios_date[64];
128char smbios_bios_version[64];
129char smbios_board_vendor[64];
130char smbios_board_prod[64];
131char smbios_board_serial[64];
132
133int
134biosprobe(struct device *parent, void *match, void *aux)
135{
136	struct bios_attach_args *bia = aux;
137
138#ifdef BIOS_DEBUG
139	printf("%s%d: boot API ver %x, %x; args %p[%d]\n",
140	    bia->ba_name, bios_cd.cd_ndevs,
141	    bootapiver, BOOTARG_APIVER, bootargp, bootargc);
142#endif
143	/* only one */
144	if (bios_cd.cd_ndevs || strcmp(bia->ba_name, bios_cd.cd_name))
145		return 0;
146
147	if (!(bootapiver & BAPIV_VECTOR) || bootargp == NULL)
148		return 0;
149
150	return 1;
151}
152
153void
154biosattach(struct device *parent, struct device *self, void *aux)
155{
156	struct bios_softc *sc = (struct bios_softc *)self;
157#if (NPCI > 0 && NPCIBIOS > 0) || NAPM > 0
158	struct bios_attach_args *bia = aux;
159#endif
160	struct smbios_struct_bios *sb;
161	struct smbtable bios;
162	volatile uint8_t *va;
163	char scratch[64], *str;
164	int flags, smbiosrev = 0, ncpu = 0, isa_hole_exec = 0;
165#if NACPI > 0 || NAPM > 0
166	int usingacpi = 0;
167#endif
168
169	bios_softc = sc;
170	/* remember flags */
171	flags = sc->sc_dev.dv_cfdata->cf_flags;
172
173	va = ISA_HOLE_VADDR(0xffff0);
174	printf(": date %c%c/%c%c/%c%c",
175	    va[5], va[6], va[8], va[9], va[11], va[12]);
176
177	/*
178	 * Determining whether BIOS32 extensions are available is
179	 * done by searching for the BIOS32 service directory.
180	 * This 16-byte structure can be found somewhere in the
181	 * range 0E0000h - 0FFFFFh and must be 16-byte aligned.
182	 *
183	 *  _______________________________________________________
184	 * | Offset | Bytes | Description                          |
185	 * |-------------------------------------------------------|
186	 * |    0   |   4   | ASCII signature string of "_32_".    |
187	 * |    4   |   4   | 32-bit entry point.                  |
188	 * |    8   |   1   | Revision Level. Typically 00h.       |
189	 * |    9   |   1   | Header length in 16-byte units. So   |
190	 * |        |       | would have the value of 01h.         |
191	 * |    A   |   1   | Checksum. The sum of all bytes in    |
192	 * |        |       | this header must be zero.            |
193	 * |    B   |   5   | Reserved. Set to zero.               |
194	 *  -------------------------------------------------------
195	 *
196	 * To find the service directory, we first search for the
197	 * signature. If we find a match, we must also verify the
198	 * checksum. This service directory may then be used to
199	 * determine whether a PCI BIOS is present.
200	 *
201	 * For more information see the PCI BIOS Specification,
202	 * Revision 2.1 (August 26, 1994).
203	 */
204
205	if (!(flags & BIOSF_BIOS32)) {
206		for (va = ISA_HOLE_VADDR(BIOS32_START);
207		    va < (uint8_t *)ISA_HOLE_VADDR(BIOS32_END); va += 16) {
208			bios32_header_t h = (bios32_header_t)va;
209			uint8_t cksum;
210			int i;
211
212			if (h->signature != BIOS32_SIGNATURE)
213				continue;
214
215			/* verify checksum */
216			for (cksum = 0, i = h->length * 16; i--; cksum += va[i])
217				;
218			if (cksum != 0)
219				continue;
220
221			if (h->entry <= BIOS32_START || h->entry >= BIOS32_END)
222				continue;
223
224			bios32_entry.segment = GSEL(GCODE_SEL, SEL_KPL);
225			bios32_entry.offset = (uint32_t)ISA_HOLE_VADDR(h->entry);
226			printf(", BIOS32 rev. %d @ 0x%x", h->rev, h->entry);
227			break;
228		}
229	}
230
231	/* see if we have SMBIOS extensions */
232	if (!(flags & BIOSF_SMBIOS)) {
233		for (va = ISA_HOLE_VADDR(SMBIOS_START);
234		    va < (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END); va+= 16) {
235			struct smbhdr *sh = (struct smbhdr *)va;
236			char *sminfop;
237			uint8_t chksum;
238			vaddr_t eva;
239			paddr_t pa, end;
240			int i;
241
242			if (sh->sig != SMBIOS_SIGNATURE)
243				continue;
244			i = sh->len;
245			for (chksum = 0; i--; chksum += va[i])
246				;
247			if (chksum != 0)
248				continue;
249			va += 0x10;
250			if (!(va[0] == '_' && va[1] == 'D' && va[2] == 'M' &&
251			    va[3] == 'I' && va[4] == '_'))
252				continue;
253			for (chksum = 0, i = 0xf; i--; chksum += va[i])
254				;
255			if (chksum != 0)
256				continue;
257
258			pa = trunc_page(sh->addr);
259			end = round_page(sh->addr + sh->size);
260			eva = (vaddr_t)km_alloc(end - pa, &kv_any,
261			    &kp_none, &kd_nowait);
262			if (eva == 0)
263				break;
264
265			smbios_entry.addr = (uint8_t *)(eva +
266			    (sh->addr & PGOFSET));
267			smbios_entry.len = sh->size;
268			smbios_entry.mjr = sh->majrev;
269			smbios_entry.min = sh->minrev;
270			smbios_entry.count = sh->count;
271
272			for (; pa < end; pa+= NBPG, eva+= NBPG)
273				pmap_kenter_pa(eva, pa, PROT_READ);
274
275			printf(", SMBIOS rev. %d.%d @ 0x%x (%hd entries)",
276			    sh->majrev, sh->minrev, sh->addr, sh->count);
277			/*
278			 * Unbelievably the SMBIOS version number
279			 * sequence is like 2.3 ... 2.33 ... 2.4 ... 2.5
280			 */
281			smbiosrev = sh->majrev * 100 + sh->minrev;
282			if (sh->minrev < 10)
283				smbiosrev = sh->majrev * 100 + sh->minrev * 10;
284
285			bios.cookie = 0;
286			if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) {
287				sb = bios.tblhdr;
288				printf("\n%s:", sc->sc_dev.dv_xname);
289
290				if ((smbios_get_string(&bios, sb->vendor,
291				    scratch, sizeof(scratch))) != NULL)
292					printf(" vendor %s",
293					    fixstring(scratch));
294				if ((smbios_get_string(&bios, sb->version,
295				    scratch, sizeof(scratch))) != NULL) {
296					sminfop = fixstring(scratch);
297					if (sminfop != NULL) {
298						strlcpy(smbios_bios_version,
299						    sminfop,
300						    sizeof(smbios_bios_version));
301						printf(" version \"%s\"",
302						    sminfop);
303					}
304				}
305				if ((smbios_get_string(&bios, sb->release,
306				    scratch, sizeof(scratch))) != NULL) {
307					sminfop = fixstring(scratch);
308					if (sminfop != NULL) {
309						strlcpy(smbios_bios_date,
310						    sminfop,
311						    sizeof(smbios_bios_date));
312						printf(" date %s", sminfop);
313					}
314				}
315			}
316			smbios_info(sc->sc_dev.dv_xname);
317
318			/* count cpus so that we can disable apm when cpu > 1 */
319			bzero(&bios, sizeof(bios));
320			while (smbios_find_table(SMBIOS_TYPE_PROCESSOR,&bios)) {
321				struct smbios_cpu *cpu = bios.tblhdr;
322
323				if (cpu->cpu_status & SMBIOS_CPUST_POPULATED) {
324					/* SMBIOS 2.5 added multicore support */
325					if (smbiosrev >= 250 &&
326					    cpu->cpu_core_enabled)
327						ncpu += cpu->cpu_core_enabled;
328					else {
329						ncpu++;
330						if (cpu->cpu_id_edx & CPUID_HTT)
331							ncpu++;
332					}
333				}
334			}
335			break;
336		}
337	}
338
339	printf("\n");
340
341	/* No SMBIOS extensions, go looking for Soekris comBIOS */
342	if (!(flags & BIOSF_SMBIOS) && smbiosrev == 0) {
343		const char *signature = "Soekris Engineering";
344
345		for (va = ISA_HOLE_VADDR(SMBIOS_START);
346		    va <= (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END -
347		    (strlen(signature) - 1)); va++)
348			if (!memcmp((uint8_t *)va, signature,
349			    strlen(signature))) {
350				hw_vendor = malloc(strlen(signature) + 1,
351				    M_DEVBUF, M_NOWAIT);
352				if (hw_vendor)
353					strlcpy(hw_vendor, signature,
354					    strlen(signature) + 1);
355				va += strlen(signature);
356				break;
357			}
358
359		for (; hw_vendor &&
360		    va <= (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END - 6); va++)
361			/*
362			 * Search for "net(4(5xx|801)|[56]501)" which matches
363			 * the strings found in the comBIOS images
364			 */
365			if (!memcmp((uint8_t *)va, "net45xx", 7) ||
366			    !memcmp((uint8_t *)va, "net4801", 7) ||
367			    !memcmp((uint8_t *)va, "net5501", 7) ||
368			    !memcmp((uint8_t *)va, "net6501", 7)) {
369				hw_prod = malloc(8, M_DEVBUF, M_NOWAIT);
370				if (hw_prod) {
371					memcpy(hw_prod, (uint8_t *)va, 7);
372					hw_prod[7] = '\0';
373				}
374				break;
375			}
376	}
377
378#if NACPI > 0
379#if NPCI > 0
380	if (smbiosrev >= 210 && pci_mode_detect() != 0)
381#endif
382	{
383		struct bios_attach_args ba;
384
385		memset(&ba, 0, sizeof(ba));
386		ba.ba_name = "acpi";
387		ba.ba_func = 0x00;		/* XXX ? */
388		ba.ba_iot = I386_BUS_SPACE_IO;
389		ba.ba_memt = I386_BUS_SPACE_MEM;
390		if (config_found(self, &ba, bios_print)) {
391			flags |= BIOSF_PCIBIOS;
392			usingacpi = 1;
393		}
394	}
395#endif
396
397#if NAPM > 0
398	if (usingacpi == 0 && apm && ncpu < 2 && smbiosrev < 240) {
399		struct bios_attach_args ba;
400
401#if defined(DEBUG) || defined(APMDEBUG)
402		printf("apminfo: %x, code %x[%x]/%x[%x], data %x[%x], ept %x\n",
403		    apm->apm_detail,
404		    apm->apm_code32_base, apm->apm_code_len,
405		    apm->apm_code16_base, apm->apm_code16_len,
406		    apm->apm_data_base, apm->apm_data_len, apm->apm_entry);
407#endif
408		ba.ba_name = "apm";
409		ba.ba_func = 0x15;
410		ba.ba_memt = bia->ba_memt;
411		ba.ba_iot = bia->ba_iot;
412		ba.ba_apmp = apm;
413		config_found(self, &ba, bios_print);
414		isa_hole_exec = 1;
415	}
416#endif
417
418
419#if NMPBIOS > 0
420	if (mpbios_probe(self)) {
421		struct bios_attach_args ba;
422
423		memset(&ba, 0, sizeof(ba));
424		ba.ba_name = "mpbios";
425		ba.ba_iot = I386_BUS_SPACE_IO;
426		ba.ba_memt = I386_BUS_SPACE_MEM;
427
428		config_found(self, &ba, bios_print);
429	}
430#endif
431
432#if NPCI > 0 && NPCIBIOS > 0
433	if (!(flags & BIOSF_PCIBIOS)) {
434		struct bios_attach_args ba;
435
436		ba.ba_name = "pcibios";
437		ba.ba_func = 0x1A;
438		ba.ba_memt = bia->ba_memt;
439		ba.ba_iot = bia->ba_iot;
440		config_found(self, &ba, bios_print);
441	}
442#endif
443
444	/*
445	 * now that we gave 'em a chance to attach,
446	 * scan and map all the proms we can find
447	 */
448	if (!(flags & BIOSF_PROMSCAN)) {
449		volatile uint8_t *eva;
450
451		for (str = NULL, va = ISA_HOLE_VADDR(0xc0000),
452		    eva = ISA_HOLE_VADDR(0xf0000);
453		    va < eva; va += 512) {
454			extern struct extent *iomem_ex;
455			bios_romheader_t romh = (bios_romheader_t)va;
456			uint32_t off, len;
457			uint8_t cksum;
458			int i;
459
460			if (romh->signature != 0xaa55)
461				continue;
462
463			/*
464			 * for this and the next check we probably want
465			 * to reserve the page in the extent anyway
466			 */
467			if (!romh->len || romh->len == 0xff)
468				continue;
469
470			len = romh->len * 512;
471			if (va + len > eva)
472				continue;
473
474			for (cksum = 0, i = len; i--; cksum += va[i])
475				;
476			off = 0xc0000 + (va - (uint8_t *)
477			    ISA_HOLE_VADDR(0xc0000));
478
479			if (!str)
480				printf("%s: ROM list:",
481				    str = sc->sc_dev.dv_xname);
482			printf(" 0x%05x/0x%x%s", off, len,
483			    cksum? "!" : "");
484
485			if ((i = extent_alloc_region(iomem_ex,
486			    (paddr_t)off, len, EX_NOWAIT)))
487				printf(":%d", i);
488
489			isa_hole_exec = 1;
490			va += len - 512;
491		}
492		if (str)
493			printf("\n");
494	}
495
496	/*
497	 * If we had no BIOS / proms that need exec permission in the ISA
498	 * hole, remove X permissions.
499	 */
500	if (!isa_hole_exec)
501		pmap_write_protect(pmap_kernel(), (vaddr_t)atdevbase,
502		    (vaddr_t)atdevbase + IOM_SIZE, PROT_READ | PROT_WRITE);
503}
504
505void
506bios_getopt(void)
507{
508	bootarg_t *q;
509	bios_ddb_t *bios_ddb;
510	bios_bootduid_t *bios_bootduid;
511	bios_bootsr_t *bios_bootsr;
512
513#ifdef BIOS_DEBUG
514	printf("bootargv:");
515#endif
516
517	for(q = bootargp; q->ba_type != BOOTARG_END; q = q->ba_next) {
518		q->ba_next = (bootarg_t *)((caddr_t)q + q->ba_size);
519		switch (q->ba_type) {
520		case BOOTARG_MEMMAP:
521			bios_memmap = (bios_memmap_t *)q->ba_arg;
522#ifdef BIOS_DEBUG
523			printf(" memmap %p", bios_memmap);
524#endif
525			break;
526		case BOOTARG_DISKINFO:
527			bios_diskinfo = (bios_diskinfo_t *)q->ba_arg;
528#ifdef BIOS_DEBUG
529			printf(" diskinfo %p", bios_diskinfo);
530#endif
531			break;
532#if NAPM > 0 || defined(DEBUG)
533		case BOOTARG_APMINFO:
534#ifdef BIOS_DEBUG
535			printf(" apminfo %p", q->ba_arg);
536#endif
537			apm = (bios_apminfo_t *)q->ba_arg;
538			break;
539#endif
540		case BOOTARG_CKSUMLEN:
541			bios_cksumlen = *(uint32_t *)q->ba_arg;
542#ifdef BIOS_DEBUG
543			printf(" cksumlen %d", bios_cksumlen);
544#endif
545			break;
546#if NPCI > 0
547		case BOOTARG_PCIINFO:
548			bios_pciinfo = (bios_pciinfo_t *)q->ba_arg;
549#ifdef BIOS_DEBUG
550			printf(" pciinfo %p", bios_pciinfo);
551#endif
552			break;
553#endif
554		case BOOTARG_CONSDEV:
555			if (q->ba_size >= sizeof(bios_consdev_t) +
556			    offsetof(bootarg_t, ba_arg)) {
557				bios_consdev_t *cdp =
558				    (bios_consdev_t*)q->ba_arg;
559#if NCOM > 0
560				static const int ports[] =
561				    { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
562				int unit = minor(cdp->consdev);
563				int consaddr = cdp->consaddr;
564				if (consaddr == -1 && unit >=0 &&
565				    unit < (sizeof(ports)/sizeof(ports[0])))
566					consaddr = ports[unit];
567				if (major(cdp->consdev) == 8 &&
568				    consaddr != -1) {
569					comconsunit = unit;
570					comconsaddr = consaddr;
571					comconsrate = cdp->conspeed;
572					comconsiot = I386_BUS_SPACE_IO;
573
574					/* Probe the serial port this time. */
575					cninit();
576				}
577#endif
578#ifdef BIOS_DEBUG
579				printf(" console 0x%x:%d",
580				    cdp->consdev, cdp->conspeed);
581#endif
582			}
583			break;
584#ifdef MULTIPROCESSOR
585		case BOOTARG_SMPINFO:
586			bios_smpinfo = q->ba_arg;
587			printf(" smpinfo %p", bios_smpinfo);
588			break;
589#endif
590
591		case BOOTARG_BOOTMAC:
592			bios_bootmac = (bios_bootmac_t *)q->ba_arg;
593			break;
594
595		case BOOTARG_DDB:
596			bios_ddb = (bios_ddb_t *)q->ba_arg;
597#ifdef DDB
598			db_console = bios_ddb->db_console;
599#endif
600			break;
601
602		case BOOTARG_BOOTDUID:
603			bios_bootduid = (bios_bootduid_t *)q->ba_arg;
604			bcopy(bios_bootduid, bootduid, sizeof(bootduid));
605			break;
606
607		case BOOTARG_BOOTSR:
608			bios_bootsr = (bios_bootsr_t *)q->ba_arg;
609#if NSOFTRAID > 0
610			bcopy(&bios_bootsr->uuid, &sr_bootuuid,
611			    sizeof(sr_bootuuid));
612			bcopy(&bios_bootsr->maskkey, &sr_bootkey,
613			    sizeof(sr_bootkey));
614#endif
615			explicit_bzero(bios_bootsr, sizeof(bios_bootsr_t));
616			break;
617
618		case BOOTARG_UCODE:
619			bios_ucode = (bios_ucode_t *)q->ba_arg;
620			break;
621
622		default:
623#ifdef BIOS_DEBUG
624			printf(" unsupported arg (%d) %p", q->ba_type,
625			    q->ba_arg);
626#endif
627			break;
628		}
629	}
630	printf("\n");
631
632}
633
634int
635bios_print(void *aux, const char *pnp)
636{
637	struct bios_attach_args *ba = aux;
638
639	if (pnp)
640		printf("%s at %s function 0x%x",
641		    ba->ba_name, pnp, ba->ba_func);
642	return (UNCONF);
643}
644
645int
646bios32_service(uint32_t service, bios32_entry_t e, bios32_entry_info_t ei)
647{
648	u_long pa, endpa;
649	vaddr_t va, sva;
650	uint32_t base, count, off, ent;
651
652	if (bios32_entry.offset == 0)
653		return 0;
654
655	base = 0;
656	__asm volatile("lcall *(%4)"
657	    : "+a" (service), "+b" (base), "=c" (count), "=d" (off)
658	    : "D" (&bios32_entry)
659	    : "%esi", "cc", "memory");
660
661	if (service & 0xff)
662		return 0;	/* not found */
663
664	ent = base + off;
665	if (ent <= BIOS32_START || ent >= BIOS32_END)
666		return 0;
667
668	endpa = round_page(BIOS32_END);
669
670	sva = va = (vaddr_t)km_alloc(endpa, &kv_any, &kp_none, &kd_nowait);
671	if (va == 0)
672		return (0);
673
674	/* Store bios32 service kva for cleanup later */
675	bios_softc->bios32_service_va = sva;
676
677	setgdt(GBIOS32_SEL, (caddr_t)va, BIOS32_END, SDT_MEMERA, SEL_KPL, 1, 0);
678
679	for (pa = trunc_page(BIOS32_START),
680	    va += trunc_page(BIOS32_START);
681	    pa < endpa; pa += NBPG, va += NBPG) {
682		pmap_enter(pmap_kernel(), va, pa,
683		    PROT_READ | PROT_WRITE | PROT_EXEC,
684		    PROT_READ | PROT_WRITE | PROT_EXEC | PMAP_WIRED);
685
686		if (pa >= trunc_page(base)) {
687			pmap_enter(pmap_kernel(), sva, pa,
688			    PROT_READ | PROT_WRITE | PROT_EXEC,
689			    PROT_READ | PROT_WRITE | PROT_EXEC | PMAP_WIRED);
690			sva += NBPG;
691		}
692	}
693
694	e->segment = GSEL(GBIOS32_SEL, SEL_KPL);
695	e->offset = (vaddr_t)ent;
696
697	ei->bei_base = base;
698	ei->bei_size = count;
699	ei->bei_entry = ent;
700
701	return 1;
702}
703
704void
705bios32_cleanup(void)
706{
707	u_long pa, size;
708	vaddr_t va;
709
710	size = round_page(BIOS32_END);
711
712	for (va = bios_softc->bios32_service_va;
713	    va < bios_softc->bios32_service_va + size;
714	    va += NBPG) {
715		if (pmap_extract(pmap_kernel(), va, &pa))
716			pmap_remove(pmap_kernel(), va, va + PAGE_SIZE);
717	}
718
719	km_free((void *)bios_softc->bios32_service_va, size,
720	    &kv_any, &kp_none);
721}
722
723int
724biosopen(dev_t dev, int flag, int mode, struct proc *p)
725{
726	struct bios_softc *sc = bios_cd.cd_devs[0];
727
728	if (minor(dev))
729		return (ENXIO);
730
731	(void)sc;
732
733	return 0;
734}
735
736int
737biosclose(dev_t dev, int flag, int mode, struct proc *p)
738{
739	struct bios_softc *sc = bios_cd.cd_devs[0];
740
741	if (minor(dev))
742		return (ENXIO);
743
744	(void)sc;
745
746	return 0;
747}
748
749int
750biosioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
751{
752	struct bios_softc *sc = bios_cd.cd_devs[0];
753
754	if (minor(dev))
755		return (ENXIO);
756
757	switch (cmd) {
758	default:
759		return ENXIO;
760	}
761
762	(void)sc;
763
764	return 0;
765}
766
767int
768bios_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
769    size_t newlen, struct proc *p)
770{
771	bios_diskinfo_t *pdi;
772	int biosdev;
773
774	/* all sysctl names at this level except diskinfo are terminal */
775	if (namelen != 1 && name[0] != BIOS_DISKINFO)
776		return (ENOTDIR);		/* overloaded */
777
778	if (!(bootapiver & BAPIV_VECTOR))
779		return EOPNOTSUPP;
780
781	switch (name[0]) {
782	case BIOS_DEV:
783		if ((pdi = bios_getdiskinfo(bootdev)) == NULL)
784			return ENXIO;
785		biosdev = pdi->bios_number;
786		return sysctl_rdint(oldp, oldlenp, newp, biosdev);
787	case BIOS_DISKINFO:
788		if (namelen != 2)
789			return ENOTDIR;
790		if ((pdi = bios_getdiskinfo(name[1])) == NULL)
791			return ENXIO;
792		return sysctl_rdstruct(oldp, oldlenp, newp, pdi, sizeof(*pdi));
793	case BIOS_CKSUMLEN:
794		return sysctl_rdint(oldp, oldlenp, newp, bios_cksumlen);
795	default:
796		return EOPNOTSUPP;
797	}
798	/* NOTREACHED */
799}
800
801bios_diskinfo_t *
802bios_getdiskinfo(dev_t dev)
803{
804	bios_diskinfo_t *pdi;
805
806	if (bios_diskinfo == NULL)
807		return NULL;
808
809	for (pdi = bios_diskinfo; pdi->bios_number != -1; pdi++) {
810		if ((dev & B_MAGICMASK) == B_DEVMAGIC) { /* search by bootdev */
811			if (pdi->bsd_dev == dev)
812				break;
813		} else {
814			if (pdi->bios_number == dev)
815				break;
816		}
817	}
818
819	if (pdi->bios_number == -1)
820		return NULL;
821	else
822		return pdi;
823}
824
825/*
826 * smbios_find_table() takes a caller supplied smbios struct type and
827 * a pointer to a handle (struct smbtable) returning one if the structure
828 * is successfully located and zero otherwise. Callers should take care
829 * to initialize the cookie field of the smbtable structure to zero before
830 * the first invocation of this function.
831 * Multiple tables of the same type can be located by repeatedly calling
832 * smbios_find_table with the same arguments.
833 */
834int
835smbios_find_table(uint8_t type, struct smbtable *st)
836{
837	uint8_t *va, *end;
838	struct smbtblhdr *hdr;
839	int ret = 0, tcount = 1;
840
841	va = smbios_entry.addr;
842	end = va + smbios_entry.len;
843
844	/*
845	 * The cookie field of the smtable structure is used to locate
846	 * multiple instances of a table of an arbitrary type. Following the
847	 * successful location of a table, the type is encoded as bits 0:7 of
848	 * the cookie value, the offset in terms of the number of structures
849	 * preceding that referenced by the handle is encoded in bits 15:31.
850	 */
851	if ((st->cookie & 0xfff) == type && st->cookie >> 16) {
852		if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) {
853			hdr = st->hdr;
854			if (hdr->type == type) {
855				va = (uint8_t *)hdr + hdr->size;
856				for (; va + 1 < end; va++)
857					if (*va == 0 && *(va + 1) == 0)
858						break;
859				va += 2;
860				tcount = st->cookie >> 16;
861			}
862		}
863	}
864	for (; va + sizeof(struct smbtblhdr) < end &&
865	    tcount <= smbios_entry.count; tcount++) {
866		hdr = (struct smbtblhdr *)va;
867		if (hdr->type == type) {
868			ret = 1;
869			st->hdr = hdr;
870			st->tblhdr = va + sizeof(struct smbtblhdr);
871			st->cookie = (tcount + 1) << 16 | type;
872			break;
873		}
874		if (hdr->type == SMBIOS_TYPE_EOT)
875			break;
876		va += hdr->size;
877		for (; va + 1 < end; va++)
878			if (*va == 0 && *(va + 1) == 0)
879				break;
880		va += 2;
881	}
882	return ret;
883}
884
885char *
886smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len)
887{
888	uint8_t *va, *end;
889	char *ret = NULL;
890	int i;
891
892	va = (uint8_t *)st->hdr + st->hdr->size;
893	end = smbios_entry.addr + smbios_entry.len;
894	for (i = 1; va < end && i < indx && *va; i++)
895		while (*va++)
896			;
897	if (i == indx) {
898		if (va + len < end) {
899			ret = dest;
900			memcpy(ret, va, len);
901			ret[len - 1] = '\0';
902		}
903	}
904
905	return ret;
906}
907
908char *
909fixstring(char *s)
910{
911	char *p, *e;
912	int i;
913
914	for (i = 0; i < nitems(smbios_uninfo); i++)
915		if ((strncasecmp(s, smbios_uninfo[i],
916		    strlen(smbios_uninfo[i]))) == 0)
917			return NULL;
918	/*
919	 * Remove leading and trailing whitespace
920	 */
921	for (p = s; *p == ' '; p++)
922		;
923	/*
924	 * Special case entire string is whitespace
925	 */
926	if (p == s + strlen(s))
927		return NULL;
928	for (e = s + strlen(s) - 1; e > s && *e == ' '; e--)
929		;
930	if (p > s || e < s + strlen(s) - 1) {
931		memmove(s, p, e - p + 1);
932		s[e - p + 1] = '\0';
933	}
934
935	return s;
936}
937
938void
939smbios_info(char *str)
940{
941	char *sminfop, sminfo[64];
942	struct smbtable stbl, btbl;
943	struct smbios_sys *sys;
944	struct smbios_board *board;
945	int i, infolen, uuidf, havebb;
946	char *p;
947
948	if (smbios_entry.mjr < 2)
949		return;
950	/*
951	 * According to the spec the system table among others is required,
952	 * if it is not we do not bother with this smbios implementation.
953	 */
954	stbl.cookie = btbl.cookie = 0;
955	if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl))
956		return;
957	havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl);
958
959	sys = (struct smbios_sys *)stbl.tblhdr;
960	if (havebb) {
961		board = (struct smbios_board *)btbl.tblhdr;
962
963		sminfop = NULL;
964		if ((p = smbios_get_string(&btbl, board->vendor,
965		    sminfo, sizeof(sminfo))) != NULL)
966			sminfop = fixstring(p);
967		if (sminfop)
968			strlcpy(smbios_board_vendor, sminfop,
969			    sizeof(smbios_board_vendor));
970
971		sminfop = NULL;
972		if ((p = smbios_get_string(&btbl, board->product,
973		    sminfo, sizeof(sminfo))) != NULL)
974			sminfop = fixstring(p);
975		if (sminfop)
976			strlcpy(smbios_board_prod, sminfop,
977			    sizeof(smbios_board_prod));
978
979		sminfop = NULL;
980		if ((p = smbios_get_string(&btbl, board->serial,
981		    sminfo, sizeof(sminfo))) != NULL)
982			sminfop = fixstring(p);
983		if (sminfop)
984			strlcpy(smbios_board_serial, sminfop,
985			    sizeof(smbios_board_serial));
986	}
987
988	/*
989	 * Some smbios implementations have no system vendor or
990	 * product strings, some have very uninformative data which is
991	 * harder to work around and we must rely upon various
992	 * heuristics to detect this. In both cases we attempt to fall
993	 * back on the base board information in the perhaps naive
994	 * belief that motherboard vendors will supply this
995	 * information.
996	 */
997	sminfop = NULL;
998	if ((p = smbios_get_string(&stbl, sys->vendor, sminfo,
999	    sizeof(sminfo))) != NULL)
1000		sminfop = fixstring(p);
1001	if (sminfop == NULL) {
1002		if (havebb) {
1003			if ((p = smbios_get_string(&btbl, board->vendor,
1004			    sminfo, sizeof(sminfo))) != NULL)
1005				sminfop = fixstring(p);
1006		}
1007	}
1008	if (sminfop) {
1009		infolen = strlen(sminfop) + 1;
1010		hw_vendor = malloc(infolen, M_DEVBUF, M_NOWAIT);
1011		if (hw_vendor)
1012			strlcpy(hw_vendor, sminfop, infolen);
1013		sminfop = NULL;
1014	}
1015	if ((p = smbios_get_string(&stbl, sys->product, sminfo,
1016	    sizeof(sminfo))) != NULL)
1017		sminfop = fixstring(p);
1018	if (sminfop == NULL) {
1019		if (havebb) {
1020			if ((p = smbios_get_string(&btbl, board->product,
1021			    sminfo, sizeof(sminfo))) != NULL)
1022				sminfop = fixstring(p);
1023		}
1024	}
1025	if (sminfop) {
1026		infolen = strlen(sminfop) + 1;
1027		hw_prod = malloc(infolen, M_DEVBUF, M_NOWAIT);
1028		if (hw_prod)
1029			strlcpy(hw_prod, sminfop, infolen);
1030		sminfop = NULL;
1031	}
1032	if (hw_vendor != NULL && hw_prod != NULL)
1033		printf("\n%s: %s %s", str, hw_vendor, hw_prod);
1034	if ((p = smbios_get_string(&stbl, sys->version, sminfo,
1035	    sizeof(sminfo))) != NULL)
1036		sminfop = fixstring(p);
1037	if (sminfop) {
1038		infolen = strlen(sminfop) + 1;
1039		hw_ver = malloc(infolen, M_DEVBUF, M_NOWAIT);
1040		if (hw_ver)
1041			strlcpy(hw_ver, sminfop, infolen);
1042		sminfop = NULL;
1043	}
1044	if ((p = smbios_get_string(&stbl, sys->serial, sminfo,
1045	    sizeof(sminfo))) != NULL)
1046		sminfop = fixstring(p);
1047	if (sminfop) {
1048		infolen = strlen(sminfop) + 1;
1049		for (i = 0; i < infolen - 1; i++)
1050			enqueue_randomness(sminfop[i]);
1051		hw_serial = malloc(infolen, M_DEVBUF, M_NOWAIT);
1052		if (hw_serial)
1053			strlcpy(hw_serial, sminfop, infolen);
1054	}
1055	if (smbios_entry.mjr > 2 || (smbios_entry.mjr == 2 &&
1056	    smbios_entry.min >= 1)) {
1057		/*
1058		 * If the uuid value is all 0xff the uuid is present but not
1059		 * set, if its all 0 then the uuid isn't present at all.
1060		 */
1061		uuidf = SMBIOS_UUID_NPRESENT|SMBIOS_UUID_NSET;
1062		for (i = 0; i < sizeof(sys->uuid); i++) {
1063			if (sys->uuid[i] != 0xff)
1064				uuidf &= ~SMBIOS_UUID_NSET;
1065			if (sys->uuid[i] != 0)
1066				uuidf &= ~SMBIOS_UUID_NPRESENT;
1067		}
1068
1069		if (uuidf & SMBIOS_UUID_NPRESENT)
1070			hw_uuid = NULL;
1071		else if (uuidf & SMBIOS_UUID_NSET)
1072			hw_uuid = "Not Set";
1073		else {
1074			for (i = 0; i < sizeof(sys->uuid); i++)
1075				enqueue_randomness(sys->uuid[i]);
1076			hw_uuid = malloc(SMBIOS_UUID_REPLEN, M_DEVBUF,
1077			    M_NOWAIT);
1078			if (hw_uuid) {
1079				snprintf(hw_uuid, SMBIOS_UUID_REPLEN,
1080				    SMBIOS_UUID_REP,
1081				    sys->uuid[0], sys->uuid[1], sys->uuid[2],
1082				    sys->uuid[3], sys->uuid[4], sys->uuid[5],
1083				    sys->uuid[6], sys->uuid[7], sys->uuid[8],
1084				    sys->uuid[9], sys->uuid[10], sys->uuid[11],
1085				    sys->uuid[12], sys->uuid[13], sys->uuid[14],
1086				    sys->uuid[15]);
1087			}
1088		}
1089	}
1090}
1091