/*++ Copyright (c) 1992 Microsoft Corporation Module Name: int.c Abstract: This file contains interrupt support routines for the monitor Author: Dave Hastings (daveh) 18-Apr-1992 Notes: The code in this file split out from monitor.c (18-Apr-1992) Revision History: --*/ #include BOOL DpmiHwIntHandler( ULONG IntNumber ); VOID IRQ13_Eoi( int IrqLine, int CallCount ); BOOLEAN IRQ13BeingHandled; // true until IRQ13 eoi'ed VOID InterruptInit( VOID ) /*++ Routine Description: This routine initializes the interrupt code for the monitor. Arguments: Return Value: None. --*/ { BOOL Bool; Bool = RegisterEOIHook( 13, IRQ13_Eoi); if (!Bool) { #if DBG DbgPrint("NtVdm : Could not register IRQ 13 Eoi handler\n"); DbgBreakPoint(); #endif TerminateVDM(); } } VOID InterruptTerminate( VOID ) /*++ Routine Description: This routine frees the resoures allocated by InterruptInit Arguments: Return Value: None. --*/ { } VOID cpu_interrupt( IN int Type, IN int Number ) /*++ Routine Description: This routine causes an interrupt of the specified type to be raised at the appropriate time. Arguments: Type -- indicates the type of the interrupt. One of HARDWARE, TIMER, YODA, or RESET YODA and RESET are ignored Return Value: None. Notes: --*/ { NTSTATUS Status; HANDLE MonitorThread; host_ica_lock(); if (Type == CPU_TIMER_TICK) { // // Set the VDM State for timer tick int pending // _asm { mov eax, FIXED_NTVDMSTATE_LINEAR lock or dword ptr [eax], VDM_INT_TIMER } } else if (Type == CPU_HW_INT) { if (*pNtVDMState & VDM_INT_HARDWARE) { goto EarlyExit; } // // Set the VDM State for Hardware Int pending // _asm { mov eax, FIXED_NTVDMSTATE_LINEAR lock or dword ptr [eax], VDM_INT_HARDWARE } } else { #if DBG DbgPrint("Monitor: Invalid Interrupt Type=%ld\n",Type); #endif goto EarlyExit; } if (CurrentMonitorTeb != NtCurrentTeb()) { /* * Look up the ThreadHandle and Queue and InterruptApc * If no ThreadHandle found do nothing * * The CurrentMonitorTeb may not be in the ThreadHandle\Teb list * because upon task termination the the CurrentMonitorTeb variable * cannot be updated until a new task is activated by the * non-preemptive scheduler. */ MonitorThread = ThreadLookUp(CurrentMonitorTeb); if (MonitorThread) { Status = NtVdmControl(VdmQueueInterrupt, (PVOID)MonitorThread); // nothing much we can do if this fails #if DBG if (!NT_SUCCESS(Status) && Status != STATUS_UNSUCCESSFUL) { DbgPrint("NtVdmControl.VdmQueueInterrupt Status=%lx\n",Status); } #endif } } EarlyExit: host_ica_unlock(); } VOID DispatchInterrupts( ) /*++ Routine Description: This routine dispatches interrupts to their appropriate handler routine in priority order. The order is YODA, RESET, TIMER, HARDWARE. however the YODA and RESET interrupts do nothing. Hardware interrupts are not simulated unless the virtual interrupt enable flag is set. Flags indicating which interrupts are pending appear in the pNtVDMState. Arguments: None. Return Value: None. Notes: --*/ { host_ica_lock(); // If any delayed interrupts have expired // call the ica to restart interrupts if (UndelayIrqLine) { ica_RestartInterrupts(UndelayIrqLine); } if (*pNtVDMState & VDM_INT_TIMER) { *pNtVDMState &= ~VDM_INT_TIMER; host_ica_unlock(); // maybe don't need to unlock ? Jonle host_timer_event(); host_ica_lock(); } if ( getIF() && getMSW() & MSW_PE && *pNtVDMState & VDM_INT_HARDWARE) { // // Mark the vdm state as hw int dispatched. Must use the lock as // kernel mode DelayedIntApcRoutine changes the bit as well // _asm { mov eax,FIXED_NTVDMSTATE_LINEAR lock and dword ptr [eax], NOT VDM_INT_HARDWARE } DispatchHwInterrupt(); } host_ica_unlock(); } VOID DispatchHwInterrupt( ) /*++ Routine Description: This routine dispatches hardware interrupts to the vdm in Protect Mode. It calls the ICA to get the vector number and sets up the VDM stack appropriately. Real Mode interrupt dispatching has been moved to the kernel. Arguments: None. Return Value: None. --*/ { int InterruptNumber; ULONG IretHookAddress = 0L; InterruptNumber = ica_intack(&IretHookAddress); if (InterruptNumber == -1) { // skip spurious ints return; } DpmiHwIntHandler(InterruptNumber); if (IretHookAddress) { BOOL Frame32 = (BOOL) VdmTib.PmStackInfo.Flags; BOOL Stack32; USHORT SegSs, VdmCs; ULONG VdmSp, VdmEip; PUCHAR VdmStackPointer; ULONG StackOffset; SegSs = getSS(); VdmStackPointer = Sim32GetVDMPointer(((ULONG)SegSs) << 16, 1, TRUE); // // Figure out how many bits of sp to use // if (Ldt[(SegSs & ~0x7)/sizeof(LDT_ENTRY)].HighWord.Bits.Default_Big) { VdmSp = getESP(); StackOffset = 12; } else { VdmSp = getSP(); StackOffset = 6; } (PCHAR)VdmStackPointer += VdmSp; // // BUGBUG need to add stack limit checking 15-Nov-1993 Jonle // setESP(VdmSp - StackOffset); // // Push info for Iret hook handler // VdmCs = (USHORT) ((IretHookAddress & 0xFFFF0000) >> 16); VdmEip = (IretHookAddress & 0xFFFF); if (Frame32) { *(PULONG)(VdmStackPointer - 4) = VdmTib.VdmContext.EFlags; *(PULONG)(VdmStackPointer - 8) = (ULONG) VdmCs; *(PULONG)(VdmStackPointer - 12) = VdmEip; } else { *(PUSHORT)(VdmStackPointer - 2) = (USHORT) VdmTib.VdmContext.EFlags; *(PUSHORT)(VdmStackPointer - 4) = VdmCs; *(PUSHORT)(VdmStackPointer - 6) = (USHORT) VdmEip; } } } VOID IRQ13_Eoi( int IrqLine, int CallCount ) { UNREFERENCED_PARAMETER(IrqLine); UNREFERENCED_PARAMETER(CallCount); // // if CallCount is less than Zero, then the interrupt request // is being canceled. // if (CallCount < 0) { return; } IRQ13BeingHandled = FALSE; } VOID MonitorEndIretHook( VOID ) /*++ Routine Description: Arguments: None. Return Value: None. --*/ { PVOID VdmStackPointer; if (IntelMSW & MSW_PE) { BOOL Frame32 = (BOOL) VdmTib.PmStackInfo.Flags; ULONG FrameSize; if (Frame32) { FrameSize = 12; } else { FrameSize = 6; } VdmStackPointer = Sim32GetVDMPointer(((ULONG)getSS() << 16),2,TRUE); if (Ldt[(getSS() & ~0x7)/sizeof(LDT_ENTRY)].HighWord.Bits.Default_Big) { (PCHAR)VdmStackPointer += getESP(); setESP(getESP() + FrameSize); } else { (PCHAR)VdmStackPointer += getSP(); setSP((USHORT) (getSP() + FrameSize)); } if (Frame32) { VdmTib.VdmContext.EFlags = *(PULONG)((PCHAR)VdmStackPointer + 8); setCS(*(PUSHORT)((PCHAR)VdmStackPointer + 4)); VdmTib.VdmContext.Eip = *((PULONG)VdmStackPointer); } else { VdmTib.VdmContext.EFlags = (VdmTib.VdmContext.EFlags & 0xFFFF0000) | ((ULONG) *(PUSHORT)((PCHAR)VdmStackPointer + 4)); setCS(*(PUSHORT)((PCHAR)VdmStackPointer + 2)); VdmTib.VdmContext.Eip = (VdmTib.VdmContext.Eip & 0xFFFF0000) | ((ULONG) *(PUSHORT)((PCHAR)VdmStackPointer)); } } else { VdmStackPointer = Sim32GetVDMPointer(((ULONG)getSS() << 16) | getSP(),2,FALSE); setSP((USHORT) (getSP() + 6)); (USHORT)(VdmTib.VdmContext.EFlags) = *((PUSHORT)((PCHAR)VdmStackPointer + 4)); setCS(*((PUSHORT)((PCHAR)VdmStackPointer + 2))); setIP(*((PUSHORT)VdmStackPointer)); } } VOID host_clear_hw_int() /*++ Routine Description: This routine "forgets" a previously requested hardware interrupt. Arguments: None. Return Value: None. --*/ { /* * We do nothing here to save a kernel call, because the * interrupt if it hasn't been intacked yet or dispatched, * will produce a harmless spurious int, which is dropped * in the i386 interrupt dispatching code anyhow. */ }