]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/i386/bios.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.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$
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         return (0);                     /* all OK */
170     }
171     return (1);                         /* failed */
172 }
173
174
175 /*
176  * bios_sigsearch
177  *
178  * Search some or all of the BIOS region for a signature string.
179  *
180  * (start)      Optional offset returned from this function 
181  *              (for searching for multiple matches), or NULL
182  *              to start the search from the base of the BIOS.
183  *              Note that this will be a _physical_ address in
184  *              the range 0xe0000 - 0xfffff.
185  * (sig)        is a pointer to the byte(s) of the signature.
186  * (siglen)     number of bytes in the signature.
187  * (paralen)    signature paragraph (alignment) size.
188  * (sigofs)     offset of the signature within the paragraph.
189  *
190  * Returns the _physical_ address of the found signature, 0 if the
191  * signature was not found.
192  */
193
194 u_int32_t
195 bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs)
196 {
197     u_char      *sp, *end;
198     
199     /* compute the starting address */
200     if ((start >= BIOS_START) && (start <= (BIOS_START + BIOS_SIZE))) {
201         sp = (char *)BIOS_PADDRTOVADDR(start);
202     } else if (start == 0) {
203         sp = (char *)BIOS_PADDRTOVADDR(BIOS_START);
204     } else {
205         return 0;                               /* bogus start address */
206     }
207
208     /* compute the end address */
209     end = (u_char *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_SIZE);
210
211     /* loop searching */
212     while ((sp + sigofs + siglen) < end) {
213         
214         /* compare here */
215         if (!bcmp(sp + sigofs, sig, siglen)) {
216             /* convert back to physical address */
217             return((u_int32_t)BIOS_VADDRTOPADDR(sp));
218         }
219         sp += paralen;
220     }
221     return(0);
222 }
223
224 /*
225  * do not staticize, used by bioscall.s
226  */
227 union {
228     struct {
229         u_short offset;
230         u_short segment;
231     } vec16;
232     struct {
233         u_int   offset;
234         u_short segment;
235     } vec32;
236 } bioscall_vector;                      /* bios jump vector */
237
238 void
239 set_bios_selectors(struct bios_segments *seg, int flags)
240 {
241     struct soft_segment_descriptor ssd = {
242         0,                      /* segment base address (overwritten) */
243         0,                      /* length (overwritten) */
244         SDT_MEMERA,             /* segment type (overwritten) */
245         0,                      /* priority level */
246         1,                      /* descriptor present */
247         0, 0,
248         1,                      /* descriptor size (overwritten) */
249         0                       /* granularity == byte units */
250     };
251     union descriptor *p_gdt;
252
253 #ifdef SMP
254     p_gdt = &gdt[cpuid];
255 #else
256     p_gdt = gdt;
257 #endif
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         /* Add the device and parse its resources */
562         dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
563         isa_set_vendorid(dev, pd->devid);
564         isa_set_logicalid(dev, pd->devid);
565         ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
566         pnp_parse_resources(dev, &pd->devdata[0],
567                             pd->size - sizeof(struct pnp_sysdev));
568         if (!device_get_desc(dev))
569             device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
570
571         /* Find device IDs */
572         devid = &pd->devid;
573         compid = NULL;
574
575         /* look for a compatible device ID too */
576         left = pd->size - sizeof(struct pnp_sysdev);
577         idx = 0;
578         while (idx < left) {
579             tag = pd->devdata[idx++];
580             if (PNP_RES_TYPE(tag) == 0) {
581                 /* Small resource */
582                 switch (PNP_SRES_NUM(tag)) {
583                 case PNP_TAG_COMPAT_DEVICE:
584                     compid = (u_int32_t *)(pd->devdata + idx);
585                     if (bootverbose)
586                         printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
587                     /* FALLTHROUGH */
588                 case PNP_TAG_END:
589                     idx = left;
590                     break;
591                 default:
592                     idx += PNP_SRES_LEN(tag);
593                     break;
594                 }
595             } else
596                 /* Large resource, skip it */
597                 idx += *(u_int16_t *)(pd->devdata + idx) + 2;
598         }
599         if (bootverbose) {
600             printf("pnpbios: handle %d device ID %s (%08x)", 
601                    pd->handle, pnp_eisaformat(*devid), *devid);
602             if (compid != NULL)
603                 printf(" compat ID %s (%08x)",
604                        pnp_eisaformat(*compid), *compid);
605             printf("\n");
606         }
607     }
608 }
609
610 static device_method_t pnpbios_methods[] = {
611         /* Device interface */
612         DEVMETHOD(device_identify,      pnpbios_identify),
613
614         { 0, 0 }
615 };
616
617 static driver_t pnpbios_driver = {
618         "pnpbios",
619         pnpbios_methods,
620         1,                      /* no softc */
621 };
622
623 static devclass_t pnpbios_devclass;
624
625 DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0);
626
627 #endif /* PNPBIOS */