Commit 76f93fb6 authored by Jukka Heinonen's avatar Jukka Heinonen Committed by Alexandre Julliard

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.
parent c7827219
......@@ -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 );
......
......@@ -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())
{
/*
......@@ -620,6 +652,27 @@ void WINAPI DOSVM_QueueEvent( INT irq, INT priority, DOSRELAY relay, LPVOID data
/**********************************************************************
* 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)
*/
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
......
......@@ -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 )
......
......@@ -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);
}
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" );
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;
}
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 );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment