/* * Emulation of priviledged instructions * * Copyright 1995 Alexandre Julliard */ #include #include "windows.h" #include "ldt.h" #include "miscemu.h" #include "registers.h" /*********************************************************************** * INSTR_EmulateInstruction * * Emulate a priviledged instruction. Returns TRUE if emulation successful. */ BOOL INSTR_EmulateInstruction( struct sigcontext_struct *context ) { int prefix, segprefix, repX, long_op, long_addr; BYTE *instr; long_op = long_addr = (GET_SEL_FLAGS(CS) & LDT_FLAGS_32BIT) != 0; instr = (BYTE *) PTR_SEG_OFF_TO_LIN( CS, long_op ? EIP : IP ); /* First handle any possible prefix */ segprefix = -1; /* no prefix */ prefix = 1; repX = 0; while(prefix) { switch(*instr) { case 0x2e: segprefix = CS; break; case 0x36: segprefix = SS; break; case 0x3e: segprefix = DS; break; case 0x26: segprefix = ES; break; case 0x64: segprefix = FS; break; case 0x65: segprefix = GS; break; case 0x66: long_op = !long_op; /* opcode size prefix */ break; case 0x67: long_addr = !long_addr; /* addr size prefix */ break; case 0xf0: /* lock */ break; case 0xf2: /* repne */ repX = 1; break; case 0xf3: /* repe */ repX = 2; break; default: prefix = 0; /* no more prefixes */ break; } if (prefix) { instr++; EIP++; } } /* Now look at the actual instruction */ switch(*instr) { case 0xcd: /* int */ if (long_op) { fprintf(stderr, "int xx from 32-bit code is not supported.\n"); return FALSE; /* Unable to emulate it */ } else { SEGPTR addr = INT_GetHandler( instr[1] ); /* FIXME: should check the stack 'big' bit */ WORD *stack = (WORD *)PTR_SEG_OFF_TO_LIN( SS, SP ); /* Push the flags and return address on the stack */ *(--stack) = FL; *(--stack) = CS; *(--stack) = IP + 2; SP -= 3 * sizeof(WORD); /* Jump to the interrupt handler */ CS = HIWORD(addr); EIP = LOWORD(addr); } break; case 0xcf: /* iret */ if (long_op) { /* FIXME: should check the stack 'big' bit */ DWORD *stack = (DWORD *)PTR_SEG_OFF_TO_LIN( SS, SP ); EIP = *stack++; CS = *stack++; EFL = *stack; SP += 3*sizeof(DWORD); /* Pop the return address and flags */ } else { /* FIXME: should check the stack 'big' bit */ WORD *stack = (WORD *)PTR_SEG_OFF_TO_LIN( SS, SP ); EIP = *stack++; CS = *stack++; FL = *stack; SP += 3*sizeof(WORD); /* Pop the return address and flags */ } break; case 0xe4: /* inb al,XX */ AL = inport( instr[1], 1 ); EIP += 2; break; case 0xe5: /* in (e)ax,XX */ if (long_op) EAX = inport( instr[1], 4 ); else AX = inport( instr[1], 2 ); EIP += 2; break; case 0xe6: /* outb XX,al */ outport( instr[1], 1, AL ); EIP += 2; break; case 0xe7: /* out XX,(e)ax */ if (long_op) outport( instr[1], 4, EAX ); else outport( instr[1], 2, AX ); EIP += 2; break; case 0xec: /* inb al,dx */ AL = inport( DX, 1 ); EIP++; break; case 0xed: /* in (e)ax,dx */ if (long_op) EAX = inport( DX, 4 ); else AX = inport( DX, 2 ); EIP++; break; case 0xee: /* outb dx,al */ outport( DX, 1, AL ); EIP++; break; case 0xef: /* out dx,(e)ax */ if (long_op) outport( DX, 4, EAX ); else outport( DX, 2, AX ); EIP++; break; case 0xfa: /* cli, ignored */ EIP++; break; case 0xfb: /* sti, ignored */ EIP++; break; case 0x6c: /* insb */ case 0x6d: /* insw/d */ case 0x6e: /* outsb */ case 0x6f: /* outsw/d */ { int typ = *instr; /* Just in case it's overwritten. */ int outp = (typ >= 0x6e); unsigned long count = repX ? (long_addr ? ECX : CX) : 1; int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1; int step = (EFL & 0x400) ? -opsize : +opsize; /* FIXME: Check this, please. */ int seg = outp ? (segprefix >= 0 ? segprefix : DS) : ES; if (outp) /* FIXME: Check segment readable. */ ; else /* FIXME: Check segment writeable. */ ; if (repX) if (long_addr) ECX = 0; else CX = 0; while (count-- > 0) { void *data; if (outp) { data = PTR_SEG_OFF_TO_LIN (seg, long_addr ? ESI : SI); if (long_addr) ESI += step; else SI += step; } else { data = PTR_SEG_OFF_TO_LIN (seg, long_addr ? EDI : DI); if (long_addr) EDI += step; else DI += step; } switch (typ) { case 0x6c: *((BYTE *)data) = inport (DX, 1); break; case 0x6d: if (long_op) *((DWORD *)data) = inport (DX, 4); else *((WORD *)data) = inport (DX, 2); break; case 0x6e: outport (DX, 1, *((BYTE *)data)); break; case 0x6f: if (long_op) outport (DX, 4, *((DWORD *)data)); else outport (DX, 2, *((WORD *)data)); break; } } EIP++; break; } default: fprintf(stderr, "Unexpected Windows program segfault" " - opcode = %x\n", *instr); return FALSE; /* Unable to emulate it */ } return TRUE; }