bios.c revision 115683
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 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/amd64/amd64/bios.c 115683 2003-06-02 06:43:15Z obrien $");
31
32/*
33 * Code for dealing with the BIOS in x86 PC systems.
34 */
35
36#include "opt_isa.h"
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/kernel.h>
41#include <sys/malloc.h>
42#include <sys/bus.h>
43#include <sys/pcpu.h>
44#include <vm/vm.h>
45#include <vm/pmap.h>
46#include <machine/md_var.h>
47#include <machine/segments.h>
48#include <machine/stdarg.h>
49#include <machine/vmparam.h>
50#include <machine/pc/bios.h>
51#ifdef DEV_ISA
52#include <isa/isavar.h>
53#include <isa/pnpreg.h>
54#include <isa/pnpvar.h>
55#endif
56
57#define BIOS_START	0xe0000
58#define BIOS_SIZE	0x20000
59
60/* exported lookup results */
61struct bios32_SDentry		PCIbios;
62struct PnPBIOS_table		*PnPBIOStable;
63
64static u_int			bios32_SDCI;
65
66/* start fairly early */
67static void			bios32_init(void *junk);
68SYSINIT(bios32, SI_SUB_CPU, SI_ORDER_ANY, bios32_init, NULL);
69
70/*
71 * bios32_init
72 *
73 * Locate various bios32 entities.
74 */
75static void
76bios32_init(void *junk)
77{
78    u_long			sigaddr;
79    struct bios32_SDheader	*sdh;
80    struct PnPBIOS_table	*pt;
81    u_int8_t			ck, *cv;
82    int				i;
83    char			*p;
84
85    /*
86     * BIOS32 Service Directory, PCI BIOS
87     */
88
89    /* look for the signature */
90    if ((sigaddr = bios_sigsearch(0, "_32_", 4, 16, 0)) != 0) {
91
92	/* get a virtual pointer to the structure */
93	sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
94	for (cv = (u_int8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) {
95	    ck += cv[i];
96	}
97	/* If checksum is OK, enable use of the entrypoint */
98	if ((ck == 0) && (BIOS_START <= sdh->entry ) &&
99	    (sdh->entry < (BIOS_START + BIOS_SIZE))) {
100	    bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry);
101	    if (bootverbose) {
102		printf("bios32: Found BIOS32 Service Directory header at %p\n", sdh);
103		printf("bios32: Entry = 0x%x (%x)  Rev = %d  Len = %d\n",
104		       sdh->entry, bios32_SDCI, sdh->revision, sdh->len);
105	    }
106
107	    /* Allow user override of PCI BIOS search */
108	    if (((p = getenv("machdep.bios.pci")) == NULL) || strcmp(p, "disable")) {
109
110		/* See if there's a PCI BIOS entrypoint here */
111		PCIbios.ident.id = 0x49435024;	/* PCI systems should have this */
112		if (!bios32_SDlookup(&PCIbios) && bootverbose)
113		    printf("pcibios: PCI BIOS entry at 0x%x+0x%x\n", PCIbios.base, PCIbios.entry);
114	    }
115	    if (p != NULL)
116		    freeenv(p);
117	} else {
118	    printf("bios32: Bad BIOS32 Service Directory\n");
119	}
120    }
121
122    /*
123     * PnP BIOS
124     *
125     * Allow user override of PnP BIOS search
126     */
127    if ((((p = getenv("machdep.bios.pnp")) == NULL) || strcmp(p, "disable")) &&
128	((sigaddr = bios_sigsearch(0, "$PnP", 4, 16, 0)) != 0)) {
129
130	/* get a virtual pointer to the structure */
131	pt = (struct PnPBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
132	for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < pt->len; i++) {
133	    ck += cv[i];
134	}
135	/* If checksum is OK, enable use of the entrypoint */
136	if (ck == 0) {
137	    PnPBIOStable = pt;
138	    if (bootverbose) {
139		printf("pnpbios: Found PnP BIOS data at %p\n", pt);
140		printf("pnpbios: Entry = %x:%x  Rev = %d.%d\n",
141		       pt->pmentrybase, pt->pmentryoffset, pt->version >> 4, pt->version & 0xf);
142		if ((pt->control & 0x3) == 0x01)
143		    printf("pnpbios: Event flag at %x\n", pt->evflagaddr);
144		if (pt->oemdevid != 0)
145		    printf("pnpbios: OEM ID %x\n", pt->oemdevid);
146
147	    }
148	} else {
149	    printf("pnpbios: Bad PnP BIOS data checksum\n");
150	}
151    }
152    if (p != NULL)
153	    freeenv(p);
154    if (bootverbose) {
155	    /* look for other know signatures */
156	    printf("Other BIOS signatures found:\n");
157    }
158}
159
160/*
161 * bios32_SDlookup
162 *
163 * Query the BIOS32 Service Directory for the service named in (ent),
164 * returns nonzero if the lookup fails.  The caller must fill in
165 * (ent->ident), the remainder are populated on a successful lookup.
166 */
167int
168bios32_SDlookup(struct bios32_SDentry *ent)
169{
170    struct bios_regs args;
171
172    if (bios32_SDCI == 0)
173	return (1);
174
175    args.eax = ent->ident.id;		/* set up arguments */
176    args.ebx = args.ecx = args.edx = 0;
177    bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL));
178    if ((args.eax & 0xff) == 0) {	/* success? */
179	ent->base = args.ebx;
180	ent->len = args.ecx;
181	ent->entry = args.edx;
182	ent->ventry = BIOS_PADDRTOVADDR(ent->base + ent->entry);
183	return (0);			/* all OK */
184    }
185    return (1);				/* failed */
186}
187
188
189/*
190 * bios_sigsearch
191 *
192 * Search some or all of the BIOS region for a signature string.
193 *
194 * (start)	Optional offset returned from this function
195 *		(for searching for multiple matches), or NULL
196 *		to start the search from the base of the BIOS.
197 *		Note that this will be a _physical_ address in
198 *		the range 0xe0000 - 0xfffff.
199 * (sig)	is a pointer to the byte(s) of the signature.
200 * (siglen)	number of bytes in the signature.
201 * (paralen)	signature paragraph (alignment) size.
202 * (sigofs)	offset of the signature within the paragraph.
203 *
204 * Returns the _physical_ address of the found signature, 0 if the
205 * signature was not found.
206 */
207
208u_int32_t
209bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs)
210{
211    u_char	*sp, *end;
212
213    /* compute the starting address */
214    if ((start >= BIOS_START) && (start <= (BIOS_START + BIOS_SIZE))) {
215	sp = (char *)BIOS_PADDRTOVADDR(start);
216    } else if (start == 0) {
217	sp = (char *)BIOS_PADDRTOVADDR(BIOS_START);
218    } else {
219	return 0;				/* bogus start address */
220    }
221
222    /* compute the end address */
223    end = (u_char *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_SIZE);
224
225    /* loop searching */
226    while ((sp + sigofs + siglen) < end) {
227
228	/* compare here */
229	if (!bcmp(sp + sigofs, sig, siglen)) {
230	    /* convert back to physical address */
231	    return((u_int32_t)BIOS_VADDRTOPADDR(sp));
232	}
233	sp += paralen;
234    }
235    return(0);
236}
237
238/*
239 * do not staticize, used by bioscall.s
240 */
241union {
242    struct {
243	u_short	offset;
244	u_short	segment;
245    } vec16;
246    struct {
247	u_int	offset;
248	u_short	segment;
249    } vec32;
250} bioscall_vector;			/* bios jump vector */
251
252void
253set_bios_selectors(struct bios_segments *seg, int flags)
254{
255    struct soft_segment_descriptor ssd = {
256	0,			/* segment base address (overwritten) */
257	0,			/* length (overwritten) */
258	SDT_MEMERA,		/* segment type (overwritten) */
259	0,			/* priority level */
260	1,			/* descriptor present */
261	0, 0,
262	1,			/* descriptor size (overwritten) */
263	0			/* granularity == byte units */
264    };
265    union descriptor *p_gdt;
266
267#ifdef SMP
268    p_gdt = &gdt[PCPU_GET(cpuid) * NGDT];
269#else
270    p_gdt = gdt;
271#endif
272
273    ssd.ssd_base = seg->code32.base;
274    ssd.ssd_limit = seg->code32.limit;
275    ssdtosd(&ssd, &p_gdt[GBIOSCODE32_SEL].sd);
276
277    ssd.ssd_def32 = 0;
278    if (flags & BIOSCODE_FLAG) {
279	ssd.ssd_base = seg->code16.base;
280	ssd.ssd_limit = seg->code16.limit;
281	ssdtosd(&ssd, &p_gdt[GBIOSCODE16_SEL].sd);
282    }
283
284    ssd.ssd_type = SDT_MEMRWA;
285    if (flags & BIOSDATA_FLAG) {
286	ssd.ssd_base = seg->data.base;
287	ssd.ssd_limit = seg->data.limit;
288	ssdtosd(&ssd, &p_gdt[GBIOSDATA_SEL].sd);
289    }
290
291    if (flags & BIOSUTIL_FLAG) {
292	ssd.ssd_base = seg->util.base;
293	ssd.ssd_limit = seg->util.limit;
294	ssdtosd(&ssd, &p_gdt[GBIOSUTIL_SEL].sd);
295    }
296
297    if (flags & BIOSARGS_FLAG) {
298	ssd.ssd_base = seg->args.base;
299	ssd.ssd_limit = seg->args.limit;
300	ssdtosd(&ssd, &p_gdt[GBIOSARGS_SEL].sd);
301    }
302}
303
304extern int vm86pa;
305extern void bios16_jmp(void);
306
307/*
308 * this routine is really greedy with selectors, and uses 5:
309 *
310 * 32-bit code selector:	to return to kernel
311 * 16-bit code selector:	for running code
312 *        data selector:	for 16-bit data
313 *        util selector:	extra utility selector
314 *        args selector:	to handle pointers
315 *
316 * the util selector is set from the util16 entry in bios16_args, if a
317 * "U" specifier is seen.
318 *
319 * See <machine/pc/bios.h> for description of format specifiers
320 */
321int
322bios16(struct bios_args *args, char *fmt, ...)
323{
324    char	*p, *stack, *stack_top;
325    va_list 	ap;
326    int 	flags = BIOSCODE_FLAG | BIOSDATA_FLAG;
327    u_int 	i, arg_start, arg_end;
328    pt_entry_t	*pte;
329    pd_entry_t	*ptd;
330
331    arg_start = 0xffffffff;
332    arg_end = 0;
333
334    /*
335     * Some BIOS entrypoints attempt to copy the largest-case
336     * argument frame (in order to generalise handling for
337     * different entry types).  If our argument frame is
338     * smaller than this, the BIOS will reach off the top of
339     * our constructed stack segment.  Pad the top of the stack
340     * with some garbage to avoid this.
341     */
342    stack = (caddr_t)PAGE_SIZE - 32;
343
344    va_start(ap, fmt);
345    for (p = fmt; p && *p; p++) {
346	switch (*p) {
347	case 'p':			/* 32-bit pointer */
348	    i = va_arg(ap, u_int);
349	    arg_start = min(arg_start, i);
350	    arg_end = max(arg_end, i);
351	    flags |= BIOSARGS_FLAG;
352	    stack -= 4;
353	    break;
354
355	case 'i':			/* 32-bit integer */
356	    i = va_arg(ap, u_int);
357	    stack -= 4;
358	    break;
359
360	case 'U':			/* 16-bit selector */
361	    flags |= BIOSUTIL_FLAG;
362	    /* FALLTHROUGH */
363	case 'D':			/* 16-bit selector */
364	case 'C':			/* 16-bit selector */
365	    stack -= 2;
366	    break;
367
368	case 's':			/* 16-bit integer passed as an int */
369	    i = va_arg(ap, int);
370	    stack -= 2;
371	    break;
372
373	default:
374	    return (EINVAL);
375	}
376    }
377
378    if (flags & BIOSARGS_FLAG) {
379	if (arg_end - arg_start > ctob(16))
380	    return (EACCES);
381	args->seg.args.base = arg_start;
382	args->seg.args.limit = 0xffff;
383    }
384
385    args->seg.code32.base = (u_int)&bios16_jmp & PG_FRAME;
386    args->seg.code32.limit = 0xffff;
387
388    ptd = (pd_entry_t *)rcr3();
389#ifdef PAE
390    if (ptd == IdlePDPT)
391#else
392    if (ptd == IdlePTD)
393#endif
394    {
395	/*
396	 * no page table, so create one and install it.
397	 */
398	pte = (pt_entry_t *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
399	ptd = (pd_entry_t *)((u_int)IdlePTD + KERNBASE);
400	*ptd = vtophys(pte) | PG_RW | PG_V;
401    } else {
402	/*
403	 * this is a user-level page table
404	 */
405	pte = PTmap;
406    }
407    /*
408     * install pointer to page 0.  we don't need to flush the tlb,
409     * since there should not be a previous mapping for page 0.
410     */
411    *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V;
412
413    stack_top = stack;
414    va_start(ap, fmt);
415    for (p = fmt; p && *p; p++) {
416	switch (*p) {
417	case 'p':			/* 32-bit pointer */
418	    i = va_arg(ap, u_int);
419	    *(u_int *)stack = (i - arg_start) |
420		(GSEL(GBIOSARGS_SEL, SEL_KPL) << 16);
421	    stack += 4;
422	    break;
423
424	case 'i':			/* 32-bit integer */
425	    i = va_arg(ap, u_int);
426	    *(u_int *)stack = i;
427	    stack += 4;
428	    break;
429
430	case 'U':			/* 16-bit selector */
431	    *(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL);
432	    stack += 2;
433	    break;
434
435	case 'D':			/* 16-bit selector */
436	    *(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL);
437	    stack += 2;
438	    break;
439
440	case 'C':			/* 16-bit selector */
441	    *(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL);
442	    stack += 2;
443	    break;
444
445	case 's':			/* 16-bit integer passed as an int */
446	    i = va_arg(ap, int);
447	    *(u_short *)stack = i;
448	    stack += 2;
449	    break;
450
451	default:
452	    return (EINVAL);
453	}
454    }
455
456    set_bios_selectors(&args->seg, flags);
457    bioscall_vector.vec16.offset = (u_short)args->entry;
458    bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL);
459
460    i = bios16_call(&args->r, stack_top);
461
462    if (pte == PTmap) {
463	*pte = 0;			/* remove entry */
464    } else {
465	*ptd = 0;			/* remove page table */
466	free(pte, M_TEMP);		/* ... and free it */
467    }
468
469    /*
470     * XXX only needs to be invlpg(0) but that doesn't work on the 386
471     */
472    pmap_invalidate_all(kernel_pmap);
473
474    return (i);
475}
476
477#ifdef DEV_ISA
478/*
479 * PnP BIOS interface; enumerate devices only known to the system
480 * BIOS and save information about them for later use.
481 */
482
483struct pnp_sysdev
484{
485    u_int16_t	size;
486    u_int8_t	handle;
487    u_int32_t	devid;
488    u_int8_t	type[3];
489    u_int16_t	attrib;
490#define PNPATTR_NODISABLE	(1<<0)	/* can't be disabled */
491#define PNPATTR_NOCONFIG	(1<<1)	/* can't be configured */
492#define PNPATTR_OUTPUT		(1<<2)	/* can be primary output */
493#define PNPATTR_INPUT		(1<<3)	/* can be primary input */
494#define PNPATTR_BOOTABLE	(1<<4)	/* can be booted from */
495#define PNPATTR_DOCK		(1<<5)	/* is a docking station */
496#define PNPATTR_REMOVEABLE	(1<<6)	/* device is removeable */
497#define PNPATTR_CONFIG_STATIC	(0)
498#define PNPATTR_CONFIG_DYNAMIC	(1)
499#define PNPATTR_CONFIG_DYNONLY	(3)
500#define PNPATTR_CONFIG(a)	(((a) >> 7) & 0x3)
501    /* device-specific data comes here */
502    u_int8_t	devdata[0];
503} __packed;
504
505/* We have to cluster arguments within a 64k range for the bios16 call */
506struct pnp_sysdevargs
507{
508    u_int16_t	next;
509    struct pnp_sysdev node;
510};
511
512/*
513 * This function is called after the bus has assigned resource
514 * locations for a logical device.
515 */
516static void
517pnpbios_set_config(void *arg, struct isa_config *config, int enable)
518{
519}
520
521/*
522 * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
523 */
524static void
525pnpbios_identify(driver_t *driver, device_t parent)
526{
527    struct PnPBIOS_table	*pt = PnPBIOStable;
528    struct bios_args		args;
529    struct pnp_sysdev		*pd;
530    struct pnp_sysdevargs	*pda;
531    u_int16_t			ndevs, bigdev;
532    int				error, currdev;
533    u_int8_t			*devnodebuf, tag;
534    u_int32_t			*devid, *compid;
535    int				idx, left;
536    device_t			dev;
537
538    /* no PnP BIOS information */
539    if (pt == NULL)
540	return;
541
542    /* ACPI already active */
543    if (devclass_get_softc(devclass_find("ACPI"), 0) != NULL)
544	return;
545
546    /* get count of PnP devices */
547    bzero(&args, sizeof(args));
548    args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
549    args.seg.code16.limit = 0xffff;		/* XXX ? */
550    args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
551    args.seg.data.limit = 0xffff;
552    args.entry = pt->pmentryoffset;
553
554    if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
555	printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
556    ndevs &= 0xff;				/* clear high byte garbage */
557    if (bootverbose)
558	printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
559
560    devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)),
561			M_DEVBUF, M_NOWAIT);
562    pda = (struct pnp_sysdevargs *)devnodebuf;
563    pd = &pda->node;
564
565    for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
566
567	bzero(pd, bigdev);
568	pda->next = currdev;
569	/* get current configuration */
570	if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) {
571	    printf("pnpbios: error %d making BIOS16 call\n", error);
572	    break;
573	}
574	if ((error = (args.r.eax & 0xff))) {
575	    if (bootverbose)
576		printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
577	    if (error & 0x80)
578		break;
579	}
580	currdev = pda->next;
581	if (pd->size < sizeof(struct pnp_sysdev)) {
582	    printf("pnpbios: bogus system node data, aborting scan\n");
583	    break;
584	}
585
586	/*
587	 * If we are in APIC_IO mode, we should ignore the ISA PIC if it
588	 * shows up.  Likewise, in !APIC_IO mode, we should ignore the
589	 * APIC (less important).
590	 * This is significant because the ISA PIC will claim IRQ 2 (which
591	 * it uses for chaining), while in APIC mode this is a valid IRQ
592	 * available for general use.
593	 */
594#ifdef APIC_IO
595	if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000"))	/* ISA PIC */
596	    continue;
597#else
598	if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003"))	/* APIC */
599	    continue;
600#endif
601
602	/* Add the device and parse its resources */
603	dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
604	isa_set_vendorid(dev, pd->devid);
605	isa_set_logicalid(dev, pd->devid);
606	/*
607	 * It appears that some PnP BIOS doesn't allow us to re-enable
608	 * the embedded system device once it is disabled.  We shall
609	 * mark all system device nodes as "cannot be disabled", regardless
610	 * of actual settings in the device attribute byte.
611	 * XXX
612	isa_set_configattr(dev,
613	    ((pd->attrib & PNPATTR_NODISABLE) ?  0 : ISACFGATTR_CANDISABLE) |
614	    ((!(pd->attrib & PNPATTR_NOCONFIG) &&
615		PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
616		? ISACFGATTR_DYNAMIC : 0));
617	 */
618	isa_set_configattr(dev,
619	    (!(pd->attrib & PNPATTR_NOCONFIG) &&
620		PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
621		? ISACFGATTR_DYNAMIC : 0);
622	ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
623	pnp_parse_resources(dev, &pd->devdata[0],
624			    pd->size - sizeof(struct pnp_sysdev), 0);
625	if (!device_get_desc(dev))
626	    device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
627
628	/* Find device IDs */
629	devid = &pd->devid;
630	compid = NULL;
631
632	/* look for a compatible device ID too */
633	left = pd->size - sizeof(struct pnp_sysdev);
634	idx = 0;
635	while (idx < left) {
636	    tag = pd->devdata[idx++];
637	    if (PNP_RES_TYPE(tag) == 0) {
638		/* Small resource */
639		switch (PNP_SRES_NUM(tag)) {
640		case PNP_TAG_COMPAT_DEVICE:
641		    compid = (u_int32_t *)(pd->devdata + idx);
642		    if (bootverbose)
643			printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
644		    /* FALLTHROUGH */
645		case PNP_TAG_END:
646		    idx = left;
647		    break;
648		default:
649		    idx += PNP_SRES_LEN(tag);
650		    break;
651		}
652	    } else
653		/* Large resource, skip it */
654		idx += *(u_int16_t *)(pd->devdata + idx) + 2;
655	}
656	if (bootverbose) {
657	    printf("pnpbios: handle %d device ID %s (%08x)",
658		   pd->handle, pnp_eisaformat(*devid), *devid);
659	    if (compid != NULL)
660		printf(" compat ID %s (%08x)",
661		       pnp_eisaformat(*compid), *compid);
662	    printf("\n");
663	}
664    }
665}
666
667static device_method_t pnpbios_methods[] = {
668	/* Device interface */
669	DEVMETHOD(device_identify,	pnpbios_identify),
670
671	{ 0, 0 }
672};
673
674static driver_t pnpbios_driver = {
675	"pnpbios",
676	pnpbios_methods,
677	1,			/* no softc */
678};
679
680static devclass_t pnpbios_devclass;
681
682DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0);
683#endif /* DEV_ISA */
684