/************************************************************* * call16_handler * * Handler for exceptions occurring in 16-bit code. */ static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame, CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher ) { if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) { /* unwinding: restore the stack pointer in the TEB, and leave the Win16 mutex */ STACK32FRAME *frame32 = (STACK32FRAME *)((char *)frame - offsetof(STACK32FRAME,frame)); NtCurrentTeb()->WOW32Reserved = (void *)frame32->frame16; _LeaveWin16Lock(); } else if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) { if (wine_ldt_is_system(context->SegCs)) { if (fix_selector( context )) return ExceptionContinueExecution; } else { SEGPTR gpHandler; DWORD ret = INSTR_EmulateInstruction( record, context ); /* * Insert check for pending DPMI events. Note that this * check must be inserted after instructions have been * emulated because the instruction emulation requires * original CS:IP and the emulation may change TEB.dpmi_vif. */ if(NtCurrentTeb()->dpmi_vif) insert_event_check( context ); if (ret != ExceptionContinueSearch) return ret; /* check for Win16 __GP handler */ if ((gpHandler = HasGPHandler16( MAKESEGPTR( context->SegCs, context->Eip ) ))) { WORD *stack = wine_ldt_get_ptr( context->SegSs, context->Esp ); *--stack = context->SegCs; *--stack = context->Eip; if (!IS_SELECTOR_32BIT(context->SegSs)) context->Esp = MAKELONG( LOWORD(context->Esp - 2*sizeof(WORD)), HIWORD(context->Esp) ); else context->Esp -= 2*sizeof(WORD); context->SegCs = SELECTOROF( gpHandler ); context->Eip = OFFSETOF( gpHandler ); return ExceptionContinueExecution; } } } else if (record->ExceptionCode == EXCEPTION_VM86_STI) { insert_event_check( context ); } return ExceptionContinueSearch; }
/********************************************************************** * DPMI_CallRMCBProc * * This routine does the hard work of calling a callback procedure. */ static void DPMI_CallRMCBProc( CONTEXT86 *context, RMCB *rmcb, WORD flag ) { DWORD old_vif = NtCurrentTeb()->dpmi_vif; /* Disable virtual interrupts. */ NtCurrentTeb()->dpmi_vif = 0; if (wine_ldt_is_system( rmcb->proc_sel )) { /* Wine-internal RMCB, call directly */ ((RMCBPROC)rmcb->proc_ofs)(context); } else __TRY { #ifdef __i386__ UINT16 ss,es; DWORD esp,edi; INT_SetRealModeContext(MapSL(MAKESEGPTR( rmcb->regs_sel, rmcb->regs_ofs )), context); ss = alloc_pm_selector( context->SegSs, WINE_LDT_FLAGS_DATA ); esp = context->Esp; FIXME("untested!\n"); /* The called proc ends with an IRET, and takes these parameters: * DS:ESI = pointer to real-mode SS:SP * ES:EDI = pointer to real-mode call structure * It returns: * ES:EDI = pointer to real-mode call structure (may be a copy) * It is the proc's responsibility to change the return CS:IP in the * real-mode call structure. */ if (flag & 1) { /* 32-bit DPMI client */ DPMI_CallRMCB32(rmcb, ss, esp, &es, &edi); } else { /* 16-bit DPMI client */ CONTEXT86 ctx = *context; ctx.SegCs = rmcb->proc_sel; ctx.Eip = rmcb->proc_ofs; ctx.SegDs = ss; ctx.Esi = esp; ctx.SegEs = rmcb->regs_sel; ctx.Edi = rmcb->regs_ofs; /* FIXME: I'm pretty sure this isn't right - should push flags first */ WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&ctx ); es = ctx.SegEs; edi = ctx.Edi; } wine_ldt_free_entries( ss, 1 ); INT_GetRealModeContext( MapSL( MAKESEGPTR( es, edi )), context); #else ERR("RMCBs only implemented for i386\n"); #endif } __EXCEPT(dpmi_exception_handler) { } __ENDTRY /* Restore virtual interrupt flag. */ NtCurrentTeb()->dpmi_vif = old_vif; }
/************************************************************* * insert_event_check * * Make resuming the context check for pending DPMI events * before the original context is restored. This is required * because DPMI events are asynchronous, they are blocked while * Wine 32-bit code is being executed and we want to prevent * a race when returning back to 16-bit or 32-bit DPMI context. */ static void insert_event_check( CONTEXT *context ) { char *stack = wine_ldt_get_ptr( context->SegSs, context->Esp ); /* don't do event check while in system code */ if (wine_ldt_is_system(context->SegCs)) return; if(context->SegCs == dpmi_checker_selector && context->Eip >= dpmi_checker_offset_call && context->Eip <= dpmi_checker_offset_cleanup) { /* * Nested call. Stack will be preserved. */ } else if(context->SegCs == dpmi_checker_selector && context->Eip == dpmi_checker_offset_return) { /* * Nested call. We have just finished popping the fs * register, lets put it back into stack. */ stack -= sizeof(WORD); *(WORD*)stack = context->SegFs; context->Esp -= 2; } else { /* * Call is not nested. * Push modified registers into stack. * These will be popped by the assembler stub. */ stack -= sizeof(DWORD); *(DWORD*)stack = context->EFlags; stack -= sizeof(DWORD); *(DWORD*)stack = context->SegCs; stack -= sizeof(DWORD); *(DWORD*)stack = context->Eip; stack -= sizeof(WORD); *(WORD*)stack = context->SegFs; context->Esp -= 14; } /* * Modify the context so that we jump into assembler stub. * TEB access is made easier by providing the stub * with the correct fs register value. */ context->SegCs = dpmi_checker_selector; context->Eip = dpmi_checker_offset_call; context->SegFs = wine_get_fs(); }
/********************************************************************** * DOSVM_EmulateInterruptPM * * Emulate software interrupt in 16-bit or 32-bit protected mode. * Called from signal handler when intXX opcode is executed. * * Pushes interrupt frame to stack and changes instruction * pointer to interrupt handler. */ BOOL DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum ) { TRACE_(relay)("Call DOS int 0x%02x ret=%04x:%08x\n" " eax=%08x ebx=%08x ecx=%08x edx=%08x\n" " esi=%08x edi=%08x ebp=%08x esp=%08x\n" " ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n", intnum, context->SegCs, context->Eip, context->Eax, context->Ebx, context->Ecx, context->Edx, context->Esi, context->Edi, context->Ebp, context->Esp, context->SegDs, context->SegEs, context->SegFs, context->SegGs, context->SegSs, context->EFlags ); DOSMEM_InitDosMemory(); if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel) { DOSVM_BuildCallFrame( context, DOSVM_IntProcRelay, DOSVM_RawModeSwitchHandler ); } else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel) { /* * This must not be called using DOSVM_BuildCallFrame. */ DOSVM_RelayHandler( context ); } else if (context->SegCs == DOSVM_dpmi_segments->int48_sel) { /* Restore original flags stored into the stack by the caller. */ DWORD *stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp); context->EFlags = stack[2]; if (intnum != context->Eip / DOSVM_STUB_PM48) WARN( "interrupt stub has been modified " "(interrupt is %02x, interrupt stub is %02x)\n", intnum, context->Eip/DOSVM_STUB_PM48 ); TRACE( "builtin interrupt %02x has been branched to\n", intnum ); if (intnum == 0x25 || intnum == 0x26) DOSVM_PushFlags( context, TRUE, TRUE ); DOSVM_BuildCallFrame( context, DOSVM_IntProcRelay, DOSVM_GetBuiltinHandler(intnum) ); } else if (context->SegCs == DOSVM_dpmi_segments->int16_sel) { /* Restore original flags stored into the stack by the caller. */ WORD *stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp); context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) ); if (intnum != context->Eip / DOSVM_STUB_PM16) WARN( "interrupt stub has been modified " "(interrupt is %02x, interrupt stub is %02x)\n", intnum, context->Eip/DOSVM_STUB_PM16 ); TRACE( "builtin interrupt %02x has been branched to\n", intnum ); if (intnum == 0x25 || intnum == 0x26) DOSVM_PushFlags( context, FALSE, TRUE ); DOSVM_BuildCallFrame( context, DOSVM_IntProcRelay, DOSVM_GetBuiltinHandler(intnum) ); } else if (wine_ldt_is_system(context->SegCs)) { INTPROC proc; if (intnum >= sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) return FALSE; if (!(proc = DOSVM_VectorsBuiltin[intnum])) return FALSE; proc( context ); } else { DOSVM_HardwareInterruptPM( context, intnum ); } return TRUE; }