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