Remove i386 support.
[dragonfly.git] / sys / platform / pc32 / i386 / bios.c
CommitLineData
984263bc
MD
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 $
ba39e2e0 28 * $DragonFly: src/sys/platform/pc32/i386/bios.c,v 1.15 2007/04/30 07:18:55 dillon Exp $
984263bc
MD
29 */
30
31/*
32 * Code for dealing with the BIOS in x86 PC systems.
33 */
34
984263bc
MD
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
984263bc
MD
38#include <vm/vm.h>
39#include <vm/pmap.h>
40#include <machine/md_var.h>
c439ad8f 41#include <machine/vmparam.h>
85100692 42#include <machine/globaldata.h>
984263bc 43#include <machine/pc/bios.h>
984263bc
MD
44
45#define BIOS_START 0xe0000
46#define BIOS_SIZE 0x20000
47
48/* exported lookup results */
6c6180e0
MD
49struct bios32_SDentry PCIbios;
50struct PnPBIOS_table *PnPBIOStable;
984263bc 51
6c6180e0 52static u_int bios32_SDCI;
984263bc
MD
53
54/* start fairly early */
55static void bios32_init(void *junk);
ba39e2e0 56SYSINIT(bios32, SI_BOOT2_BIOS, SI_ORDER_ANY, bios32_init, NULL);
984263bc
MD
57
58/*
59 * bios32_init
60 *
61 * Locate various bios32 entities.
62 */
63static void
64bios32_init(void *junk)
65{
66 u_long sigaddr;
67 struct bios32_SDheader *sdh;
68 struct PnPBIOS_table *pt;
0e1cb2e2 69 uint8_t ck, *cv;
984263bc 70 int i;
6c6180e0 71 char *p;
984263bc
MD
72
73 /*
6c6180e0 74 * BIOS32 Service Directory, PCI BIOS
984263bc
MD
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);
0e1cb2e2 82 for (cv = (uint8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) {
984263bc
MD
83 ck += cv[i];
84 }
85 /* If checksum is OK, enable use of the entrypoint */
6c6180e0
MD
86 if ((ck == 0) && BIOS_START <= sdh->entry &&
87 (sdh->entry < (BIOS_START + BIOS_SIZE))
88 ) {
984263bc
MD
89 bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry);
90 if (bootverbose) {
26be20a0
SW
91 kprintf("bios32: Found BIOS32 Service Directory header at %p\n", sdh);
92 kprintf("bios32: Entry = 0x%x (%x) Rev = %d Len = %d\n",
984263bc
MD
93 sdh->entry, bios32_SDCI, sdh->revision, sdh->len);
94 }
6c6180e0
MD
95
96 /* Allow user override of PCI BIOS search */
bc01a404 97 if (((p = kgetenv("machdep.bios.pci")) == NULL) || strcmp(p, "disable")) {
6c6180e0
MD
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)
26be20a0 101 kprintf("pcibios: PCI BIOS entry at 0x%x\n", PCIbios.entry);
6c6180e0
MD
102 }
103 if (p != NULL)
104 freeenv(p);
984263bc 105 } else {
26be20a0 106 kprintf("bios32: Bad BIOS32 Service Directory\n");
984263bc
MD
107 }
108 }
109
110 /*
111 * PnP BIOS
6c6180e0
MD
112 *
113 * Allow user override of PnP BIOS search
984263bc 114 */
bc01a404 115 if (((p = kgetenv("machdep.bios.pnp")) == NULL || strcmp(p, "disable")) &&
6c6180e0
MD
116 (sigaddr = bios_sigsearch(0, "$PnP", 4, 16, 0)) != 0
117 ) {
984263bc
MD
118 /* get a virtual pointer to the structure */
119 pt = (struct PnPBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
0e1cb2e2 120 for (cv = (uint8_t *)pt, ck = 0, i = 0; i < pt->len; i++) {
984263bc
MD
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) {
26be20a0
SW
127 kprintf("pnpbios: Found PnP BIOS data at %p\n", pt);
128 kprintf("pnpbios: Entry = %x:%x Rev = %d.%d\n",
984263bc
MD
129 pt->pmentrybase, pt->pmentryoffset, pt->version >> 4, pt->version & 0xf);
130 if ((pt->control & 0x3) == 0x01)
26be20a0 131 kprintf("pnpbios: Event flag at %x\n", pt->evflagaddr);
984263bc 132 if (pt->oemdevid != 0)
26be20a0 133 kprintf("pnpbios: OEM ID %x\n", pt->oemdevid);
984263bc
MD
134
135 }
136 } else {
26be20a0 137 kprintf("pnpbios: Bad PnP BIOS data checksum\n");
984263bc
MD
138 }
139 }
6c6180e0
MD
140 if (p != NULL)
141 freeenv(p);
984263bc
MD
142 if (bootverbose) {
143 /* look for other know signatures */
26be20a0 144 kprintf("Other BIOS signatures found:\n");
984263bc
MD
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 */
155int
156bios32_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
0e1cb2e2
JS
196uint32_t
197bios_sigsearch(uint32_t start, u_char *sig, int siglen, int paralen, int sigofs)
984263bc
MD
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 */
0e1cb2e2 219 return((uint32_t)BIOS_VADDRTOPADDR(sp));
984263bc
MD
220 }
221 sp += paralen;
222 }
223 return(0);
224}
225
226/*
227 * do not staticize, used by bioscall.s
228 */
229union {
230 struct {
0e1cb2e2
JS
231 uint16_t offset;
232 uint16_t segment;
984263bc
MD
233 } vec16;
234 struct {
0e1cb2e2
JS
235 uint32_t offset;
236 uint16_t segment;
984263bc
MD
237 } vec32;
238} bioscall_vector; /* bios jump vector */
239
240void
241set_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
17a9f566 255 p_gdt = &gdt[mycpu->gd_cpuid * NGDT];
6c6180e0 256
984263bc
MD
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
288extern int vm86pa;
289extern 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 */
305int
306bios16(struct bios_args *args, char *fmt, ...)
307{
308 char *p, *stack, *stack_top;
e2565a42 309 __va_list ap;
984263bc
MD
310 int flags = BIOSCODE_FLAG | BIOSDATA_FLAG;
311 u_int i, arg_start, arg_end;
6c6180e0
MD
312 pt_entry_t *pte;
313 pd_entry_t *ptd;
984263bc
MD
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
e2565a42 328 __va_start(ap, fmt);
984263bc
MD
329 for (p = fmt; p && *p; p++) {
330 switch (*p) {
331 case 'p': /* 32-bit pointer */
e2565a42 332 i = __va_arg(ap, u_int);
984263bc
MD
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 */
e2565a42 340 i = __va_arg(ap, u_int);
984263bc
MD
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
6c6180e0 352 case 's': /* 16-bit integer passed as an int */
e2565a42 353 i = __va_arg(ap, int);
984263bc
MD
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
6c6180e0
MD
372 ptd = (pd_entry_t *)rcr3();
373 if ((pd_entry_t)ptd == IdlePTD) {
984263bc
MD
374 /*
375 * no page table, so create one and install it.
376 */
efda3bd0 377 pte = kmalloc(PAGE_SIZE, M_TEMP, M_WAITOK|M_ZERO);
6c6180e0 378 ptd = (pd_entry_t *)((vm_offset_t)ptd + KERNBASE);
984263bc
MD
379 *ptd = vtophys(pte) | PG_RW | PG_V;
380 } else {
381 /*
382 * this is a user-level page table
383 */
6c6180e0 384 pte = PTmap;
984263bc
MD
385 }
386 /*
0f7a3396
MD
387 * install pointer to page 0. Flush the tlb for safety. We don't
388 * migrate between cpus so a local flush is sufficient.
984263bc
MD
389 */
390 *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V;
0f7a3396 391 cpu_invltlb();
984263bc
MD
392
393 stack_top = stack;
e2565a42 394 __va_start(ap, fmt);
984263bc
MD
395 for (p = fmt; p && *p; p++) {
396 switch (*p) {
397 case 'p': /* 32-bit pointer */
e2565a42 398 i = __va_arg(ap, u_int);
984263bc
MD
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 */
e2565a42 405 i = __va_arg(ap, u_int);
984263bc
MD
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
6c6180e0 425 case 's': /* 16-bit integer passed as an int */
e2565a42 426 i = __va_arg(ap, int);
984263bc
MD
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
6c6180e0 442 if (pte == PTmap) {
984263bc
MD
443 *pte = 0; /* remove entry */
444 } else {
445 *ptd = 0; /* remove page table */
efda3bd0 446 kfree(pte, M_TEMP); /* ... and free it */
984263bc 447 }
984263bc
MD
448 /*
449 * XXX only needs to be invlpg(0) but that doesn't work on the 386
450 */
0f7a3396 451 cpu_invltlb();
984263bc
MD
452 return (i);
453}