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