diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index 8436ec0bd94..1b0ae8c4653 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -943,6 +943,11 @@ static void set_vm86_pend( CONTEXT *context ) teb->vm86_ptr = NULL; rec.ExceptionAddress = (LPVOID)context->Eip; EXC_RtlRaiseException( &rec, context ); + /* + * FIXME: EXC_RtlRaiseException has unblocked all signals. + * If we receive nested SIGUSR2 here, VM86 event + * handling may lock up! + */ teb->vm86_ptr = vm86; } } @@ -965,10 +970,31 @@ static void set_vm86_pend( CONTEXT *context ) save_vm86_context( &vcontext, vm86 ); rec.ExceptionAddress = (LPVOID)vcontext.Eip; EXC_RtlRaiseException( &rec, &vcontext ); + /* + * FIXME: EXC_RtlRaiseException has unblocked all signals. + * If we receive nested SIGUSR2 here, VM86 event + * handling may lock up! + */ teb->vm86_ptr = vm86; restore_vm86_context( &vcontext, vm86 ); } } + else if(teb->dpmi_vif && + !IS_SELECTOR_SYSTEM(context->SegCs) && + !IS_SELECTOR_SYSTEM(context->SegSs)) + { + /* Executing DPMI code and virtual interrupts are enabled. */ + teb->vm86_pending = 0; + rec.ExceptionAddress = (LPVOID)context->Eip; + EXC_RtlRaiseException( &rec, context ); + /* + * EXC_RtlRaiseException has unblocked all signals and this + * signal handler is about to return to either DOS relay or + * IRQ handler. Because both of these will check pending + * interrupts again, it is not a problem if we receive + * a nested SIGUSR2 here and ignore it. + */ + } } diff --git a/dlls/winedos/himem.c b/dlls/winedos/himem.c index 4cee0a58092..97b02dd83e9 100644 --- a/dlls/winedos/himem.c +++ b/dlls/winedos/himem.c @@ -181,7 +181,8 @@ void DOSVM_InitSegments( void ) static const char relay[]= { 0xca, 0x04, 0x00, /* 16-bit far return and pop 4 bytes (relay void* arg) */ - 0xcd, 0x31 /* int 31 */ + 0xcd, 0x31, /* int 31 */ + 0xfb, 0x66, 0xcb /* sti and 32-bit far return */ }; /* @@ -253,6 +254,7 @@ void DOSVM_InitSegments( void ) /* * PM / offset 0: Stub where __wine_call_from_16_regs returns. * PM / offset 3: Stub which swaps back to 32-bit application code/stack. + * PM / offset 5: Stub which enables interrupts */ ptr = DOSVM_AllocCodeUMB( sizeof(relay), 0, &DOSVM_dpmi_segments->relay_code_sel); diff --git a/dlls/winedos/int31.c b/dlls/winedos/int31.c index 28e93021b82..297960dbc16 100644 --- a/dlls/winedos/int31.c +++ b/dlls/winedos/int31.c @@ -28,7 +28,9 @@ #include "msdos.h" #include "dosexe.h" +#include "excpt.h" #include "wine/debug.h" +#include "wine/exception.h" #include "stackframe.h" #include "toolhelp.h" @@ -78,6 +80,29 @@ BOOL DOSVM_IsDos32(void) } +/********************************************************************** + * dpmi_exception_handler + * + * Handle EXCEPTION_VM86_STI exceptions generated + * when there are pending asynchronous events. + */ +static WINE_EXCEPTION_FILTER(dpmi_exception_handler) +{ + EXCEPTION_RECORD *rec = GetExceptionInformation()->ExceptionRecord; + CONTEXT *context = GetExceptionInformation()->ContextRecord; + + if (rec->ExceptionCode == EXCEPTION_VM86_STI) + { + if (ISV86(context)) + ERR( "Real mode STI caught by protected mode handler!\n" ); + DOSVM_SendQueuedEvents(context); + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + + /********************************************************************** * INT_GetRealModeContext */ @@ -297,10 +322,15 @@ __ASM_GLOBAL_FUNC(DPMI_CallRMCB32, */ static void DPMI_CallRMCBProc( CONTEXT86 *context, RMCB *rmcb, WORD flag ) { + DWORD old_vif = NtCurrentTeb()->dpmi_vif; + + /* Disable virtual interrupts. */ + NtCurrentTeb()->dpmi_vif = 0; + if (IS_SELECTOR_SYSTEM( rmcb->proc_sel )) { /* Wine-internal RMCB, call directly */ ((RMCBPROC)rmcb->proc_ofs)(context); - } else { + } else __TRY { #ifdef __i386__ UINT16 ss,es; DWORD esp,edi; @@ -340,7 +370,10 @@ static void DPMI_CallRMCBProc( CONTEXT86 *context, RMCB *rmcb, WORD flag ) #else ERR("RMCBs only implemented for i386\n"); #endif - } + } __EXCEPT(dpmi_exception_handler) { } __ENDTRY + + /* Restore virtual interrupt flag. */ + NtCurrentTeb()->dpmi_vif = old_vif; } @@ -548,17 +581,36 @@ static void StartPM( CONTEXT86 *context ) TRACE("DOS program is now entering %d-bit protected mode\n", DOSVM_IsDos32() ? 32 : 16); - wine_call_to_16_regs_short(&pm_ctx, 0); + + /* + * Enable interrupts. Note that we also make a dummy + * relay call in order to process all pending events. + * This is needed in order to prevent event handling from + * getting stuck. + */ + NtCurrentTeb()->dpmi_vif = 1; + DOSVM_BuildCallFrame( context, NULL, NULL ); + + __TRY + { + wine_call_to_16_regs_short(&pm_ctx, 0); + } + __EXCEPT(dpmi_exception_handler) + { + } + __ENDTRY /* in the current state of affairs, we won't ever actually return here... */ /* we should have int21/ah=4c do it someday, though... */ +#if 0 FreeSelector16(psp->environment); psp->environment = env_seg; FreeSelector16(es); if (ds != ss) FreeSelector16(ds); FreeSelector16(ss); FreeSelector16(cs); +#endif } static RMCB *DPMI_AllocRMCB( void ) diff --git a/dlls/winedos/interrupts.c b/dlls/winedos/interrupts.c index 11973836fa8..15370adc90e 100644 --- a/dlls/winedos/interrupts.c +++ b/dlls/winedos/interrupts.c @@ -22,6 +22,8 @@ #include "wine/debug.h" #include "wine/winbase16.h" +#include "thread.h" + #ifdef HAVE_SYS_VM86_H # include #endif @@ -80,6 +82,23 @@ static const INTPROC DOSVM_VectorsBuiltin[] = #define DOSVM_STUB_PM48 6 +/********************************************************************** + * DOSVM_IsIRQ + * + * Return TRUE if interrupt is an IRQ. + */ +static BOOL DOSVM_IsIRQ( BYTE intnum ) +{ + if (intnum >= 0x08 && intnum <= 0x0f) + return TRUE; + + if (intnum >= 0x70 && intnum <= 0x77) + return TRUE; + + return FALSE; +} + + /********************************************************************** * DOSVM_DefaultHandler * @@ -105,6 +124,10 @@ static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum ) } WARN("int%x not implemented, returning dummy handler\n", intnum ); + + if (DOSVM_IsIRQ(intnum)) + return DOSVM_AcknowledgeIRQ; + return DOSVM_DefaultHandler; } @@ -121,6 +144,33 @@ static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data ) } +/********************************************************************** + * DOSVM_PrepareIRQ + * + */ +static void DOSVM_PrepareIRQ( CONTEXT86 *context, BOOL isbuiltin ) +{ + /* Disable virtual interrupts. */ + NtCurrentTeb()->dpmi_vif = 0; + + if (!isbuiltin) + { + DWORD *stack = CTX_SEG_OFF_TO_LIN(context, + context->SegSs, + context->Esp); + + /* Push return address to stack. */ + *(--stack) = context->SegCs; + *(--stack) = context->Eip; + context->Esp += -8; + + /* Jump to enable interrupts stub. */ + context->SegCs = DOSVM_dpmi_segments->relay_code_sel; + context->Eip = 5; + } +} + + /********************************************************************** * DOSVM_PushFlags * @@ -270,6 +320,8 @@ void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) if (intnum == 0x25 || intnum == 0x26) DOSVM_PushFlags( context, TRUE, FALSE ); + else if (DOSVM_IsIRQ(intnum)) + DOSVM_PrepareIRQ( context, TRUE ); DOSVM_BuildCallFrame( context, DOSVM_IntProcRelay, @@ -278,14 +330,16 @@ void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) } else { - DWORD *stack = CTX_SEG_OFF_TO_LIN(context, - context->SegSs, - context->Esp); + DWORD *stack; TRACE( "invoking hooked interrupt %02x at %04x:%08lx\n", intnum, addr.selector, addr.offset ); + if (DOSVM_IsIRQ(intnum)) + DOSVM_PrepareIRQ( context, FALSE ); + /* Push the flags and return address on the stack */ + stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp); *(--stack) = context->EFlags; *(--stack) = context->SegCs; *(--stack) = context->Eip; @@ -308,7 +362,9 @@ void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) if (intnum == 0x25 || intnum == 0x26) DOSVM_PushFlags( context, FALSE, FALSE ); - + else if (DOSVM_IsIRQ(intnum)) + DOSVM_PrepareIRQ( context, TRUE ); + DOSVM_BuildCallFrame( context, DOSVM_IntProcRelay, DOSVM_GetBuiltinHandler( @@ -316,14 +372,16 @@ void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) } else { - WORD *stack = CTX_SEG_OFF_TO_LIN(context, - context->SegSs, - context->Esp); + WORD *stack; TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", intnum, SELECTOROF(addr), OFFSETOF(addr) ); + if (DOSVM_IsIRQ(intnum)) + DOSVM_PrepareIRQ( context, FALSE ); + /* Push the flags and return address on the stack */ + stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp); *(--stack) = LOWORD(context->EFlags); *(--stack) = context->SegCs; *(--stack) = LOWORD(context->Eip); @@ -537,6 +595,12 @@ void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler ) */ void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) { + /* + * FIXME: Make all builtin interrupt calls go via this routine. + * FIXME: Check for PM->RM interrupt reflection. + * FIXME: Check for RM->PM interrupt reflection. + */ + INTPROC proc = DOSVM_GetBuiltinHandler( intnum ); proc( context ); } diff --git a/memory/instr.c b/memory/instr.c index 4beb0fb342e..380ae6cc5d3 100644 --- a/memory/instr.c +++ b/memory/instr.c @@ -26,6 +26,8 @@ #include "selectors.h" #include "wine/debug.h" #include "callback.h" +#include "thread.h" +#include "wine/exception.h" WINE_DEFAULT_DEBUG_CHANNEL(int); WINE_DECLARE_DEBUG_CHANNEL(io); @@ -793,12 +795,19 @@ DWORD INSTR_EmulateInstruction( CONTEXT86 *context ) context->Eip += prefixlen + 1; return 0; - case 0xfa: /* cli, ignored */ + case 0xfa: /* cli */ + NtCurrentTeb()->dpmi_vif = 0; context->Eip += prefixlen + 1; return 0; - case 0xfb: /* sti, ignored */ + case 0xfb: /* sti */ + NtCurrentTeb()->dpmi_vif = 1; context->Eip += prefixlen + 1; + if (NtCurrentTeb()->vm86_pending) + { + NtCurrentTeb()->vm86_pending = 0; + return EXCEPTION_VM86_STI; + } return 0; }