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