]>
Commit | Line | Data |
---|---|---|
a6316ce4 MT |
1 | /* |
2 | * Copyright 1999 Egbert Eich | |
3 | * | |
4 | * Permission to use, copy, modify, distribute, and sell this software and its | |
5 | * documentation for any purpose is hereby granted without fee, provided that | |
6 | * the above copyright notice appear in all copies and that both that | |
7 | * copyright notice and this permission notice appear in supporting | |
8 | * documentation, and that the name of the authors not be used in | |
9 | * advertising or publicity pertaining to distribution of the software without | |
10 | * specific, written prior permission. The authors makes no representations | |
11 | * about the suitability of this software for any purpose. It is provided | |
12 | * "as is" without express or implied warranty. | |
13 | * | |
14 | * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | |
16 | * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | |
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | |
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
20 | * PERFORMANCE OF THIS SOFTWARE. | |
21 | */ | |
22 | ||
23 | #include <unistd.h> | |
24 | #include <errno.h> | |
25 | #include <asm/unistd.h> | |
26 | #include <stdio.h> | |
27 | #include <string.h> | |
28 | #ifdef __i386__ | |
29 | #include <sys/vm86.h> | |
30 | #else | |
31 | #include "vm86_struct.h" | |
32 | #endif | |
33 | #include <signal.h> | |
34 | #include "v86bios.h" | |
35 | #include "AsmMacros.h" | |
36 | ||
37 | extern int emu_vm86(struct vm86_struct *vm, unsigned debug); | |
38 | ||
39 | #define INT2PTR(a) ((a) + (unsigned char *) 0) | |
40 | ||
41 | void log_err(char *format, ...) __attribute__ ((format (printf, 1, 2))); | |
42 | ||
43 | struct vm86_struct vm86s; | |
44 | ||
45 | static int vm86_GP_fault(void); | |
46 | static int vm86_do_int(int num); | |
47 | #ifdef __i386__ | |
48 | static int vm86_rep(struct vm86_struct *ptr); | |
49 | #endif | |
50 | void log_registers(void); | |
51 | ||
52 | #define CPU_REG(x) (vm86s.regs.x) | |
53 | #define CPU_REG_LW(reg) (*((CARD16 *)&CPU_REG(reg))) | |
54 | #define CPU_REG_HW(reg) (*((CARD16 *)&CPU_REG(reg) + 1)) | |
55 | #define CPU_REG_LB(reg) (*(CARD8 *)&CPU_REG(e##reg)) | |
56 | #define SEG_ADR(type, seg, reg) type((CPU_REG_LW(seg) << 4) + CPU_REG_LW(e##reg) + (unsigned char *) 0) | |
57 | #define DF (1 << 10) | |
58 | ||
59 | struct pio P; | |
60 | ||
61 | void | |
62 | setup_io(void) | |
63 | { | |
64 | P.inb = (CARD8(*)(CARD16))inb; | |
65 | P.inw = (CARD16(*)(CARD16))inw; | |
66 | P.inl = (CARD32(*)(CARD16))inl; | |
67 | P.outb = (void(*)(CARD16,CARD8))outb; | |
68 | P.outw = (void(*)(CARD16,CARD16))outw; | |
69 | P.outl = (void(*)(CARD16,CARD32))outl; | |
70 | } | |
71 | ||
72 | ||
73 | static void | |
74 | setup_vm86(unsigned long bios_start, i86biosRegsPtr regs) | |
75 | { | |
76 | CARD32 eip; | |
77 | CARD16 cs; | |
78 | ||
79 | vm86s.flags = VM86_SCREEN_BITMAP; | |
80 | vm86s.flags = 0; | |
81 | vm86s.screen_bitmap = 0; | |
82 | vm86s.cpu_type = CPU_586; | |
83 | memset(&vm86s.int_revectored, 0xff,sizeof(vm86s.int_revectored)) ; | |
84 | memset(&vm86s.int21_revectored, 0xff,sizeof(vm86s.int21_revectored)) ; | |
85 | ||
86 | eip = bios_start & 0xFFFF; | |
87 | cs = (bios_start & 0xFF0000) >> 4; | |
88 | ||
89 | CPU_REG(eax) = regs->ax; | |
90 | CPU_REG(ebx) = regs->bx; | |
91 | CPU_REG(ecx) = regs->cx; | |
92 | CPU_REG(edx) = regs->dx; | |
93 | CPU_REG(esi) = 0; | |
94 | CPU_REG(edi) = regs->di; | |
95 | CPU_REG(ebp) = 0; | |
96 | CPU_REG(eip) = eip; | |
97 | CPU_REG(cs) = cs; | |
98 | CPU_REG(esp) = 0x100; | |
99 | CPU_REG(ss) = 0x30; /* This is the standard pc bios stack */ | |
100 | CPU_REG(es) = regs->es; | |
101 | CPU_REG(ds) = 0x40; /* standard pc ds */ | |
102 | CPU_REG(fs) = 0; | |
103 | CPU_REG(gs) = 0; | |
104 | CPU_REG(eflags) |= (VIF_MASK | VIP_MASK); | |
105 | } | |
106 | ||
107 | void | |
108 | collect_bios_regs(i86biosRegsPtr regs) | |
109 | { | |
110 | regs->ax = CPU_REG(eax); | |
111 | regs->bx = CPU_REG(ebx); | |
112 | regs->cx = CPU_REG(ecx); | |
113 | regs->dx = CPU_REG(edx); | |
114 | regs->es = CPU_REG(es); | |
115 | regs->ds = CPU_REG(ds); | |
116 | regs->di = CPU_REG(edi); | |
117 | regs->si = CPU_REG(esi); | |
118 | } | |
119 | ||
120 | static int do_vm86(int cpuemu) | |
121 | { | |
122 | int retval; | |
123 | ||
124 | #ifdef V86BIOS_DEBUG | |
125 | dump_registers(); | |
126 | #endif | |
127 | ||
128 | #ifdef __i386__ | |
129 | if(cpuemu) { | |
130 | retval = emu_vm86(&vm86s, cpuemu & 2); | |
131 | } | |
132 | else { | |
133 | retval = vm86_rep(&vm86s); | |
134 | } | |
135 | #else | |
136 | retval = emu_vm86(&vm86s, cpuemu & 2); | |
137 | #endif | |
138 | ||
139 | switch (VM86_TYPE(retval)) { | |
140 | case VM86_UNKNOWN: | |
141 | if (!vm86_GP_fault()) | |
142 | return 0; | |
143 | break; | |
144 | case VM86_STI: | |
145 | log_err("vm86_sti :-((\n"); | |
146 | log_registers(); | |
147 | return 0; | |
148 | case VM86_INTx: | |
149 | if (!vm86_do_int(VM86_ARG(retval))) { | |
150 | log_err("Unknown vm86_int: %X\n",VM86_ARG(retval)); | |
151 | log_registers(); | |
152 | return 0; | |
153 | } | |
154 | /* I'm not sure yet what to do if we can handle ints */ | |
155 | break; | |
156 | case VM86_SIGNAL: | |
157 | log_err("VBE: received a signal!\n"); | |
158 | log_registers(); | |
159 | return 0; | |
160 | default: | |
161 | log_err("unknown type(0x%x)=0x%x\n", | |
162 | VM86_ARG(retval),VM86_TYPE(retval)); | |
163 | log_registers(); | |
164 | return 0; | |
165 | } | |
166 | ||
167 | return 1; | |
168 | } | |
169 | ||
170 | void | |
171 | do_x86(unsigned long bios_start, i86biosRegsPtr regs, int cpuemu) | |
172 | { | |
173 | setup_vm86(bios_start, regs); | |
174 | while(do_vm86(cpuemu)) {}; | |
175 | collect_bios_regs(regs); | |
176 | } | |
177 | ||
178 | /* get the linear address */ | |
179 | #define LIN_PREF_SI ((pref_seg << 4) + CPU_REG_LW(esi)) | |
180 | ||
181 | #define LWECX (prefix66 ^ prefix67 ? CPU_REG(ecx) : CPU_REG_LW(ecx)) | |
182 | ||
183 | static int | |
184 | vm86_GP_fault(void) | |
185 | { | |
186 | unsigned char *csp, *lina; | |
187 | CARD32 org_eip; | |
188 | int pref_seg; | |
189 | int done,is_rep,prefix66,prefix67; | |
190 | ||
191 | ||
192 | csp = lina = SEG_ADR((unsigned char *), cs, ip); | |
193 | #ifdef V86BIOS_DEBUG | |
194 | printf("exception: \n"); | |
195 | dump_code(); | |
196 | #endif | |
197 | ||
198 | is_rep = 0; | |
199 | prefix66 = prefix67 = 0; | |
200 | pref_seg = -1; | |
201 | ||
202 | /* eat up prefixes */ | |
203 | done = 0; | |
204 | do { | |
205 | switch (*(csp++)) { | |
206 | case 0x66: /* operand prefix */ prefix66=1; break; | |
207 | case 0x67: /* address prefix */ prefix67=1; break; | |
208 | case 0x2e: /* CS */ pref_seg=CPU_REG(cs); break; | |
209 | case 0x3e: /* DS */ pref_seg=CPU_REG(ds); break; | |
210 | case 0x26: /* ES */ pref_seg=CPU_REG(es); break; | |
211 | case 0x36: /* SS */ pref_seg=CPU_REG(ss); break; | |
212 | case 0x65: /* GS */ pref_seg=CPU_REG(gs); break; | |
213 | case 0x64: /* FS */ pref_seg=CPU_REG(fs); break; | |
214 | case 0xf2: /* repnz */ | |
215 | case 0xf3: /* rep */ is_rep=1; break; | |
216 | default: done=1; | |
217 | } | |
218 | } while (!done); | |
219 | csp--; /* oops one too many */ | |
220 | org_eip = CPU_REG(eip); | |
221 | CPU_REG_LW(eip) += (csp - lina); | |
222 | ||
223 | switch (*csp) { | |
224 | ||
225 | case 0x6c: /* insb */ | |
226 | /* NOTE: ES can't be overwritten; prefixes 66,67 should use esi,edi,ecx | |
227 | * but is anyone using extended regs in real mode? */ | |
228 | /* WARNING: no test for DI wrapping! */ | |
229 | CPU_REG_LW(edi) += port_rep_inb(CPU_REG_LW(edx), | |
230 | SEG_ADR((CARD8 *),es,di), | |
231 | CPU_REG_LW(eflags)&DF, | |
232 | (is_rep? LWECX:1)); | |
233 | if (is_rep) LWECX = 0; | |
234 | CPU_REG_LW(eip)++; | |
235 | break; | |
236 | ||
237 | case 0x6d: /* (rep) insw / insd */ | |
238 | /* NOTE: ES can't be overwritten */ | |
239 | /* WARNING: no test for _DI wrapping! */ | |
240 | if (prefix66) { | |
241 | CPU_REG_LW(edi) += port_rep_inl(CPU_REG_LW(edx), | |
242 | SEG_ADR((CARD32 *),es,di), | |
243 | CPU_REG_LW(eflags)&DF, | |
244 | (is_rep? LWECX:1)); | |
245 | } | |
246 | else { | |
247 | CPU_REG_LW(edi) += port_rep_inw(CPU_REG_LW(edx), | |
248 | SEG_ADR((CARD16 *),es,di), | |
249 | CPU_REG_LW(eflags)&DF, | |
250 | (is_rep? LWECX:1)); | |
251 | } | |
252 | if (is_rep) LWECX = 0; | |
253 | CPU_REG_LW(eip)++; | |
254 | break; | |
255 | ||
256 | case 0x6e: /* (rep) outsb */ | |
257 | if (pref_seg < 0) pref_seg = CPU_REG_LW(ds); | |
258 | /* WARNING: no test for _SI wrapping! */ | |
259 | CPU_REG_LW(esi) += port_rep_outb(CPU_REG_LW(edx),(CARD8*)INT2PTR(LIN_PREF_SI), | |
260 | CPU_REG_LW(eflags)&DF, | |
261 | (is_rep? LWECX:1)); | |
262 | if (is_rep) LWECX = 0; | |
263 | CPU_REG_LW(eip)++; | |
264 | break; | |
265 | ||
266 | case 0x6f: /* (rep) outsw / outsd */ | |
267 | if (pref_seg < 0) pref_seg = CPU_REG_LW(ds); | |
268 | /* WARNING: no test for _SI wrapping! */ | |
269 | if (prefix66) { | |
270 | CPU_REG_LW(esi) += port_rep_outl(CPU_REG_LW(edx), | |
271 | (CARD32 *)INT2PTR(LIN_PREF_SI), | |
272 | CPU_REG_LW(eflags)&DF, | |
273 | (is_rep? LWECX:1)); | |
274 | } | |
275 | else { | |
276 | CPU_REG_LW(esi) += port_rep_outw(CPU_REG_LW(edx), | |
277 | (CARD16 *)INT2PTR(LIN_PREF_SI), | |
278 | CPU_REG_LW(eflags)&DF, | |
279 | (is_rep? LWECX:1)); | |
280 | } | |
281 | if (is_rep) LWECX = 0; | |
282 | CPU_REG_LW(eip)++; | |
283 | break; | |
284 | ||
285 | case 0xe5: /* inw xx, inl xx */ | |
286 | if (prefix66) CPU_REG(eax) = P.inl((int) csp[1]); | |
287 | else CPU_REG_LW(eax) = P.inw((int) csp[1]); | |
288 | CPU_REG_LW(eip) += 2; | |
289 | break; | |
290 | case 0xe4: /* inb xx */ | |
291 | CPU_REG_LW(eax) &= ~(CARD32)0xff; | |
292 | CPU_REG_LB(ax) |= P.inb((int) csp[1]); | |
293 | CPU_REG_LW(eip) += 2; | |
294 | break; | |
295 | case 0xed: /* inw dx, inl dx */ | |
296 | if (prefix66) CPU_REG(eax) = P.inl(CPU_REG_LW(edx)); | |
297 | else CPU_REG_LW(eax) = P.inw(CPU_REG_LW(edx)); | |
298 | CPU_REG_LW(eip) += 1; | |
299 | break; | |
300 | case 0xec: /* inb dx */ | |
301 | CPU_REG_LW(eax) &= ~(CARD32)0xff; | |
302 | CPU_REG_LB(ax) |= P.inb(CPU_REG_LW(edx)); | |
303 | CPU_REG_LW(eip) += 1; | |
304 | break; | |
305 | ||
306 | case 0xe7: /* outw xx */ | |
307 | if (prefix66) P.outl((int)csp[1], CPU_REG(eax)); | |
308 | else P.outw((int)csp[1], CPU_REG_LW(eax)); | |
309 | CPU_REG_LW(eip) += 2; | |
310 | break; | |
311 | case 0xe6: /* outb xx */ | |
312 | P.outb((int) csp[1], CPU_REG_LB(ax)); | |
313 | CPU_REG_LW(eip) += 2; | |
314 | break; | |
315 | case 0xef: /* outw dx */ | |
316 | if (prefix66) P.outl(CPU_REG_LW(edx), CPU_REG(eax)); | |
317 | else P.outw(CPU_REG_LW(edx), CPU_REG_LW(eax)); | |
318 | CPU_REG_LW(eip) += 1; | |
319 | break; | |
320 | case 0xee: /* outb dx */ | |
321 | P.outb(CPU_REG_LW(edx), CPU_REG_LB(ax)); | |
322 | CPU_REG_LW(eip) += 1; | |
323 | break; | |
324 | ||
325 | case 0xf4: | |
326 | #ifdef V86BIOS_DEBUG | |
327 | printf("hlt at %p\n", lina); | |
328 | #endif | |
329 | return 0; | |
330 | ||
331 | case 0x0f: | |
332 | log_err("CPU 0x0f Trap at eip=0x%lx\n",CPU_REG(eip)); | |
333 | goto op0ferr; | |
334 | break; | |
335 | ||
336 | case 0xf0: /* lock */ | |
337 | default: | |
338 | log_err("unknown reason for exception\n"); | |
339 | log_registers(); | |
340 | op0ferr: | |
341 | log_err("cannot continue\n"); | |
342 | return 0; | |
343 | } /* end of switch() */ | |
344 | return 1; | |
345 | } | |
346 | ||
347 | static int | |
348 | vm86_do_int(int num) | |
349 | { | |
350 | int val; | |
351 | struct regs86 regs; | |
352 | ||
353 | /* try to run bios interrupt */ | |
354 | ||
355 | /* if not installed fall back */ | |
356 | #define COPY(x) regs.x = CPU_REG(x) | |
357 | #define COPY_R(x) CPU_REG(x) = regs.x | |
358 | ||
359 | COPY(eax); | |
360 | COPY(ebx); | |
361 | COPY(ecx); | |
362 | COPY(edx); | |
363 | COPY(esi); | |
364 | COPY(edi); | |
365 | COPY(ebp); | |
366 | COPY(eip); | |
367 | COPY(esp); | |
368 | COPY(cs); | |
369 | COPY(ss); | |
370 | COPY(ds); | |
371 | COPY(es); | |
372 | COPY(fs); | |
373 | COPY(gs); | |
374 | COPY(eflags); | |
375 | ||
376 | if (!(val = int_handler(num,®s))) | |
377 | if (!(val = run_bios_int(num,®s))) | |
378 | return val; | |
379 | ||
380 | COPY_R(eax); | |
381 | COPY_R(ebx); | |
382 | COPY_R(ecx); | |
383 | COPY_R(edx); | |
384 | COPY_R(esi); | |
385 | COPY_R(edi); | |
386 | COPY_R(ebp); | |
387 | COPY_R(eip); | |
388 | COPY_R(esp); | |
389 | COPY_R(cs); | |
390 | COPY_R(ss); | |
391 | COPY_R(ds); | |
392 | COPY_R(es); | |
393 | COPY_R(fs); | |
394 | COPY_R(gs); | |
395 | COPY_R(eflags); | |
396 | ||
397 | return val; | |
398 | #undef COPY | |
399 | #undef COPY_R | |
400 | } | |
401 | ||
402 | #ifdef __i386__ | |
403 | ||
404 | static int | |
405 | vm86_rep(struct vm86_struct *ptr) | |
406 | { | |
407 | ||
408 | int __res; | |
409 | ||
410 | /* stay away from %ebx */ | |
411 | __asm__ __volatile__("push %%ebx\n\tmov %%ecx,%%ebx\n\tpush %%gs\n\tint $0x80\n\tpop %%gs\n\tpop %%ebx\n" | |
412 | :"=a" (__res):"a" ((int)113), | |
413 | "c" ((struct vm86_struct *)ptr)); | |
414 | ||
415 | if ((__res) < 0) { | |
416 | errno = -__res; | |
417 | __res=-1; | |
418 | } | |
419 | else errno = 0; | |
420 | return __res; | |
421 | } | |
422 | ||
423 | #endif | |
424 | ||
425 | #ifdef __i386__ | |
426 | ||
427 | #define pushw(base, ptr, val) \ | |
428 | __asm__ __volatile__( \ | |
429 | "decw %w0\n\t" \ | |
430 | "movb %h2,(%1,%0)\n\t" \ | |
431 | "decw %w0\n\t" \ | |
432 | "movb %b2,(%1,%0)" \ | |
433 | : "=r" (ptr) \ | |
434 | : "r" (base), "q" (val), "0" (ptr)) | |
435 | ||
436 | #else | |
437 | ||
438 | #define pushw(base, ptr, val) { \ | |
439 | ptr = ((ptr) - 1) & 0xffff; \ | |
440 | *((unsigned char *)(base) + (ptr)) = (val) >> 8; \ | |
441 | ptr = ((ptr) - 1) & 0xffff; \ | |
442 | *((unsigned char *)(base) + (ptr)) = (val); \ | |
443 | } | |
444 | ||
445 | #endif | |
446 | ||
447 | int | |
448 | run_bios_int(int num, struct regs86 *regs) | |
449 | { | |
450 | CARD16 *ssp; | |
451 | CARD32 sp; | |
452 | CARD32 eflags; | |
453 | ||
454 | #ifdef V86BIOS_DEBUG | |
455 | static int firsttime = 1; | |
456 | #endif | |
457 | /* check if bios vector is initialized */ | |
458 | if (((CARD16*)0)[(num<<1)+1] == 0x0000) { /* SYS_BIOS_SEG ?*/ | |
459 | return 0; | |
460 | } | |
461 | ||
462 | #ifdef V86BIOS_DEBUG | |
463 | if (firsttime) { | |
464 | dprint(0,0x3D0); | |
465 | firsttime = 0; | |
466 | } | |
467 | #endif | |
468 | ||
469 | ssp = (CARD16*)INT2PTR(CPU_REG(ss)<<4); | |
470 | sp = (CARD32) CPU_REG_LW(esp); | |
471 | ||
472 | eflags = regs->eflags; | |
473 | eflags = ((eflags & VIF_MASK) != 0) | |
474 | ? (eflags | IF_MASK) : (eflags & ~(CARD32) IF_MASK); | |
475 | pushw(ssp, sp, eflags); | |
476 | pushw(ssp, sp, regs->cs); | |
477 | pushw(ssp, sp, (CARD16)regs->eip); | |
478 | regs->esp -= 6; | |
479 | regs->cs = ((CARD16 *) 0)[(num << 1) + 1]; | |
480 | regs->eip = (regs->eip & 0xFFFF0000) | ((CARD16 *) 0)[num << 1]; | |
481 | #ifdef V86BIOS_DEBUG | |
482 | dump_code(); | |
483 | #endif | |
484 | regs->eflags = regs->eflags | |
485 | & ~(VIF_MASK | TF_MASK | IF_MASK | NT_MASK); | |
486 | return 1; | |
487 | } | |
488 | ||
489 | CARD32 | |
490 | getIntVect(int num) | |
491 | { | |
492 | return ((CARD32*)0)[num]; | |
493 | } | |
494 | ||
495 | CARD32 | |
496 | getIP(void) | |
497 | { | |
498 | return (CPU_REG(cs) << 4) + CPU_REG(eip); | |
499 | } | |
500 | ||
501 | void log_registers() | |
502 | { | |
503 | log_err( | |
504 | " eax %08x, ebx %08x, ecx %08x, edx %08x\n" | |
505 | " esi %08x, edi %08x, ebp %08x, esp %08x\n" | |
506 | " ds %04x, es %04x, fs %04x, gs %04x, ss %04x\n" | |
507 | " cs:eip %04x:%08x\n", | |
508 | (unsigned) CPU_REG(eax), (unsigned) CPU_REG(ebx), (unsigned) CPU_REG(ecx), (unsigned) CPU_REG(edx), | |
509 | (unsigned) CPU_REG(esi), (unsigned) CPU_REG(edi), (unsigned) CPU_REG(ebp), (unsigned) CPU_REG(esp), | |
510 | (unsigned) CPU_REG(ds), (unsigned) CPU_REG(es), | |
511 | (unsigned) CPU_REG(fs), (unsigned) CPU_REG(gs), (unsigned) CPU_REG(ss), | |
512 | (unsigned) CPU_REG(cs), (unsigned) CPU_REG(eip) | |
513 | ); | |
514 | } | |
515 |