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