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