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