bios.c revision 60668
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1997 Michael Smith
31556Srgrimes * Copyright (c) 1998 Jonathan Lemon
41556Srgrimes * All rights reserved.
51556Srgrimes *
61556Srgrimes * Redistribution and use in source and binary forms, with or without
71556Srgrimes * modification, are permitted provided that the following conditions
81556Srgrimes * are met:
91556Srgrimes * 1. Redistributions of source code must retain the above copyright
101556Srgrimes *    notice, this list of conditions and the following disclaimer.
111556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer in the
131556Srgrimes *    documentation and/or other materials provided with the distribution.
141556Srgrimes *
151556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
161556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
171556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
191556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
211556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
221556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
231556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
241556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
251556Srgrimes * SUCH DAMAGE.
261556Srgrimes *
271556Srgrimes * $FreeBSD: head/sys/amd64/amd64/bios.c 60668 2000-05-17 19:44:16Z msmith $
281556Srgrimes */
291556Srgrimes
301556Srgrimes/*
311556Srgrimes * Code for dealing with the BIOS in x86 PC systems.
321556Srgrimes */
331556Srgrimes
341556Srgrimes#include "isa.h"
353044Sdg#include "opt_smp.h"
369991Sache
371556Srgrimes#include <sys/param.h>
381556Srgrimes#include <sys/systm.h>
391556Srgrimes#include <sys/kernel.h>
401556Srgrimes#include <sys/malloc.h>
411556Srgrimes#include <sys/bus.h>
421556Srgrimes#include <vm/vm.h>
431556Srgrimes#include <vm/pmap.h>
441556Srgrimes#include <machine/md_var.h>
451556Srgrimes#include <machine/segments.h>
461556Srgrimes#include <machine/stdarg.h>
471556Srgrimes#include <machine/vmparam.h>
481556Srgrimes#include <machine/pc/bios.h>
491556Srgrimes#include <isa/pnpreg.h>
501556Srgrimes#include <isa/pnpvar.h>
511556Srgrimes#if NISA > 0
521556Srgrimes#include <isa/isavar.h>
531556Srgrimes#endif
541556Srgrimes
551556Srgrimes#define BIOS_START	0xe0000
561556Srgrimes#define BIOS_SIZE	0x20000
571556Srgrimes
581556Srgrimes/* exported lookup results */
591556Srgrimesstruct bios32_SDentry		PCIbios = {entry : 0};
601556Srgrimesstruct PnPBIOS_table		*PnPBIOStable = 0;
611556Srgrimes
621556Srgrimesstatic u_int			bios32_SDCI = 0;
631556Srgrimes
641556Srgrimes/* start fairly early */
651556Srgrimesstatic void			bios32_init(void *junk);
661556SrgrimesSYSINIT(bios32, SI_SUB_CPU, SI_ORDER_ANY, bios32_init, NULL);
671556Srgrimes
681556Srgrimes/*
691556Srgrimes * bios32_init
701556Srgrimes *
711556Srgrimes * Locate various bios32 entities.
721556Srgrimes */
731556Srgrimesstatic void
741556Srgrimesbios32_init(void *junk)
751556Srgrimes{
761556Srgrimes    u_long			sigaddr;
771556Srgrimes    struct bios32_SDheader	*sdh;
781556Srgrimes    struct PnPBIOS_table	*pt;
791556Srgrimes    u_int8_t			ck, *cv;
801556Srgrimes    int				i;
811556Srgrimes    char			*p;
821556Srgrimes
831556Srgrimes    /*
841556Srgrimes     * BIOS32 Service Directory, PCI BIOS
851556Srgrimes     */
861556Srgrimes
871556Srgrimes    /* look for the signature */
881556Srgrimes    if ((sigaddr = bios_sigsearch(0, "_32_", 4, 16, 0)) != 0) {
891556Srgrimes
901556Srgrimes	/* get a virtual pointer to the structure */
911556Srgrimes	sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
921556Srgrimes	for (cv = (u_int8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) {
931556Srgrimes	    ck += cv[i];
941556Srgrimes	}
951556Srgrimes	/* If checksum is OK, enable use of the entrypoint */
961556Srgrimes	if ((ck == 0) && (sdh->entry < (BIOS_START + BIOS_SIZE))) {
971556Srgrimes	    bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry);
981556Srgrimes	    if (bootverbose) {
991556Srgrimes		printf("bios32: Found BIOS32 Service Directory header at %p\n", sdh);
1001556Srgrimes		printf("bios32: Entry = 0x%x (%x)  Rev = %d  Len = %d\n",
1011556Srgrimes		       sdh->entry, bios32_SDCI, sdh->revision, sdh->len);
1021556Srgrimes	    }
1031556Srgrimes
1041556Srgrimes#ifndef PC98
1051556Srgrimes	    /* Allow user override of PCI BIOS search */
1061556Srgrimes	    if (((p = getenv("machdep.bios.pci")) == NULL) || strcmp(p, "disable")) {
1071556Srgrimes
1081556Srgrimes		/* See if there's a PCI BIOS entrypoint here */
1091556Srgrimes		PCIbios.ident.id = 0x49435024;	/* PCI systems should have this */
1101556Srgrimes		if (!bios32_SDlookup(&PCIbios) && bootverbose)
1111556Srgrimes		    printf("pcibios: PCI BIOS entry at 0x%x+0x%x\n", PCIbios.base, PCIbios.entry);
1121556Srgrimes	    }
1131556Srgrimes#endif
1141556Srgrimes	} else {
1151556Srgrimes	    printf("bios32: Bad BIOS32 Service Directory\n");
1161556Srgrimes	}
1171556Srgrimes    }
1181556Srgrimes
1191556Srgrimes    /*
1201556Srgrimes     * PnP BIOS
1211556Srgrimes     *
1221556Srgrimes     * Allow user override of PnP BIOS search
1231556Srgrimes     */
1241556Srgrimes    if ((((p = getenv("machdep.bios.pnp")) == NULL) || strcmp(p, "disable")) &&
1251556Srgrimes	((sigaddr = bios_sigsearch(0, "$PnP", 4, 16, 0)) != 0)) {
1261556Srgrimes
1271556Srgrimes	/* get a virtual pointer to the structure */
1281556Srgrimes	pt = (struct PnPBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
1291556Srgrimes	for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < pt->len; i++) {
1301556Srgrimes	    ck += cv[i];
1311556Srgrimes	}
1321556Srgrimes	/* If checksum is OK, enable use of the entrypoint */
1331556Srgrimes	if (ck == 0) {
1341556Srgrimes	    PnPBIOStable = pt;
1351556Srgrimes	    if (bootverbose) {
1361556Srgrimes		printf("pnpbios: Found PnP BIOS data at %p\n", pt);
1371556Srgrimes		printf("pnpbios: Entry = %x:%x  Rev = %d.%d\n",
1381556Srgrimes		       pt->pmentrybase, pt->pmentryoffset, pt->version >> 4, pt->version & 0xf);
1391556Srgrimes		if ((pt->control & 0x3) == 0x01)
1401556Srgrimes		    printf("pnpbios: Event flag at %x\n", pt->evflagaddr);
1411556Srgrimes		if (pt->oemdevid != 0)
1421556Srgrimes		    printf("pnpbios: OEM ID %x\n", pt->oemdevid);
1431556Srgrimes
1441556Srgrimes	    }
1451556Srgrimes	} else {
1461556Srgrimes	    printf("pnpbios: Bad PnP BIOS data checksum\n");
1471556Srgrimes	}
1481556Srgrimes    }
1491556Srgrimes
1501556Srgrimes    if (bootverbose) {
1511556Srgrimes	    /* look for other know signatures */
1521556Srgrimes	    printf("Other BIOS signatures found:\n");
1531556Srgrimes	    printf("ACPI: %08x\n", bios_sigsearch(0, "RSD PTR ", 8, 16, 0));
1541556Srgrimes    }
1551556Srgrimes}
1561556Srgrimes
1571556Srgrimes/*
1581556Srgrimes * bios32_SDlookup
1591556Srgrimes *
1601556Srgrimes * Query the BIOS32 Service Directory for the service named in (ent),
1611556Srgrimes * returns nonzero if the lookup fails.  The caller must fill in
1621556Srgrimes * (ent->ident), the remainder are populated on a successful lookup.
1631556Srgrimes */
1641556Srgrimesint
1651556Srgrimesbios32_SDlookup(struct bios32_SDentry *ent)
1661556Srgrimes{
1671556Srgrimes    struct bios_regs args;
1681556Srgrimes
1691556Srgrimes    if (bios32_SDCI == 0)
1701556Srgrimes	return (1);
1711556Srgrimes
1721556Srgrimes    args.eax = ent->ident.id;		/* set up arguments */
1731556Srgrimes    args.ebx = args.ecx = args.edx = 0;
1741556Srgrimes    bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL));
1751556Srgrimes    if ((args.eax & 0xff) == 0) {	/* success? */
1761556Srgrimes	ent->base = args.ebx;
1771556Srgrimes	ent->len = args.ecx;
1781556Srgrimes	ent->entry = args.edx;
1791556Srgrimes	ent->ventry = BIOS_PADDRTOVADDR(ent->base + ent->entry);
1801556Srgrimes	return (0);			/* all OK */
1811556Srgrimes    }
1821556Srgrimes    return (1);				/* failed */
1831556Srgrimes}
1841556Srgrimes
1851556Srgrimes
1861556Srgrimes/*
1871556Srgrimes * bios_sigsearch
1881556Srgrimes *
1891556Srgrimes * Search some or all of the BIOS region for a signature string.
1907165Sjoerg *
1911556Srgrimes * (start)	Optional offset returned from this function
1921556Srgrimes *		(for searching for multiple matches), or NULL
1931556Srgrimes *		to start the search from the base of the BIOS.
1941556Srgrimes *		Note that this will be a _physical_ address in
1951556Srgrimes *		the range 0xe0000 - 0xfffff.
1961556Srgrimes * (sig)	is a pointer to the byte(s) of the signature.
1971556Srgrimes * (siglen)	number of bytes in the signature.
1981556Srgrimes * (paralen)	signature paragraph (alignment) size.
1991556Srgrimes * (sigofs)	offset of the signature within the paragraph.
2001556Srgrimes *
2011556Srgrimes * Returns the _physical_ address of the found signature, 0 if the
2021556Srgrimes * signature was not found.
2031556Srgrimes */
2041556Srgrimes
2051556Srgrimesu_int32_t
2061556Srgrimesbios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs)
2071556Srgrimes{
2081556Srgrimes    u_char	*sp, *end;
2091556Srgrimes
2101556Srgrimes    /* compute the starting address */
2111556Srgrimes    if ((start >= BIOS_START) && (start <= (BIOS_START + BIOS_SIZE))) {
2121556Srgrimes	sp = (char *)BIOS_PADDRTOVADDR(start);
2131556Srgrimes    } else if (start == 0) {
2141556Srgrimes	sp = (char *)BIOS_PADDRTOVADDR(BIOS_START);
2151556Srgrimes    } else {
2161556Srgrimes	return 0;				/* bogus start address */
2171556Srgrimes    }
2181556Srgrimes
2191556Srgrimes    /* compute the end address */
2201556Srgrimes    end = (u_char *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_SIZE);
2211556Srgrimes
2221556Srgrimes    /* loop searching */
2231556Srgrimes    while ((sp + sigofs + siglen) < end) {
2241556Srgrimes
2251556Srgrimes	/* compare here */
2261556Srgrimes	if (!bcmp(sp + sigofs, sig, siglen)) {
2271556Srgrimes	    /* convert back to physical address */
2281556Srgrimes	    return((u_int32_t)BIOS_VADDRTOPADDR(sp));
2291556Srgrimes	}
2309991Sache	sp += paralen;
2311556Srgrimes    }
2329991Sache    return(0);
2331556Srgrimes}
2341556Srgrimes
2351556Srgrimes/*
2369987Swollman * do not staticize, used by bioscall.s
2371556Srgrimes */
2381556Srgrimesunion {
2391556Srgrimes    struct {
2401556Srgrimes	u_short	offset;
2411556Srgrimes	u_short	segment;
2421556Srgrimes    } vec16;
2431556Srgrimes    struct {
2441556Srgrimes	u_int	offset;
2451556Srgrimes	u_short	segment;
2461556Srgrimes    } vec32;
2471556Srgrimes} bioscall_vector;			/* bios jump vector */
2481556Srgrimes
2491556Srgrimesvoid
2501556Srgrimesset_bios_selectors(struct bios_segments *seg, int flags)
2511556Srgrimes{
2521556Srgrimes    struct soft_segment_descriptor ssd = {
2531556Srgrimes	0,			/* segment base address (overwritten) */
2541556Srgrimes	0,			/* length (overwritten) */
2551556Srgrimes	SDT_MEMERA,		/* segment type (overwritten) */
2561556Srgrimes	0,			/* priority level */
2571556Srgrimes	1,			/* descriptor present */
2581556Srgrimes	0, 0,
2591556Srgrimes	1,			/* descriptor size (overwritten) */
2601556Srgrimes	0			/* granularity == byte units */
2611556Srgrimes    };
2621556Srgrimes    union descriptor *p_gdt;
2631556Srgrimes
2641556Srgrimes#ifdef SMP
2651556Srgrimes    p_gdt = &gdt[cpuid];
2661556Srgrimes#else
2671556Srgrimes    p_gdt = gdt;
2681556Srgrimes#endif
2691556Srgrimes
2701556Srgrimes    ssd.ssd_base = seg->code32.base;
2711556Srgrimes    ssd.ssd_limit = seg->code32.limit;
2721556Srgrimes    ssdtosd(&ssd, &p_gdt[GBIOSCODE32_SEL].sd);
2731556Srgrimes
2741556Srgrimes    ssd.ssd_def32 = 0;
2751556Srgrimes    if (flags & BIOSCODE_FLAG) {
2761556Srgrimes	ssd.ssd_base = seg->code16.base;
2771556Srgrimes	ssd.ssd_limit = seg->code16.limit;
2781556Srgrimes	ssdtosd(&ssd, &p_gdt[GBIOSCODE16_SEL].sd);
2791556Srgrimes    }
2801556Srgrimes
2811556Srgrimes    ssd.ssd_type = SDT_MEMRWA;
2821556Srgrimes    if (flags & BIOSDATA_FLAG) {
2831556Srgrimes	ssd.ssd_base = seg->data.base;
2841556Srgrimes	ssd.ssd_limit = seg->data.limit;
2858855Srgrimes	ssdtosd(&ssd, &p_gdt[GBIOSDATA_SEL].sd);
2861556Srgrimes    }
2871556Srgrimes
2881556Srgrimes    if (flags & BIOSUTIL_FLAG) {
2891556Srgrimes	ssd.ssd_base = seg->util.base;
2901556Srgrimes	ssd.ssd_limit = seg->util.limit;
2911556Srgrimes	ssdtosd(&ssd, &p_gdt[GBIOSUTIL_SEL].sd);
2921556Srgrimes    }
2931556Srgrimes
2941556Srgrimes    if (flags & BIOSARGS_FLAG) {
295	ssd.ssd_base = seg->args.base;
296	ssd.ssd_limit = seg->args.limit;
297	ssdtosd(&ssd, &p_gdt[GBIOSARGS_SEL].sd);
298    }
299}
300
301extern int vm86pa;
302extern void bios16_jmp(void);
303
304/*
305 * this routine is really greedy with selectors, and uses 5:
306 *
307 * 32-bit code selector:	to return to kernel
308 * 16-bit code selector:	for running code
309 *        data selector:	for 16-bit data
310 *        util selector:	extra utility selector
311 *        args selector:	to handle pointers
312 *
313 * the util selector is set from the util16 entry in bios16_args, if a
314 * "U" specifier is seen.
315 *
316 * See <machine/pc/bios.h> for description of format specifiers
317 */
318int
319bios16(struct bios_args *args, char *fmt, ...)
320{
321    char	*p, *stack, *stack_top;
322    va_list 	ap;
323    int 	flags = BIOSCODE_FLAG | BIOSDATA_FLAG;
324    u_int 	i, arg_start, arg_end;
325    u_int 	*pte, *ptd;
326
327    arg_start = 0xffffffff;
328    arg_end = 0;
329
330    /*
331     * Some BIOS entrypoints attempt to copy the largest-case
332     * argument frame (in order to generalise handling for
333     * different entry types).  If our argument frame is
334     * smaller than this, the BIOS will reach off the top of
335     * our constructed stack segment.  Pad the top of the stack
336     * with some garbage to avoid this.
337     */
338    stack = (caddr_t)PAGE_SIZE - 32;
339
340    va_start(ap, fmt);
341    for (p = fmt; p && *p; p++) {
342	switch (*p) {
343	case 'p':			/* 32-bit pointer */
344	    i = va_arg(ap, u_int);
345	    arg_start = min(arg_start, i);
346	    arg_end = max(arg_end, i);
347	    flags |= BIOSARGS_FLAG;
348	    stack -= 4;
349	    break;
350
351	case 'i':			/* 32-bit integer */
352	    i = va_arg(ap, u_int);
353	    stack -= 4;
354	    break;
355
356	case 'U':			/* 16-bit selector */
357	    flags |= BIOSUTIL_FLAG;
358	    /* FALLTHROUGH */
359	case 'D':			/* 16-bit selector */
360	case 'C':			/* 16-bit selector */
361	    stack -= 2;
362	    break;
363
364	case 's':			/* 16-bit integer */
365	    i = va_arg(ap, u_short);
366	    stack -= 2;
367	    break;
368
369	default:
370	    return (EINVAL);
371	}
372    }
373
374    if (flags & BIOSARGS_FLAG) {
375	if (arg_end - arg_start > ctob(16))
376	    return (EACCES);
377	args->seg.args.base = arg_start;
378	args->seg.args.limit = 0xffff;
379    }
380
381    args->seg.code32.base = (u_int)&bios16_jmp & PG_FRAME;
382    args->seg.code32.limit = 0xffff;
383
384    ptd = (u_int *)rcr3();
385    if (ptd == IdlePTD) {
386	/*
387	 * no page table, so create one and install it.
388	 */
389	pte = (u_int *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
390	ptd = (u_int *)((u_int)ptd + KERNBASE);
391	*ptd = vtophys(pte) | PG_RW | PG_V;
392    } else {
393	/*
394	 * this is a user-level page table
395	 */
396	pte = (u_int *)&PTmap;
397    }
398    /*
399     * install pointer to page 0.  we don't need to flush the tlb,
400     * since there should not be a previous mapping for page 0.
401     */
402    *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V;
403
404    stack_top = stack;
405    va_start(ap, fmt);
406    for (p = fmt; p && *p; p++) {
407	switch (*p) {
408	case 'p':			/* 32-bit pointer */
409	    i = va_arg(ap, u_int);
410	    *(u_int *)stack = (i - arg_start) |
411		(GSEL(GBIOSARGS_SEL, SEL_KPL) << 16);
412	    stack += 4;
413	    break;
414
415	case 'i':			/* 32-bit integer */
416	    i = va_arg(ap, u_int);
417	    *(u_int *)stack = i;
418	    stack += 4;
419	    break;
420
421	case 'U':			/* 16-bit selector */
422	    *(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL);
423	    stack += 2;
424	    break;
425
426	case 'D':			/* 16-bit selector */
427	    *(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL);
428	    stack += 2;
429	    break;
430
431	case 'C':			/* 16-bit selector */
432	    *(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL);
433	    stack += 2;
434	    break;
435
436	case 's':			/* 16-bit integer */
437	    i = va_arg(ap, u_short);
438	    *(u_short *)stack = i;
439	    stack += 2;
440	    break;
441
442	default:
443	    return (EINVAL);
444	}
445    }
446
447    set_bios_selectors(&args->seg, flags);
448    bioscall_vector.vec16.offset = (u_short)args->entry;
449    bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL);
450
451    i = bios16_call(&args->r, stack_top);
452
453    if (pte == (u_int *)&PTmap) {
454	*pte = 0;			/* remove entry */
455    } else {
456	*ptd = 0;			/* remove page table */
457	free(pte, M_TEMP);		/* ... and free it */
458    }
459
460    /*
461     * XXX only needs to be invlpg(0) but that doesn't work on the 386
462     */
463    invltlb();
464
465    return (i);
466}
467
468/*
469 * PnP BIOS interface; enumerate devices only known to the system
470 * BIOS and save information about them for later use.
471 */
472
473struct pnp_sysdev
474{
475    u_int16_t	size;
476    u_int8_t	handle;
477    u_int32_t	devid;
478    u_int8_t	type[3];
479    u_int16_t	attrib;
480#define PNPATTR_NODISABLE	(1<<0)	/* can't be disabled */
481#define PNPATTR_NOCONFIG	(1<<1)	/* can't be configured */
482#define PNPATTR_OUTPUT		(1<<2)	/* can be primary output */
483#define PNPATTR_INPUT		(1<<3)	/* can be primary input */
484#define PNPATTR_BOOTABLE	(1<<4)	/* can be booted from */
485#define PNPATTR_DOCK		(1<<5)	/* is a docking station */
486#define PNPATTR_REMOVEABLE	(1<<6)	/* device is removeable */
487#define PNPATTR_CONFIG_STATIC	0x00
488#define PNPATTR_CONFIG_DYNAMIC	0x07
489#define PNPATTR_CONFIG_DYNONLY	0x17
490    /* device-specific data comes here */
491    u_int8_t	devdata[0];
492} __attribute__ ((packed));
493
494/* We have to cluster arguments within a 64k range for the bios16 call */
495struct pnp_sysdevargs
496{
497    u_int16_t	next;
498    struct pnp_sysdev node;
499};
500
501/*
502 * This function is called after the bus has assigned resource
503 * locations for a logical device.
504 */
505static void
506pnpbios_set_config(void *arg, struct isa_config *config, int enable)
507{
508}
509
510/*
511 * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
512 */
513static void
514pnpbios_identify(driver_t *driver, device_t parent)
515{
516    struct PnPBIOS_table	*pt = PnPBIOStable;
517    struct bios_args		args;
518    struct pnp_sysdev		*pd;
519    struct pnp_sysdevargs	*pda;
520    u_int16_t			ndevs, bigdev;
521    int				error, currdev;
522    u_int8_t			*devnodebuf, tag;
523    u_int32_t			*devid, *compid;
524    int				idx, left;
525    device_t			dev;
526
527    /* no PnP BIOS information */
528    if (pt == NULL)
529	return;
530
531    bzero(&args, sizeof(args));
532    args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
533    args.seg.code16.limit = 0xffff;		/* XXX ? */
534    args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
535    args.seg.data.limit = 0xffff;
536    args.entry = pt->pmentryoffset;
537
538    if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
539	printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
540    ndevs &= 0xff;				/* clear high byte garbage */
541    if (bootverbose)
542	printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
543
544    devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)),
545			M_DEVBUF, M_NOWAIT);
546    pda = (struct pnp_sysdevargs *)devnodebuf;
547    pd = &pda->node;
548
549    for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
550
551	bzero(pd, bigdev);
552	pda->next = currdev;
553	/* get current configuration */
554	if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, (u_int16_t)1))) {
555	    printf("pnpbios: error %d making BIOS16 call\n", error);
556	    break;
557	}
558	if ((error = (args.r.eax & 0xff))) {
559	    if (bootverbose)
560		printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
561	    if (error & 0x80)
562		break;
563	}
564	currdev = pda->next;
565	if (pd->size < sizeof(struct pnp_sysdev)) {
566	    printf("pnpbios: bogus system node data, aborting scan\n");
567	    break;
568	}
569
570	/*
571	 * If we are in APIC_IO mode, we should ignore the ISA PIC if it
572	 * shows up.  Likewise, in !APIC_IO mode, we should ignore the
573	 * APIC (less important).
574	 * This is significant because the ISA PIC will claim IRQ 2 (which
575	 * it uses for chaining), while in APIC mode this is a valid IRQ
576	 * available for general use.
577	 */
578#ifdef APIC_IO
579	if (pnp_eisaformat(pd->devid) == "PNP0000")	/* ISA PIC */
580	    continue;
581#else
582	if (pnp_eisaformat(pd->devid) == "PNP0003")	/* APIC */
583	    continue;
584#endif
585
586	/* Add the device and parse its resources */
587	dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
588	isa_set_vendorid(dev, pd->devid);
589	isa_set_logicalid(dev, pd->devid);
590	ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
591	pnp_parse_resources(dev, &pd->devdata[0],
592			    pd->size - sizeof(struct pnp_sysdev));
593	if (!device_get_desc(dev))
594	    device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
595
596	/* Find device IDs */
597	devid = &pd->devid;
598	compid = NULL;
599
600	/* look for a compatible device ID too */
601	left = pd->size - sizeof(struct pnp_sysdev);
602	idx = 0;
603	while (idx < left) {
604	    tag = pd->devdata[idx++];
605	    if (PNP_RES_TYPE(tag) == 0) {
606		/* Small resource */
607		switch (PNP_SRES_NUM(tag)) {
608		case PNP_TAG_COMPAT_DEVICE:
609		    compid = (u_int32_t *)(pd->devdata + idx);
610		    if (bootverbose)
611			printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
612		    /* FALLTHROUGH */
613		case PNP_TAG_END:
614		    idx = left;
615		    break;
616		default:
617		    idx += PNP_SRES_LEN(tag);
618		    break;
619		}
620	    } else
621		/* Large resource, skip it */
622		idx += *(u_int16_t *)(pd->devdata + idx) + 2;
623	}
624	if (bootverbose) {
625	    printf("pnpbios: handle %d device ID %s (%08x)",
626		   pd->handle, pnp_eisaformat(*devid), *devid);
627	    if (compid != NULL)
628		printf(" compat ID %s (%08x)",
629		       pnp_eisaformat(*compid), *compid);
630	    printf("\n");
631	}
632    }
633}
634
635static device_method_t pnpbios_methods[] = {
636	/* Device interface */
637	DEVMETHOD(device_identify,	pnpbios_identify),
638
639	{ 0, 0 }
640};
641
642static driver_t pnpbios_driver = {
643	"pnpbios",
644	pnpbios_methods,
645	1,			/* no softc */
646};
647
648static devclass_t pnpbios_devclass;
649
650DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0);
651