From 76f93fb6bc661ebc3b63b2f4b1202fbe710d1dc4 Mon Sep 17 00:00:00 2001 From: Jukka Heinonen Date: Mon, 23 Jun 2003 03:35:51 +0000 Subject: [PATCH] Add IRQ acknowledge handler for internal IRQs. Change DPMI asynchronous event handling. Always use alternate stack in DPMI relays and check for pending events after original stack has been restored. --- dlls/winedos/dosexe.h | 2 ++ dlls/winedos/dosvm.c | 75 ++++++++++++++++++++++++++++++++++++------- dlls/winedos/int09.c | 3 +- dlls/winedos/relay.c | 73 ++++++++++++++++++++++------------------- 4 files changed, 108 insertions(+), 45 deletions(-) diff --git a/dlls/winedos/dosexe.h b/dlls/winedos/dosexe.h index efe7aab3c73..19f601a2a58 100644 --- a/dlls/winedos/dosexe.h +++ b/dlls/winedos/dosexe.h @@ -105,6 +105,8 @@ extern void WINAPI MZ_RunInThread( PAPCFUNC proc, ULONG_PTR arg ); extern BOOL DOSVM_IsWin16(void); /* dosvm.c */ +extern void DOSVM_SendQueuedEvents( CONTEXT86 * ); +extern void WINAPI DOSVM_AcknowledgeIRQ( CONTEXT86 * ); extern INT WINAPI DOSVM_Enter( CONTEXT86 *context ); extern void WINAPI DOSVM_Wait( CONTEXT86 * ); extern DWORD WINAPI DOSVM_Loop( HANDLE hThread ); diff --git a/dlls/winedos/dosvm.c b/dlls/winedos/dosvm.c index be7c328910d..c19071748e9 100644 --- a/dlls/winedos/dosvm.c +++ b/dlls/winedos/dosvm.c @@ -159,9 +159,23 @@ static void DOSVM_SendOneEvent( CONTEXT86 *context ) /* Callback event. */ TRACE( "Dispatching callback event.\n" ); - LeaveCriticalSection(&qcrit); - (*event->relay)( context, event->data ); - EnterCriticalSection(&qcrit); + if (ISV86(context)) + { + /* + * Call relay immediately in real mode. + */ + LeaveCriticalSection(&qcrit); + (*event->relay)( context, event->data ); + EnterCriticalSection(&qcrit); + } + else + { + /* + * Force return to relay code. We do not want to + * call relay directly because we may be inside a signal handler. + */ + DOSVM_BuildCallFrame( context, event->relay, event->data ); + } free(event); } @@ -171,23 +185,30 @@ static void DOSVM_SendOneEvent( CONTEXT86 *context ) /*********************************************************************** * DOSVM_SendQueuedEvents * - * As long as interrupts are enabled, process all pending events - * that are not blocked by currently active event. + * As long as context instruction pointer stays unmodified, + * process all pending events that are not blocked by currently + * active event. + * + * This routine assumes that caller has already cleared TEB.vm86_pending + * and checked that interrupts are enabled. */ -static void DOSVM_SendQueuedEvents( CONTEXT86 *context ) -{ +void DOSVM_SendQueuedEvents( CONTEXT86 *context ) +{ + DWORD old_cs = context->SegCs; + DWORD old_ip = context->Eip; + EnterCriticalSection(&qcrit); TRACE( "Called in %s mode %s events pending (time=%ld)\n", ISV86(context) ? "real" : "protected", DOSVM_HasPendingEvents() ? "with" : "without", GetTickCount() ); - TRACE( "cs:ip=%04lx:%08lx, ss:sp=%04lx:%08lx\n", + TRACE( "cs:ip=%04lx:%08lx, ss:sp=%04lx:%08lx\n", context->SegCs, context->Eip, context->SegSs, context->Esp); - while (DOSVM_HasPendingEvents() && - (ISV86(context) ? - (context->EFlags & VIF_MASK) : NtCurrentTeb()->dpmi_vif)) + while (context->SegCs == old_cs && + context->Eip == old_ip && + DOSVM_HasPendingEvents()) { DOSVM_SendOneEvent(context); @@ -199,6 +220,17 @@ static void DOSVM_SendQueuedEvents( CONTEXT86 *context ) NtCurrentTeb()->vm86_pending = 0; } + if (!ISV86(context) && context->SegCs == old_cs && context->Eip == old_ip) + { + /* + * Routine was called from DPMI but there was nothing to do. + * We force a dummy relay call here so that we don't get a race + * if signals are unblocked when we return to DPMI application. + */ + TRACE( "Called but there was nothing to do, calling NULL relay.\n" ); + DOSVM_BuildCallFrame( context, NULL, NULL ); + } + if (DOSVM_HasPendingEvents()) { /* @@ -619,6 +651,27 @@ void WINAPI DOSVM_QueueEvent( INT irq, INT priority, DOSRELAY relay, LPVOID data #endif +/********************************************************************** + * DOSVM_AcknowledgeIRQ + * + * This routine should be called by all internal IRQ handlers. + */ +void WINAPI DOSVM_AcknowledgeIRQ( CONTEXT86 *context ) +{ + /* + * Send EOI to PIC. + */ + DOSVM_PIC_ioport_out( 0x20, 0x20 ); + + /* + * Protected mode IRQ handlers are supposed + * to turn VIF flag on before they return. + */ + if (!ISV86(context)) + NtCurrentTeb()->dpmi_vif = 1; +} + + /********************************************************************** * DllMain (DOSVM.Init) */ diff --git a/dlls/winedos/int09.c b/dlls/winedos/int09.c index 937c7a3709b..9bf0c12e17f 100644 --- a/dlls/winedos/int09.c +++ b/dlls/winedos/int09.c @@ -206,7 +206,8 @@ void WINAPI DOSVM_Int09Handler( CONTEXT86 *context ) DOSVM_Int16AddChar(0, scan); } } - DOSVM_PIC_ioport_out( 0x20, 0x20 ); /* send EOI */ + + DOSVM_AcknowledgeIRQ( context ); } static void KbdRelay( CONTEXT86 *context, void *data ) diff --git a/dlls/winedos/relay.c b/dlls/winedos/relay.c index 85978648393..fa609ad2901 100644 --- a/dlls/winedos/relay.c +++ b/dlls/winedos/relay.c @@ -31,8 +31,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(int); #define RELAY_MAGIC 0xabcdef00 /* - * Memory block for temporary 16-bit stacks used when - * 32-bit code calls relay. + * Memory block for temporary 16-bit stacks used with relay calls. */ typedef struct { DWORD inuse; /* non-zero if stack block is in use */ @@ -62,9 +61,8 @@ static RELAY_Stack16 *RELAY_GetPointer( DWORD offset ) /********************************************************************** * RELAY_MakeShortContext * - * If context is using 32-bit stack or code segment, allocate - * 16-bit stack, make stack pointer point to this stack and - * make code pointer point to stub that restores everything. + * Allocate separate 16-bit stack, make stack pointer point to this + * stack and make code pointer point to stub that restores everything. * So, after this routine, SS and CS are guaranteed to be 16-bit. * * Note: This might be called from signal handler, so the stack @@ -72,33 +70,30 @@ static RELAY_Stack16 *RELAY_GetPointer( DWORD offset ) */ static void RELAY_MakeShortContext( CONTEXT86 *context ) { - if (IS_SELECTOR_32BIT(context->SegCs) || IS_SELECTOR_32BIT(context->SegSs)) - { - DWORD offset = offsetof(RELAY_Stack16, stack_top); - RELAY_Stack16 *stack = RELAY_GetPointer( 0 ); + DWORD offset = offsetof(RELAY_Stack16, stack_top); + RELAY_Stack16 *stack = RELAY_GetPointer( 0 ); - while (stack->inuse && offset < DOSVM_RELAY_DATA_SIZE) { - stack++; - offset += sizeof(RELAY_Stack16); - } - - if (offset >= DOSVM_RELAY_DATA_SIZE) - ERR( "Too many nested interrupts!\n" ); - - stack->inuse = 1; - stack->eip = context->Eip; - stack->seg_cs = context->SegCs; - stack->esp = context->Esp; - stack->seg_ss = context->SegSs; - - stack->stack_bottom = RELAY_MAGIC; - stack->stack_top = RELAY_MAGIC; - - context->SegSs = DOSVM_dpmi_segments->relay_data_sel; - context->Esp = offset; - context->SegCs = DOSVM_dpmi_segments->relay_code_sel; - context->Eip = 3; + while (stack->inuse && offset < DOSVM_RELAY_DATA_SIZE) { + stack++; + offset += sizeof(RELAY_Stack16); } + + if (offset >= DOSVM_RELAY_DATA_SIZE) + ERR( "Too many nested interrupts!\n" ); + + stack->inuse = 1; + stack->eip = context->Eip; + stack->seg_cs = context->SegCs; + stack->esp = context->Esp; + stack->seg_ss = context->SegSs; + + stack->stack_bottom = RELAY_MAGIC; + stack->stack_top = RELAY_MAGIC; + + context->SegSs = DOSVM_dpmi_segments->relay_data_sel; + context->Esp = offset; + context->SegCs = DOSVM_dpmi_segments->relay_code_sel; + context->Eip = 3; } @@ -112,7 +107,8 @@ static void __stdcall RELAY_RelayStub( DOSRELAY proc, unsigned char *args, void *context ) { - proc( (CONTEXT86*)context, *(LPVOID *)args ); + if (proc) + proc( (CONTEXT86*)context, *(LPVOID *)args ); } @@ -136,6 +132,17 @@ void DOSVM_RelayHandler( CONTEXT86 *context ) ERR( "Stack corrupted!\n" ); stack->inuse = 0; + + /* + * We have now restored original stack and instruction pointers. + * Because signals are blocked here, this is a safe place to + * check for pending events before we return to application context. + */ + if (NtCurrentTeb()->vm86_pending && NtCurrentTeb()->dpmi_vif) + { + NtCurrentTeb()->vm86_pending = 0; + DOSVM_SendQueuedEvents( context ); + } } @@ -182,7 +189,7 @@ void DOSVM_SaveCallFrame( CONTEXT86 *context, STACK16FRAME *frame ) void DOSVM_RestoreCallFrame( CONTEXT86 *context, STACK16FRAME *frame ) { /* - * Make sure that CS and SS are 16-bit. + * Allocate separate stack for relay call. */ RELAY_MakeShortContext( context ); @@ -226,7 +233,7 @@ void DOSVM_BuildCallFrame( CONTEXT86 *context, DOSRELAY relay, LPVOID data ) WORD code_sel = DOSVM_dpmi_segments->relay_code_sel; /* - * Make sure that CS and SS are 16-bit. + * Allocate separate stack for relay call. */ RELAY_MakeShortContext( context );