diff --git a/dlls/kernel/instr.c b/dlls/kernel/instr.c index 1eacfc6255..2eb6ba5b62 100644 --- a/dlls/kernel/instr.c +++ b/dlls/kernel/instr.c @@ -824,6 +824,7 @@ DWORD INSTR_EmulateInstruction( EXCEPTION_RECORD *rec, CONTEXT86 *context ) context->Eip += prefixlen + 1; if (NtCurrentTeb()->vm86_pending) { + NtCurrentTeb()->vm86_pending = 0; rec->ExceptionCode = EXCEPTION_VM86_STI; break; /* Handle the pending event. */ } diff --git a/dlls/kernel/wowthunk.c b/dlls/kernel/wowthunk.c index d5fedb602b..00baa659cf 100644 --- a/dlls/kernel/wowthunk.c +++ b/dlls/kernel/wowthunk.c @@ -83,6 +83,9 @@ extern void Call16_Ret_Start(), Call16_Ret_End(); extern void CallTo16_Ret(); extern void CALL32_CBClient_Ret(); extern void CALL32_CBClientEx_Ret(); +extern void DPMI_PendingEventCheck(); +extern void DPMI_PendingEventCheck_Cleanup(); +extern void DPMI_PendingEventCheck_Return(); extern DWORD CallTo16_DataSelector; extern SEGPTR CALL32_CBClient_RetAddr; extern SEGPTR CALL32_CBClientEx_RetAddr; @@ -94,6 +97,11 @@ extern void RELAY16_InitDebugLists(void); static LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs ); static SEGPTR call16_ret_addr; /* segptr to CallTo16_Ret routine */ +static WORD dpmi_checker_selector; +static DWORD dpmi_checker_offset_call; +static DWORD dpmi_checker_offset_cleanup; +static DWORD dpmi_checker_offset_return; + /*********************************************************************** * WOWTHUNK_Init */ @@ -114,6 +122,15 @@ BOOL WOWTHUNK_Init(void) CALL32_CBClientEx_RetAddr = MAKESEGPTR( codesel, (char*)CALL32_CBClientEx_Ret - (char*)Call16_Ret_Start ); + /* Prepare selector and offsets for DPMI event checking. */ + dpmi_checker_selector = codesel; + dpmi_checker_offset_call = + (char*)DPMI_PendingEventCheck - (char*)Call16_Ret_Start; + dpmi_checker_offset_cleanup = + (char*)DPMI_PendingEventCheck_Cleanup - (char*)Call16_Ret_Start; + dpmi_checker_offset_return = + (char*)DPMI_PendingEventCheck_Return - (char*)Call16_Ret_Start; + if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY16_InitDebugLists(); /* setup emulation of protected instructions from 32-bit code (only for Win9x versions) */ @@ -164,6 +181,75 @@ static BOOL fix_selector( CONTEXT *context ) } +/************************************************************* + * 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 ); + + 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(); +} + + /************************************************************* * call16_handler * @@ -191,6 +277,15 @@ static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RE 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 */ @@ -212,6 +307,10 @@ static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RE } } } + else if (record->ExceptionCode == EXCEPTION_VM86_STI) + { + insert_event_check( context ); + } return ExceptionContinueSearch; } @@ -547,6 +646,19 @@ BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags, cbArgs += sizeof(SEGPTR); } + /* + * Start call by checking for pending events. + * Note that wine_call_to_16_regs overwrites context stack + * pointer so we may modify it here without a problem. + */ + if (NtCurrentTeb()->dpmi_vif) + { + context->SegSs = wine_get_ds(); + context->Esp = (DWORD)stack; + insert_event_check( context ); + cbArgs += (DWORD)stack - context->Esp; + } + _EnterWin16Lock(); wine_call_to_16_regs( context, cbArgs, call16_handler ); _LeaveWin16Lock(); diff --git a/tools/winebuild/relay.c b/tools/winebuild/relay.c index 87877f3f05..1d8e45e81d 100644 --- a/tools/winebuild/relay.c +++ b/tools/winebuild/relay.c @@ -1043,6 +1043,57 @@ static void BuildCallFrom32Regs( FILE *outfile ) } +/******************************************************************* + * BuildPendingEventCheck + * + * Build a function that checks whether there are any + * pending DPMI events. + * + * Stack layout: + * + * (sp+12) long eflags + * (sp+6) long cs + * (sp+2) long ip + * (sp) word fs + * + * On entry to function, fs register points to a valid TEB. + * On exit from function, stack will be popped. + */ +void BuildPendingEventCheck( FILE *outfile ) +{ + /* Function header */ + + function_header( outfile, "DPMI_PendingEventCheck" ); + + /* Check for pending events. */ + + fprintf( outfile, "\t.byte 0x64\n\ttestl $0xffffffff,(%d)\n", + STRUCTOFFSET(TEB,vm86_pending) ); + fprintf( outfile, "\tje DPMI_PendingEventCheck_Cleanup\n" ); + + fprintf( outfile, "\t.byte 0x64\n\ttestl $0xffffffff,(%d)\n", + STRUCTOFFSET(TEB,dpmi_vif) ); + + fprintf( outfile, "\tje DPMI_PendingEventCheck_Cleanup\n" ); + + /* Process pending events. */ + + fprintf( outfile, "\tsti\n" ); + + /* Start cleanup. Restore fs register. */ + + fprintf( outfile, ".globl DPMI_PendingEventCheck_Cleanup\n" ); + fprintf( outfile, "DPMI_PendingEventCheck_Cleanup:\n" ); + fprintf( outfile, "\tpopw %%fs\n" ); + + /* Return from function. */ + + fprintf( outfile, ".globl DPMI_PendingEventCheck_Return\n" ); + fprintf( outfile, "DPMI_PendingEventCheck_Return:\n" ); + fprintf( outfile, "\tiret\n" ); +} + + /******************************************************************* * BuildRelays16 * @@ -1131,6 +1182,9 @@ void BuildRelays16( FILE *outfile ) /* CBClientThunkSLEx return stub */ BuildCallTo32CBClientRet( outfile, TRUE ); + /* Pending DPMI events check stub */ + BuildPendingEventCheck( outfile ); + /* End of Call16_Ret segment */ fprintf( outfile, "\n\t.globl " __ASM_NAME("Call16_Ret_End") "\n" ); fprintf( outfile, __ASM_NAME("Call16_Ret_End") ":\n" );