| /* |
| * Copyright 1999 Egbert Eich |
| * |
| * Permission to use, copy, modify, distribute, and sell this software and its |
| * documentation for any purpose is hereby granted without fee, provided that |
| * the above copyright notice appear in all copies and that both that |
| * copyright notice and this permission notice appear in supporting |
| * documentation, and that the name of the authors not be used in |
| * advertising or publicity pertaining to distribution of the software without |
| * specific, written prior permission. The authors makes no representations |
| * about the suitability of this software for any purpose. It is provided |
| * "as is" without express or implied warranty. |
| * |
| * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
| * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
| * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
| * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include "debug.h" |
| #include <sys/vm86.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <asm/unistd.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <setjmp.h> |
| #include "v86bios.h" |
| #include "AsmMacros.h" |
| |
| struct vm86_struct vm86s; |
| |
| static int vm86_GP_fault(void); |
| static int vm86_do_int(int num); |
| static void dump_code(void); |
| static void dump_registers(void); |
| static void stack_trace(void); |
| static int vm86_rep(struct vm86_struct *ptr); |
| |
| #define CPU_REG(x) (vm86s.regs.##x) |
| #define CPU_REG_LW(reg) (*((CARD16 *)&CPU_REG(reg))) |
| #define CPU_REG_HW(reg) (*((CARD16 *)&CPU_REG(reg) + 1)) |
| #define CPU_REG_LB(reg) (*(CARD8 *)&CPU_REG(e##reg)) |
| #define SEG_ADR(type, seg, reg) type((CPU_REG_LW(seg) << 4) \ |
| + CPU_REG_LW(e##reg)) |
| #define DF (1 << 10) |
| |
| struct pio P; |
| |
| |
| void |
| setup_io(void) |
| { |
| if (!Config.PrintPort && !Config.IoStatistics) { |
| P.inb = (CARD8(*)(CARD16))inb; |
| P.inw = (CARD16(*)(CARD16))inw; |
| P.inl = (CARD32(*)(CARD16))inl; |
| P.outb = (void(*)(CARD16,CARD8))outb; |
| P.outw = (void(*)(CARD16,CARD16))outw; |
| P.outl = (void(*)(CARD16,CARD32))outl; |
| } else { |
| P.inb = p_inb; |
| P.inw = p_inw; |
| P.inl = p_inl; |
| P.outb = p_outb; |
| P.outw = p_outw; |
| P.outl = p_outl; |
| } |
| } |
| |
| |
| static void |
| setup_vm86(unsigned long bios_start, i86biosRegsPtr regs) |
| { |
| CARD32 eip; |
| CARD16 cs; |
| |
| vm86s.flags = VM86_SCREEN_BITMAP; |
| vm86s.flags = 0; |
| vm86s.screen_bitmap = 0; |
| vm86s.cpu_type = CPU_586; |
| memset(&vm86s.int_revectored, 0xff,sizeof(vm86s.int_revectored)) ; |
| memset(&vm86s.int21_revectored, 0xff,sizeof(vm86s.int21_revectored)) ; |
| |
| eip = bios_start & 0xFFFF; |
| cs = (bios_start & 0xFF0000) >> 4; |
| |
| CPU_REG(eax) = regs->ax; |
| CPU_REG(ebx) = regs->bx; |
| CPU_REG(ecx) = regs->cx; |
| CPU_REG(edx) = regs->dx; |
| CPU_REG(esi) = 0; |
| CPU_REG(edi) = regs->di; |
| CPU_REG(ebp) = 0; |
| CPU_REG(eip) = eip; |
| CPU_REG(cs) = cs; |
| CPU_REG(esp) = 0x100; |
| CPU_REG(ss) = 0x30; /* This is the standard pc bios stack */ |
| CPU_REG(es) = regs->es; |
| CPU_REG(ds) = 0x40; /* standard pc ds */ |
| CPU_REG(fs) = 0; |
| CPU_REG(gs) = 0; |
| CPU_REG(eflags) |= (VIF_MASK | VIP_MASK); |
| } |
| |
| void |
| collect_bios_regs(i86biosRegsPtr regs) |
| { |
| regs->ax = CPU_REG(eax); |
| regs->bx = CPU_REG(ebx); |
| regs->cx = CPU_REG(ecx); |
| regs->dx = CPU_REG(edx); |
| regs->es = CPU_REG(es); |
| regs->ds = CPU_REG(ds); |
| regs->di = CPU_REG(edi); |
| regs->si = CPU_REG(esi); |
| } |
| |
| static int |
| do_vm86(void) |
| { |
| int retval; |
| |
| #ifdef V86BIOS_DEBUG |
| dump_registers(); |
| #endif |
| /* retval = SYS_vm86old(&vm86s); */ |
| /* retval = syscall(SYS_vm86old,&vm86s); */ |
| |
| retval = vm86_rep(&vm86s); |
| |
| switch (VM86_TYPE(retval)) { |
| case VM86_UNKNOWN: |
| if (!vm86_GP_fault()) return 0; |
| break; |
| case VM86_STI: |
| fprintf(stderr,"vm86_sti :-((\n"); |
| stack_trace(); |
| dump_code(); |
| return 0; |
| case VM86_INTx: |
| if (!vm86_do_int(VM86_ARG(retval))) { |
| fprintf(stderr,"\nUnknown vm86_int: %X\n\n",VM86_ARG(retval)); |
| dump_registers(); |
| return 0; |
| } |
| /* I'm not sure yet what to do if we can handle ints */ |
| break; |
| case VM86_SIGNAL: |
| fprintf(stderr,"received signal\n"); |
| return 0; |
| default: |
| fprintf(stderr,"unknown type(0x%x)=0x%x\n", |
| VM86_ARG(retval),VM86_TYPE(retval)); |
| dump_registers(); |
| dump_code(); |
| stack_trace(); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static jmp_buf x86_esc; |
| static void |
| vmexit(int unused) |
| { |
| longjmp(x86_esc,1); |
| } |
| |
| void |
| do_x86(unsigned long bios_start, i86biosRegsPtr regs) |
| { |
| static void (*org_handler)(int); |
| |
| setup_vm86(bios_start, regs); |
| if (setjmp(x86_esc) == 0) { |
| org_handler = signal(2,vmexit); |
| while(do_vm86()) {}; |
| signal(2,org_handler); |
| collect_bios_regs(regs); |
| } else { |
| signal(2,org_handler); |
| printf("interrupted at 0x%x\n",((CARD16)CPU_REG(cs)) << 4 |
| | (CARD16)CPU_REG(eip)); |
| } |
| } |
| |
| /* get the linear address */ |
| #define LIN_PREF_SI ((pref_seg << 4) + CPU_REG_LW(esi)) |
| |
| #define LWECX (prefix66 ^ prefix67 ? CPU_REG(ecx) : CPU_REG_LW(ecx)) |
| |
| static int |
| vm86_GP_fault(void) |
| { |
| unsigned char *csp, *lina; |
| CARD32 org_eip; |
| int pref_seg; |
| int done,is_rep,prefix66,prefix67; |
| |
| |
| csp = lina = SEG_ADR((unsigned char *), cs, ip); |
| #ifdef V86BIOS_DEBUG |
| printf("exception: \n"); |
| dump_code(); |
| #endif |
| |
| is_rep = 0; |
| prefix66 = prefix67 = 0; |
| pref_seg = -1; |
| |
| /* eat up prefixes */ |
| done = 0; |
| do { |
| switch (*(csp++)) { |
| case 0x66: /* operand prefix */ prefix66=1; break; |
| case 0x67: /* address prefix */ prefix67=1; break; |
| case 0x2e: /* CS */ pref_seg=CPU_REG(cs); break; |
| case 0x3e: /* DS */ pref_seg=CPU_REG(ds); break; |
| case 0x26: /* ES */ pref_seg=CPU_REG(es); break; |
| case 0x36: /* SS */ pref_seg=CPU_REG(ss); break; |
| case 0x65: /* GS */ pref_seg=CPU_REG(gs); break; |
| case 0x64: /* FS */ pref_seg=CPU_REG(fs); break; |
| case 0xf2: /* repnz */ |
| case 0xf3: /* rep */ is_rep=1; break; |
| default: done=1; |
| } |
| } while (!done); |
| csp--; /* oops one too many */ |
| org_eip = CPU_REG(eip); |
| CPU_REG_LW(eip) += (csp - lina); |
| |
| switch (*csp) { |
| |
| case 0x6c: /* insb */ |
| /* NOTE: ES can't be overwritten; prefixes 66,67 should use esi,edi,ecx |
| * but is anyone using extended regs in real mode? */ |
| /* WARNING: no test for DI wrapping! */ |
| CPU_REG_LW(edi) += port_rep_inb(CPU_REG_LW(edx), |
| SEG_ADR((CARD8 *),es,di), |
| CPU_REG_LW(eflags)&DF, |
| (is_rep? LWECX:1)); |
| if (is_rep) LWECX = 0; |
| CPU_REG_LW(eip)++; |
| break; |
| |
| case 0x6d: /* (rep) insw / insd */ |
| /* NOTE: ES can't be overwritten */ |
| /* WARNING: no test for _DI wrapping! */ |
| if (prefix66) { |
| CPU_REG_LW(edi) += port_rep_inl(CPU_REG_LW(edx), |
| SEG_ADR((CARD32 *),es,di), |
| CPU_REG_LW(eflags)&DF, |
| (is_rep? LWECX:1)); |
| } |
| else { |
| CPU_REG_LW(edi) += port_rep_inw(CPU_REG_LW(edx), |
| SEG_ADR((CARD16 *),es,di), |
| CPU_REG_LW(eflags)&DF, |
| (is_rep? LWECX:1)); |
| } |
| if (is_rep) LWECX = 0; |
| CPU_REG_LW(eip)++; |
| break; |
| |
| case 0x6e: /* (rep) outsb */ |
| if (pref_seg < 0) pref_seg = CPU_REG_LW(ds); |
| /* WARNING: no test for _SI wrapping! */ |
| CPU_REG_LW(esi) += port_rep_outb(CPU_REG_LW(edx),(CARD8*)LIN_PREF_SI, |
| CPU_REG_LW(eflags)&DF, |
| (is_rep? LWECX:1)); |
| if (is_rep) LWECX = 0; |
| CPU_REG_LW(eip)++; |
| break; |
| |
| case 0x6f: /* (rep) outsw / outsd */ |
| if (pref_seg < 0) pref_seg = CPU_REG_LW(ds); |
| /* WARNING: no test for _SI wrapping! */ |
| if (prefix66) { |
| CPU_REG_LW(esi) += port_rep_outl(CPU_REG_LW(edx), |
| (CARD32 *)LIN_PREF_SI, |
| CPU_REG_LW(eflags)&DF, |
| (is_rep? LWECX:1)); |
| } |
| else { |
| CPU_REG_LW(esi) += port_rep_outw(CPU_REG_LW(edx), |
| (CARD16 *)LIN_PREF_SI, |
| CPU_REG_LW(eflags)&DF, |
| (is_rep? LWECX:1)); |
| } |
| if (is_rep) LWECX = 0; |
| CPU_REG_LW(eip)++; |
| break; |
| |
| case 0xe5: /* inw xx, inl xx */ |
| if (prefix66) CPU_REG(eax) = P.inl((int) csp[1]); |
| else CPU_REG_LW(eax) = P.inw((int) csp[1]); |
| CPU_REG_LW(eip) += 2; |
| break; |
| case 0xe4: /* inb xx */ |
| CPU_REG_LW(eax) &= ~(CARD32)0xff; |
| CPU_REG_LB(ax) |= P.inb((int) csp[1]); |
| CPU_REG_LW(eip) += 2; |
| break; |
| case 0xed: /* inw dx, inl dx */ |
| if (prefix66) CPU_REG(eax) = P.inl(CPU_REG_LW(edx)); |
| else CPU_REG_LW(eax) = P.inw(CPU_REG_LW(edx)); |
| CPU_REG_LW(eip) += 1; |
| break; |
| case 0xec: /* inb dx */ |
| CPU_REG_LW(eax) &= ~(CARD32)0xff; |
| CPU_REG_LB(ax) |= P.inb(CPU_REG_LW(edx)); |
| CPU_REG_LW(eip) += 1; |
| break; |
| |
| case 0xe7: /* outw xx */ |
| if (prefix66) P.outl((int)csp[1], CPU_REG(eax)); |
| else P.outw((int)csp[1], CPU_REG_LW(eax)); |
| CPU_REG_LW(eip) += 2; |
| break; |
| case 0xe6: /* outb xx */ |
| P.outb((int) csp[1], CPU_REG_LB(ax)); |
| CPU_REG_LW(eip) += 2; |
| break; |
| case 0xef: /* outw dx */ |
| if (prefix66) P.outl(CPU_REG_LW(edx), CPU_REG(eax)); |
| else P.outw(CPU_REG_LW(edx), CPU_REG_LW(eax)); |
| CPU_REG_LW(eip) += 1; |
| break; |
| case 0xee: /* outb dx */ |
| P.outb(CPU_REG_LW(edx), CPU_REG_LB(ax)); |
| CPU_REG_LW(eip) += 1; |
| break; |
| |
| case 0xf4: |
| #ifdef V86BIOS_DEBUG |
| printf("hlt at %p\n", lina); |
| #endif |
| return 0; |
| |
| case 0x0f: |
| fprintf(stderr,"CPU 0x0f Trap at eip=0x%lx\n",CPU_REG(eip)); |
| goto op0ferr; |
| break; |
| |
| case 0xf0: /* lock */ |
| default: |
| fprintf(stderr,"unknown reason for exception\n"); |
| dump_registers(); |
| stack_trace(); |
| op0ferr: |
| dump_code(); |
| fprintf(stderr,"cannot continue\n"); |
| return 0; |
| } /* end of switch() */ |
| return 1; |
| } |
| |
| static int |
| vm86_do_int(int num) |
| { |
| int val; |
| struct regs86 regs; |
| |
| i_printf("int 0x%x received: ax:0x%lx",num,CPU_REG(eax)); |
| if (Config.PrintIp) |
| i_printf(" at: 0x%x\n",getIP()); |
| else |
| i_printf("\n"); |
| |
| /* try to run bios interrupt */ |
| |
| /* if not installed fall back */ |
| #define COPY(x) regs.##x = CPU_REG(x) |
| #define COPY_R(x) CPU_REG(x) = regs.##x |
| |
| COPY(eax); |
| COPY(ebx); |
| COPY(ecx); |
| COPY(edx); |
| COPY(esi); |
| COPY(edi); |
| COPY(ebp); |
| COPY(eip); |
| COPY(esp); |
| COPY(cs); |
| COPY(ss); |
| COPY(ds); |
| COPY(es); |
| COPY(fs); |
| COPY(gs); |
| COPY(eflags); |
| |
| if (!(val = int_handler(num,®s))) |
| if (!(val = run_bios_int(num,®s))) |
| return val; |
| |
| COPY_R(eax); |
| COPY_R(ebx); |
| COPY_R(ecx); |
| COPY_R(edx); |
| COPY_R(esi); |
| COPY_R(edi); |
| COPY_R(ebp); |
| COPY_R(eip); |
| COPY_R(esp); |
| COPY_R(cs); |
| COPY_R(ss); |
| COPY_R(ds); |
| COPY_R(es); |
| COPY_R(fs); |
| COPY_R(gs); |
| COPY_R(eflags); |
| |
| return val; |
| #undef COPY |
| #undef COPY_R |
| } |
| |
| static void |
| dump_code(void) |
| { |
| int i; |
| unsigned char *lina = SEG_ADR((unsigned char *), cs, ip); |
| |
| fprintf(stderr,"code at 0x%8.8x: ",(CARD32)lina); |
| for (i=0; i<0x10; i++) |
| fprintf(stderr,"%2.2x ",*(lina + i)); |
| fprintf(stderr,"\n "); |
| for (; i<0x20; i++) |
| fprintf(stderr,"%2.2x ",*(lina + i)); |
| fprintf(stderr,"\n"); |
| } |
| |
| #define PRINT(x) fprintf(stderr,#x":%4.4x ",CPU_REG_LW(x)) |
| #define PRINT_FLAGS(x) fprintf(stderr,#x":%8.8x ",CPU_REG_LW(x)) |
| static void |
| dump_registers(void) |
| { |
| PRINT(eip); |
| PRINT(eax); |
| PRINT(ebx); |
| PRINT(ecx); |
| PRINT(edx); |
| PRINT(esi); |
| PRINT(edi); |
| PRINT(ebp); |
| fprintf(stderr,"\n"); |
| PRINT(esp); |
| PRINT(cs); |
| PRINT(ss); |
| PRINT(es); |
| PRINT(ds); |
| PRINT(fs); |
| PRINT(gs); |
| PRINT_FLAGS(eflags); |
| fprintf(stderr,"\n"); |
| } |
| |
| static void |
| stack_trace(void) |
| { |
| int i; |
| unsigned char *stack = SEG_ADR((unsigned char *), ss, sp); |
| |
| fprintf(stderr,"stack at 0x%8.8lx:\n",(unsigned long)stack); |
| for (i=0; i < 0x10; i++) |
| fprintf(stderr,"%2.2x ",*(stack + i)); |
| fprintf(stderr,"\n"); |
| |
| } |
| |
| static int |
| vm86_rep(struct vm86_struct *ptr) |
| { |
| |
| int __res; |
| |
| __asm__ __volatile__("int $0x80\n" |
| :"=a" (__res):"a" ((int)113), |
| "b" ((struct vm86_struct *)ptr)); |
| |
| if ((__res) < 0) { |
| errno = -__res; |
| __res=-1; |
| } |
| else errno = 0; |
| return __res; |
| } |
| |
| #define pushw(base, ptr, val) \ |
| __asm__ __volatile__( \ |
| "decw %w0\n\t" \ |
| "movb %h2,(%1,%0)\n\t" \ |
| "decw %w0\n\t" \ |
| "movb %b2,(%1,%0)" \ |
| : "=r" (ptr) \ |
| : "r" (base), "q" (val), "0" (ptr)) |
| |
| int |
| run_bios_int(int num, struct regs86 *regs) |
| { |
| CARD16 *ssp; |
| CARD32 sp; |
| CARD32 eflags; |
| |
| #ifdef V86BIOS_DEBUG |
| static int firsttime = 1; |
| #endif |
| /* check if bios vector is initialized */ |
| if (((CARD16*)0)[(num<<1)+1] == 0x0000) { /* SYS_BIOS_SEG ?*/ |
| #ifdef V86BIOS_DEBUG |
| i_printf("card BIOS not loaded\n"); |
| #endif |
| return 0; |
| } |
| |
| #ifdef V86BIOS_DEBUG |
| if (firsttime) { |
| dprint(0,0x3D0); |
| firsttime = 0; |
| } |
| #endif |
| |
| i_printf("calling card BIOS at: "); |
| ssp = (CARD16*)(CPU_REG(ss)<<4); |
| sp = (CARD32) CPU_REG_LW(esp); |
| |
| eflags = regs->eflags; |
| eflags = ((eflags & VIF_MASK) != 0) |
| ? (eflags | IF_MASK) : (eflags & ~(CARD32) IF_MASK); |
| pushw(ssp, sp, eflags); |
| pushw(ssp, sp, regs->cs); |
| pushw(ssp, sp, (CARD16)regs->eip); |
| regs->esp -= 6; |
| regs->cs = ((CARD16 *) 0)[(num << 1) + 1]; |
| regs->eip = (regs->eip & 0xFFFF0000) | ((CARD16 *) 0)[num << 1]; |
| i_printf("0x%x:%lx\n",regs->cs,regs->eip); |
| #ifdef V86BIOS_DEBUG |
| dump_code(); |
| #endif |
| regs->eflags = regs->eflags |
| & ~(VIF_MASK | TF_MASK | IF_MASK | NT_MASK); |
| return 1; |
| } |
| |
| CARD32 |
| getIntVect(int num) |
| { |
| return ((CARD32*)0)[num]; |
| } |
| |
| CARD32 |
| getIP(void) |
| { |
| return (CPU_REG(cs) << 4) + CPU_REG(eip); |
| } |