Merge branch 'vendor/GCC47'
[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.16 2007/11/25 00:13:28 swildner Exp $
29  */
30
31 /*
32  * Code for dealing with the BIOS in x86 PC systems.
33  */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <vm/vm.h>
39 #include <vm/pmap.h>
40 #include <machine/md_var.h>
41 #include <machine/vmparam.h>
42 #include <machine/globaldata.h>
43 #include <machine/pc/bios.h>
44
45 #define BIOS_START      0xe0000
46 #define BIOS_SIZE       0x20000
47
48 /* exported lookup results */
49 struct bios32_SDentry           PCIbios;
50 struct PnPBIOS_table            *PnPBIOStable;
51
52 static u_int                    bios32_SDCI;
53
54 /* start fairly early */
55 static void                     bios32_init(void *junk);
56 SYSINIT(bios32, SI_BOOT2_BIOS, SI_ORDER_ANY, bios32_init, NULL);
57
58 /*
59  * bios32_init
60  *
61  * Locate various bios32 entities.
62  */
63 static void
64 bios32_init(void *junk)
65 {
66     u_long                      sigaddr;
67     struct bios32_SDheader      *sdh;
68     struct PnPBIOS_table        *pt;
69     uint8_t                     ck, *cv;
70     int                         i;
71     char                        *p;
72     
73     /*
74      * BIOS32 Service Directory, PCI BIOS
75      */
76     
77     /* look for the signature */
78     if ((sigaddr = bios_sigsearch(0, "_32_", 4, 16, 0)) != 0) {
79
80         /* get a virtual pointer to the structure */
81         sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
82         for (cv = (uint8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) {
83             ck += cv[i];
84         }
85         /* If checksum is OK, enable use of the entrypoint */
86         if ((ck == 0) && BIOS_START <= sdh->entry &&
87             (sdh->entry < (BIOS_START + BIOS_SIZE))
88         ) {
89             bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry);
90             if (bootverbose) {
91                 kprintf("bios32: Found BIOS32 Service Directory header at %p\n", sdh);
92                 kprintf("bios32: Entry = 0x%x (%x)  Rev = %d  Len = %d\n", 
93                        sdh->entry, bios32_SDCI, sdh->revision, sdh->len);
94             }
95
96             /*  Allow user override of PCI BIOS search */
97             if (((p = kgetenv("machdep.bios.pci")) == NULL) || strcmp(p, "disable")) {
98                 /* See if there's a PCI BIOS entrypoint here */
99                 PCIbios.ident.id = 0x49435024;  /* PCI systems should have this */
100                 if (!bios32_SDlookup(&PCIbios) && bootverbose)
101                     kprintf("pcibios: PCI BIOS entry at 0x%x\n", PCIbios.entry);
102             }
103             if (p != NULL)
104                 kfreeenv(p);
105         } else {
106             kprintf("bios32: Bad BIOS32 Service Directory\n");
107         }
108     }
109
110     /*
111      * PnP BIOS
112      *
113      * Allow user override of PnP BIOS search
114      */
115     if (((p = kgetenv("machdep.bios.pnp")) == NULL || strcmp(p, "disable")) &&
116         (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 = (uint8_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                 kprintf("pnpbios: Found PnP BIOS data at %p\n", pt);
128                 kprintf("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                     kprintf("pnpbios: Event flag at %x\n", pt->evflagaddr);
132                 if (pt->oemdevid != 0)
133                     kprintf("pnpbios: OEM ID %x\n", pt->oemdevid);
134                 
135             }
136         } else {
137             kprintf("pnpbios: Bad PnP BIOS data checksum\n");
138         }
139     }
140     if (p != NULL)
141         kfreeenv(p);
142     if (bootverbose) {
143             /* look for other know signatures */
144             kprintf("Other BIOS signatures found:\n");
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 uint32_t
197 bios_sigsearch(uint32_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((uint32_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         uint16_t offset;
232         uint16_t segment;
233     } vec16;
234     struct {
235         uint32_t offset;
236         uint16_t 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     p_gdt = &gdt[mycpu->gd_cpuid * NGDT];
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     pt_entry_t  *pte;
313     pd_entry_t  *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 passed as an int */
353             i = __va_arg(ap, int);
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 = (pd_entry_t *)rcr3();
373     if ((pd_entry_t)ptd == IdlePTD) {
374         /*
375          * no page table, so create one and install it.
376          */
377         pte = kmalloc(PAGE_SIZE, M_TEMP, M_WAITOK|M_ZERO);
378         ptd = (pd_entry_t *)((vm_offset_t)ptd + KERNBASE);
379         *ptd = vtophys(pte) | PG_RW | PG_V;
380     } else {
381         /*
382          * this is a user-level page table 
383          */
384         pte = PTmap;
385     }
386     /*
387      * install pointer to page 0.  Flush the tlb for safety.  We don't
388      * migrate between cpus so a local flush is sufficient.
389      */
390     *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V; 
391     cpu_invltlb();
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 passed as an int */
426             i = __va_arg(ap, int);
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 == PTmap) {
443         *pte = 0;                       /* remove entry */
444     } else {
445         *ptd = 0;                       /* remove page table */
446         kfree(pte, M_TEMP);             /* ... and free it */
447     }
448     /*
449      * XXX only needs to be invlpg(0) but that doesn't work on the 386 
450      */
451     cpu_invltlb();
452     return (i);
453 }
454
455 int
456 bios_oem_strings(struct bios_oem *oem, u_char *buffer, size_t maxlen)
457 {
458         size_t idx = 0;
459         struct bios_oem_signature *sig;
460         u_int from, to;
461         u_char c, *s, *se, *str, *bios_str;
462         size_t i, off, len, tot;
463
464         if ( !oem || !buffer || maxlen<2 )
465                 return(-1);
466
467         sig = oem->signature;
468         if (!sig)
469                 return(-2);
470
471         from = oem->range.from;
472         to = oem->range.to;
473         if ( (to<=from) || (from<BIOS_START) || (to>(BIOS_START+BIOS_SIZE)) )
474                 return(-3);
475
476         while (sig->anchor != NULL) {
477                 str = sig->anchor;
478                 len = strlen(str);
479                 off = sig->offset;
480                 tot = sig->totlen;
481                 /* make sure offset doesn't go beyond bios area */
482                 if ( (to+off)>(BIOS_START+BIOS_SIZE) ||
483                                         ((from+off)<BIOS_START) ) {
484                         kprintf("sys/i386/i386/bios.c: sig '%s' "
485                                 "from 0x%0x to 0x%0x offset %d "
486                                 "out of BIOS bounds 0x%0x - 0x%0x\n",
487                                 str, from, to, off,
488                                 BIOS_START, BIOS_START+BIOS_SIZE);
489                         return(-4);
490                 }
491                 /* make sure we don't overrun return buffer */
492                 if (idx + tot > maxlen - 1) {
493                         kprintf("sys/i386/i386/bios.c: sig '%s' "
494                                 "idx %d + tot %d = %d > maxlen-1 %d\n",
495                                 str, idx, tot, idx+tot, maxlen-1);
496                         return(-5);
497                 }
498                 bios_str = NULL;
499                 s = (u_char *)BIOS_PADDRTOVADDR(from);
500                 se = (u_char *)BIOS_PADDRTOVADDR(to-len);
501                 for (; s<se; s++) {
502                         if (!bcmp(str, s, len)) {
503                                 bios_str = s;
504                                 break;
505                         }
506                 }
507                 /*
508                 *  store pretty version of totlen bytes of bios string with
509                 *  given offset; 0x20 - 0x7E are printable; uniquify spaces
510                 */
511                 if (bios_str) {
512                         for (i=0; i<tot; i++) {
513                                 c = bios_str[i+off];
514                                 if ( (c < 0x20) || (c > 0x7E) )
515                                         c = ' ';
516                                 if (idx == 0) {
517                                         if (c != ' ')
518                                                 buffer[idx++] = c;
519                                 } else if ( (c != ' ') ||
520                                         ((c == ' ') && (buffer[idx-1] != ' ')) )
521                                                 buffer[idx++] = c;
522                         }
523                 }
524                 sig++;
525         }
526         /* remove a final trailing space */
527         if ( (idx > 1) && (buffer[idx-1] == ' ') )
528                 idx--;
529         buffer[idx] = '\0';
530         return (idx);
531 }