bios.c revision 49953
1/*-
2 * Copyright (c) 1997 Michael Smith
3 * Copyright (c) 1998 Jonathan Lemon
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *      $Id: bios.c,v 1.14 1999/07/29 06:48:26 msmith Exp $
28 */
29
30/*
31 * Code for dealing with the BIOS in x86 PC systems.
32 */
33
34#include <sys/param.h>
35#include <sys/proc.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <vm/vm.h>
40#include <vm/pmap.h>
41#include <machine/md_var.h>
42#include <machine/segments.h>
43#include <machine/stdarg.h>
44#include <machine/tss.h>
45#include <machine/vmparam.h>
46#include <machine/pc/bios.h>
47#include <i386/isa/isa_device.h>
48#include <i386/isa/pnp.h>
49
50#define BIOS_START	0xe0000
51#define BIOS_SIZE	0x20000
52
53/* exported lookup results */
54struct bios32_SDentry		PCIbios = {entry : 0};
55struct SMBIOS_table		*SMBIOStable = 0;
56struct PnPBIOS_table		*PnPBIOStable = 0;
57
58static u_int			bios32_SDCI = 0;
59
60/* start fairly early */
61static void			bios32_init(void *junk);
62SYSINIT(bios32, SI_SUB_CPU, SI_ORDER_ANY, bios32_init, NULL);
63
64static void	pnpbios_scan(void);
65static char 	*pnp_eisaformat(u_int8_t *data);
66
67
68/*
69 * bios32_init
70 *
71 * Locate various bios32 entities.
72 */
73static void
74bios32_init(void *junk)
75{
76    u_long			sigaddr;
77    struct bios32_SDheader	*sdh;
78    struct SMBIOS_table		*sbt;
79    struct PnPBIOS_table	*pt;
80    u_int8_t			ck, *cv;
81    int				i;
82
83
84    /*
85     * BIOS32 Service Directory
86     */
87
88    /* look for the signature */
89    if ((sigaddr = bios_sigsearch(0, "_32_", 4, 16, 0)) != 0) {
90
91	/* get a virtual pointer to the structure */
92	sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
93	for (cv = (u_int8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) {
94	    ck += cv[i];
95	}
96	/* If checksum is OK, enable use of the entrypoint */
97	if ((ck == 0) && (sdh->entry < (BIOS_START + BIOS_SIZE))) {
98	    bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry);
99	    if (bootverbose) {
100		printf("bios32: Found BIOS32 Service Directory header at %p\n", sdh);
101		printf("bios32: Entry = 0x%x (%x)  Rev = %d  Len = %d\n",
102		       sdh->entry, bios32_SDCI, sdh->revision, sdh->len);
103	    }
104	    /* See if there's a PCI BIOS entrypoint here */
105	    PCIbios.ident.id = 0x49435024;	/* PCI systems should have this */
106	    if (!bios32_SDlookup(&PCIbios) && bootverbose)
107		printf("pcibios: PCI BIOS entry at 0x%x\n", PCIbios.entry);
108	} else {
109	    printf("bios32: Bad BIOS32 Service Directory\n");
110	}
111    }
112
113    /*
114     * System Management BIOS
115     */
116    /* look for the SMBIOS signature */
117    if ((sigaddr = bios_sigsearch(0, "_SM_", 4, 16, 0)) != 0) {
118
119	/* get a virtual pointer to the structure */
120	sbt = (struct SMBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
121	for (cv = (u_int8_t *)sbt, ck = 0, i = 0; i < sbt->len; i++) {
122	    ck += cv[i];
123	}
124	/* if checksum is OK, we have action */
125	if (ck == 0) {
126	    SMBIOStable = sbt;		/* save reference */
127	    if (bootverbose) {
128		printf("smbios: SMBIOS header at %p\n", sbt);
129		printf("smbios: Version %d.%d\n", sbt->major, sbt->minor);
130		printf("smbios: Table at 0x%x, %d entries, %d bytes, largest entry %d bytes\n",
131		       sbt->dmi.st_base, (int)sbt->dmi.st_entries, (int)sbt->dmi.st_size,
132		       (int)sbt->st_maxsize);
133	    }
134	} else {
135	    printf("smbios: Bad SMBIOS table checksum\n");
136	}
137
138    }
139
140    /*
141     * PnP BIOS
142     */
143    if ((sigaddr = bios_sigsearch(0, "$PnP", 4, 16, 0)) != 0) {
144
145	/* get a virtual pointer to the structure */
146	pt = (struct PnPBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
147	for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < pt->len; i++) {
148	    ck += cv[i];
149	}
150	/* If checksum is OK, enable use of the entrypoint */
151	if (ck == 0) {
152	    PnPBIOStable = pt;
153	    if (bootverbose) {
154		printf("pnpbios: Found PnP BIOS data at %p\n", pt);
155		printf("pnpbios: Entry = %x:%x  Rev = %d.%d\n",
156		       pt->pmentrybase, pt->pmentryoffset, pt->version >> 4, pt->version & 0xf);
157		if ((pt->control & 0x3) == 0x01)
158		    printf("pnpbios: Event flag at %x\n", pt->evflagaddr);
159		if (pt->oemdevid != 0)
160		    printf("pnpbios: OEM ID %x\n", pt->oemdevid);
161
162	    }
163	    pnpbios_scan();
164	} else {
165	    printf("pnpbios: Bad PnP BIOS data checksum\n");
166	}
167    }
168
169    if (bootverbose) {
170	    /* look for other know signatures */
171	    printf("Other BIOS signatures found:\n");
172	    printf("ACPI: %08x\n", bios_sigsearch(0, "RST PTR", 8, 16, 0));
173    }
174}
175
176/*
177 * bios32_SDlookup
178 *
179 * Query the BIOS32 Service Directory for the service named in (ent),
180 * returns nonzero if the lookup fails.  The caller must fill in
181 * (ent->ident), the remainder are populated on a successful lookup.
182 */
183int
184bios32_SDlookup(struct bios32_SDentry *ent)
185{
186    struct bios_regs args;
187
188    if (bios32_SDCI == 0)
189	return (1);
190
191    args.eax = ent->ident.id;		/* set up arguments */
192    args.ebx = args.ecx = args.edx = 0;
193    bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL));
194    if ((args.eax & 0xff) == 0) {	/* success? */
195	ent->base = args.ebx;
196	ent->len = args.ecx;
197	ent->entry = args.edx;
198	return (0);			/* all OK */
199    }
200    return (1);				/* failed */
201}
202
203
204/*
205 * bios_sigsearch
206 *
207 * Search some or all of the BIOS region for a signature string.
208 *
209 * (start)	Optional offset returned from this function
210 *		(for searching for multiple matches), or NULL
211 *		to start the search from the base of the BIOS.
212 *		Note that this will be a _physical_ address in
213 *		the range 0xe0000 - 0xfffff.
214 * (sig)	is a pointer to the byte(s) of the signature.
215 * (siglen)	number of bytes in the signature.
216 * (paralen)	signature paragraph (alignment) size.
217 * (sigofs)	offset of the signature within the paragraph.
218 *
219 * Returns the _physical_ address of the found signature, 0 if the
220 * signature was not found.
221 */
222
223u_int32_t
224bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs)
225{
226    u_char	*sp, *end;
227
228    /* compute the starting address */
229    if ((start >= BIOS_START) && (start <= (BIOS_START + BIOS_SIZE))) {
230	sp = (char *)BIOS_PADDRTOVADDR(start);
231    } else if (start == 0) {
232	sp = (char *)BIOS_PADDRTOVADDR(BIOS_START);
233    } else {
234	return 0;				/* bogus start address */
235    }
236
237    /* compute the end address */
238    end = (u_char *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_SIZE);
239
240    /* loop searching */
241    while ((sp + sigofs + siglen) < end) {
242
243	/* compare here */
244	if (!bcmp(sp + sigofs, sig, siglen)) {
245	    /* convert back to physical address */
246	    return((u_int32_t)BIOS_VADDRTOPADDR(sp));
247	}
248	sp += paralen;
249    }
250    return(0);
251}
252
253/*
254 * do not staticize, used by bioscall.s
255 */
256union {
257    struct {
258	u_short	offset;
259	u_short	segment;
260    } vec16;
261    struct {
262	u_int	offset;
263	u_short	segment;
264    } vec32;
265} bioscall_vector;			/* bios jump vector */
266
267void
268set_bios_selectors(struct bios_segments *seg, int flags)
269{
270    static u_int curgen = 1;
271    struct soft_segment_descriptor ssd = {
272	0,			/* segment base address (overwritten) */
273	0,			/* length (overwritten) */
274	SDT_MEMERA,		/* segment type (overwritten) */
275	0,			/* priority level */
276	1,			/* descriptor present */
277	0, 0,
278	1,			/* descriptor size (overwritten) */
279	0			/* granularity == byte units */
280    };
281
282    if (seg->generation == curgen)
283	return;
284    if (++curgen == 0)
285	curgen = 1;
286    seg->generation = curgen;
287
288    ssd.ssd_base = seg->code32.base;
289    ssd.ssd_limit = seg->code32.limit;
290    ssdtosd(&ssd, &gdt[GBIOSCODE32_SEL].sd);
291
292    ssd.ssd_def32 = 0;
293    if (flags & BIOSCODE_FLAG) {
294	ssd.ssd_base = seg->code16.base;
295	ssd.ssd_limit = seg->code16.limit;
296	ssdtosd(&ssd, &gdt[GBIOSCODE16_SEL].sd);
297    }
298
299    ssd.ssd_type = SDT_MEMRWA;
300    if (flags & BIOSDATA_FLAG) {
301	ssd.ssd_base = seg->data.base;
302	ssd.ssd_limit = seg->data.limit;
303	ssdtosd(&ssd, &gdt[GBIOSDATA_SEL].sd);
304    }
305
306    if (flags & BIOSUTIL_FLAG) {
307	ssd.ssd_base = seg->util.base;
308	ssd.ssd_limit = seg->util.limit;
309	ssdtosd(&ssd, &gdt[GBIOSUTIL_SEL].sd);
310    }
311
312    if (flags & BIOSARGS_FLAG) {
313	ssd.ssd_base = seg->args.base;
314	ssd.ssd_limit = seg->args.limit;
315	ssdtosd(&ssd, &gdt[GBIOSARGS_SEL].sd);
316    }
317}
318
319/*
320 * for pointers, we don't know how much space is supposed to be allocated,
321 * so we assume a minimum size of 256 bytes.  If more than this is needed,
322 * then this can be revisited, such as adding a length specifier.
323 */
324#define	ASSUMED_ARGSIZE		256
325
326extern int vm86pa;
327
328/*
329 * this routine is really greedy with selectors, and uses 5:
330 *
331 * 32-bit code selector:	to return to kernel
332 * 16-bit code selector:	for running code
333 *        data selector:	for 16-bit data
334 *        util selector:	extra utility selector
335 *        args selector:	to handle pointers
336 *
337 * the util selector is set from the util16 entry in bios16_args, if a
338 * "U" specifier is seen.
339 *
340 * See <machine/pc/bios.h> for description of format specifiers
341 */
342int
343bios16(struct bios_args *args, char *fmt, ...)
344{
345    char	*p, *stack, *stack_top;
346    va_list 	ap;
347    int 	flags = BIOSCODE_FLAG | BIOSDATA_FLAG;
348    u_int 	i, arg_start, arg_end;
349    u_int 	*pte, *ptd;
350
351    arg_start = 0xffffffff;
352    arg_end = 0;
353
354    stack = (caddr_t)PAGE_SIZE;
355    va_start(ap, fmt);
356    for (p = fmt; p && *p; p++) {
357	switch (*p) {
358	case 'p':			/* 32-bit pointer */
359	    i = va_arg(ap, u_int);
360	    arg_start = min(arg_start, i);
361	    arg_end = max(arg_end, i + ASSUMED_ARGSIZE);
362	    flags |= BIOSARGS_FLAG;
363	    stack -= 4;
364	    break;
365
366	case 'i':			/* 32-bit integer */
367	    i = va_arg(ap, u_int);
368	    stack -= 4;
369	    break;
370
371	case 'U':			/* 16-bit selector */
372	    flags |= BIOSUTIL_FLAG;
373	    /* FALLTHROUGH */
374	case 'D':			/* 16-bit selector */
375	case 'C':			/* 16-bit selector */
376	    stack -= 2;
377	    break;
378
379	case 's':			/* 16-bit integer */
380	    i = va_arg(ap, u_short);
381	    stack -= 2;
382	    break;
383
384	default:
385	    return (EINVAL);
386	}
387    }
388
389    if (flags & BIOSARGS_FLAG) {
390	if (arg_end - arg_start > ctob(16))
391	    return (EACCES);
392	args->seg.args.base = arg_start;
393	args->seg.args.limit = arg_end - arg_start;
394    }
395
396    args->seg.code32.base = (u_int)&bios16_call & PG_FRAME;
397    args->seg.code32.limit = 0xffff;
398
399    ptd = (u_int *)rcr3();
400    if (ptd == IdlePTD) {
401	/*
402	 * no page table, so create one and install it.
403	 */
404	pte = (u_int *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
405	ptd = (u_int *)((u_int)ptd + KERNBASE);
406	*ptd = vtophys(pte) | PG_RW | PG_V;
407    } else {
408	/*
409	 * this is a user-level page table
410	 */
411	pte = (u_int *)&PTmap;
412    }
413    /*
414     * install pointer to page 0.  we don't need to flush the tlb,
415     * since there should not be a previous mapping for page 0.
416     */
417    *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V;
418
419    stack_top = stack;
420    va_start(ap, fmt);
421    for (p = fmt; p && *p; p++) {
422	switch (*p) {
423	case 'p':			/* 32-bit pointer */
424	    i = va_arg(ap, u_int);
425	    *(u_int *)stack = (i - arg_start) |
426		(GSEL(GBIOSARGS_SEL, SEL_KPL) << 16);
427	    stack += 4;
428	    break;
429
430	case 'i':			/* 32-bit integer */
431	    i = va_arg(ap, u_int);
432	    *(u_int *)stack = i;
433	    stack += 4;
434	    break;
435
436	case 'U':			/* 16-bit selector */
437	    *(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL);
438	    stack += 2;
439	    break;
440
441	case 'D':			/* 16-bit selector */
442	    *(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL);
443	    stack += 2;
444	    break;
445
446	case 'C':			/* 16-bit selector */
447	    *(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL);
448	    stack += 2;
449	    break;
450
451	case 's':			/* 16-bit integer */
452	    i = va_arg(ap, u_short);
453	    *(u_short *)stack = i;
454	    stack += 2;
455	    break;
456
457	default:
458	    return (EINVAL);
459	}
460    }
461
462    args->seg.generation = 0;			/* reload selectors */
463    set_bios_selectors(&args->seg, flags);
464    bioscall_vector.vec16.offset = (u_short)args->entry;
465    bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL);
466
467    i = bios16_call(&args->r, stack_top);
468
469    if (pte == (u_int *)&PTmap) {
470	*pte = 0;			/* remove entry */
471    } else {
472	*ptd = 0;			/* remove page table */
473	free(pte, M_TEMP);		/* ... and free it */
474    }
475
476    /*
477     * XXX only needs to be invlpg(0) but that doesn't work on the 386
478     */
479    invltlb();
480
481    return (i);
482}
483
484/*
485 * PnP BIOS interface; enumerate devices only known to the system
486 * BIOS and save information about them for later use.
487 */
488
489struct pnp_sysdev
490{
491    u_int16_t	size;
492    u_int8_t	handle;
493    u_int32_t	devid;
494    u_int8_t	type[3];
495    u_int16_t	attrib;
496#define PNPATTR_NODISABLE	(1<<0)	/* can't be disabled */
497#define PNPATTR_NOCONFIG	(1<<1)	/* can't be configured */
498#define PNPATTR_OUTPUT		(1<<2)	/* can be primary output */
499#define PNPATTR_INPUT		(1<<3)	/* can be primary input */
500#define PNPATTR_BOOTABLE	(1<<4)	/* can be booted from */
501#define PNPATTR_DOCK		(1<<5)	/* is a docking station */
502#define PNPATTR_REMOVEABLE	(1<<6)	/* device is removeable */
503#define PNPATTR_CONFIG_STATIC	0x00
504#define PNPATTR_CONFIG_DYNAMIC	0x07
505#define PNPATTR_CONFIG_DYNONLY	0x17
506    /* device-specific data comes here */
507    u_int8_t	devdata[0];
508} __attribute__ ((packed));
509
510/* We have to cluster arguments within a 64k range for the bios16 call */
511struct pnp_sysdevargs
512{
513    u_int16_t	next;
514    struct pnp_sysdev node;
515};
516
517/*
518 * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
519 */
520static void
521pnpbios_scan(void)
522{
523    struct PnPBIOS_table	*pt = PnPBIOStable;
524    struct bios_args		args;
525    struct pnp_sysdev		*pd;
526    struct pnp_sysdevargs	*pda;
527    u_int16_t			ndevs, bigdev;
528    int				error, currdev;
529    u_int8_t			*devnodebuf, tag;
530    u_int32_t			*devid, *compid;
531    int				idx, left;
532
533    /* no PnP BIOS information */
534    if (pt == NULL)
535	return;
536
537    bzero(&args, sizeof(args));
538    args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
539    args.seg.code16.limit = 0xffff;		/* XXX ? */
540    args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
541    args.seg.data.limit = 0xffff;
542    args.entry = pt->pmentryoffset;
543
544    if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
545	printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
546    if (bootverbose)
547	printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
548
549    devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)),
550			M_DEVBUF, M_NOWAIT);
551    pda = (struct pnp_sysdevargs *)devnodebuf;
552    pd = &pda->node;
553
554    for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
555
556	bzero(pd, bigdev);
557	pda->next = currdev;
558	/* get current configuration */
559	if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, (u_int16_t)1))) {
560	    printf("pnpbios: error %d making BIOS16 call\n", error);
561	    break;
562	}
563	if ((error = (args.r.eax & 0xff))) {
564	    if (bootverbose)
565		printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
566	    if (error & 0x80)
567		break;
568	}
569	currdev = pda->next;
570	if (pd->size < sizeof(struct pnp_sysdev)) {
571	    printf("pnpbios: bogus system node data, aborting scan\n");
572	    break;
573	}
574
575	/* Find device IDs */
576	devid = &pd->devid;
577	compid = NULL;
578
579	/* look for a compatible device ID too */
580	left = pd->size - sizeof(struct pnp_sysdev);
581	idx = 0;
582	while (idx < left) {
583	    tag = pd->devdata[idx++];
584	    if (PNP_RES_TYPE(tag) == 0) {
585		/* Small resource */
586		switch (PNP_SRES_NUM(tag)) {
587		case COMP_DEVICE_ID:
588		    compid = (u_int32_t *)(pd->devdata + idx);
589		    if (bootverbose)
590			printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
591		    /* FALLTHROUGH */
592		case END_TAG:
593		    idx = left;
594		    break;
595		default:
596		    idx += PNP_SRES_LEN(tag);
597		    break;
598		}
599	    } else
600		/* Large resource, skip it */
601		idx += *(u_int16_t *)(pd->devdata + idx) + 2;
602	}
603	if (bootverbose) {
604	    printf("pnpbios: handle %d device ID %s (%08x)",
605		   pd->handle, pnp_eisaformat((u_int8_t *)devid), *devid);
606	    if (compid != NULL)
607		printf(" compat ID %s (%08x)",
608		       pnp_eisaformat((u_int8_t *)compid), *compid);
609	    printf("\n");
610	}
611    }
612}
613
614/* XXX should be somewhere else */
615static char *
616pnp_eisaformat(u_int8_t *data)
617{
618    static char idbuf[8];
619    const char  hextoascii[] = "0123456789abcdef";
620
621    idbuf[0] = '@' + ((data[0] & 0x7c) >> 2);
622    idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5));
623    idbuf[2] = '@' + (data[1] & 0x1f);
624    idbuf[3] = hextoascii[(data[2] >> 4)];
625    idbuf[4] = hextoascii[(data[2] & 0xf)];
626    idbuf[5] = hextoascii[(data[3] >> 4)];
627    idbuf[6] = hextoascii[(data[3] & 0xf)];
628    idbuf[7] = 0;
629    return(idbuf);
630}
631