You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2450 lines
65 KiB
2450 lines
65 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vdmints.c
|
|
|
|
Abstract:
|
|
|
|
Vdm kernel Virtual interrupt support
|
|
|
|
Author:
|
|
|
|
13-Oct-1993 Jonathan Lew (Jonle)
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "vdmp.h"
|
|
#include <ntos.h>
|
|
#include <zwapi.h>
|
|
|
|
//
|
|
// Define thread priority boost for vdm hardware interrupt.
|
|
//
|
|
|
|
#define VDM_HWINT_INCREMENT EVENT_INCREMENT
|
|
|
|
//
|
|
// internal function prototypes
|
|
//
|
|
|
|
VOID
|
|
VdmpQueueIntApcRoutine (
|
|
IN PKAPC Apc,
|
|
IN PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN PVOID *NormalContext,
|
|
IN PVOID *SystemArgument1,
|
|
IN PVOID *SystemArgument2
|
|
);
|
|
|
|
VOID
|
|
VdmpQueueIntNormalRoutine (
|
|
IN PVOID NormalContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
);
|
|
|
|
VOID
|
|
VdmpDelayIntDpcRoutine (
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
);
|
|
|
|
VOID
|
|
VdmpDelayIntApcRoutine (
|
|
IN PKAPC Apc,
|
|
IN PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN PVOID *NormalContext,
|
|
IN PVOID *SystemArgument1,
|
|
IN PVOID *SystemArgument2
|
|
);
|
|
|
|
int
|
|
VdmpRestartDelayedInterrupts (
|
|
PVDMICAUSERDATA pIcaUserData
|
|
);
|
|
|
|
int
|
|
VdmpIcaScan (
|
|
PVDMICAUSERDATA pIcaUserData,
|
|
PVDMVIRTUALICA pIcaAdapter
|
|
);
|
|
|
|
int
|
|
VdmpIcaAccept (
|
|
PVDMICAUSERDATA pIcaUserData,
|
|
PVDMVIRTUALICA pIcaAdapter
|
|
);
|
|
|
|
ULONG
|
|
GetIretHookAddress (
|
|
PKTRAP_FRAME TrapFrame,
|
|
PVDMICAUSERDATA pIcaUserData,
|
|
int IrqNum
|
|
);
|
|
|
|
VOID
|
|
PushRmInterrupt (
|
|
PKTRAP_FRAME TrapFrame,
|
|
ULONG IretHookAddress,
|
|
PVDM_TIB VdmTib,
|
|
ULONG InterruptNumber
|
|
);
|
|
|
|
NTSTATUS
|
|
PushPmInterrupt (
|
|
PKTRAP_FRAME TrapFrame,
|
|
ULONG IretHookAddress,
|
|
PVDM_TIB VdmTib,
|
|
ULONG InterruptNumber
|
|
);
|
|
|
|
VOID
|
|
VdmpRundownRoutine (
|
|
IN PKAPC Apc
|
|
);
|
|
|
|
NTSTATUS
|
|
VdmpEnterIcaLock (
|
|
IN PRTL_CRITICAL_SECTION pIcaLock,
|
|
IN PLARGE_INTEGER Timeout
|
|
);
|
|
|
|
NTSTATUS
|
|
VdmpLeaveIcaLock (
|
|
IN PRTL_CRITICAL_SECTION pIcaLock
|
|
);
|
|
|
|
int
|
|
VdmpExceptionHandler (
|
|
IN PEXCEPTION_POINTERS ExceptionInfo
|
|
);
|
|
|
|
#pragma alloc_text(PAGE, VdmpQueueIntNormalRoutine)
|
|
#pragma alloc_text(PAGE, VdmDispatchInterrupts)
|
|
#pragma alloc_text(PAGE, VdmpRestartDelayedInterrupts)
|
|
#pragma alloc_text(PAGE, VdmpIcaScan)
|
|
#pragma alloc_text(PAGE, VdmpIcaAccept)
|
|
#pragma alloc_text(PAGE, GetIretHookAddress)
|
|
#pragma alloc_text(PAGE, PushRmInterrupt)
|
|
#pragma alloc_text(PAGE, PushPmInterrupt)
|
|
#pragma alloc_text(PAGE, VdmpDispatchableIntPending)
|
|
#pragma alloc_text(PAGE, VdmpIsThreadTerminating)
|
|
#pragma alloc_text(PAGE, VdmpRundownRoutine)
|
|
#pragma alloc_text(PAGE, VdmpExceptionHandler)
|
|
#pragma alloc_text(PAGE, VdmpEnterIcaLock)
|
|
#pragma alloc_text(PAGE, VdmpLeaveIcaLock)
|
|
|
|
extern POBJECT_TYPE ExSemaphoreObjectType;
|
|
extern POBJECT_TYPE ExEventObjectType;
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Make this variable nonzero to enable stricter ntvdm checking. Note this
|
|
// cannot be left on by default because a malicious app can provoke the asserts.
|
|
//
|
|
|
|
ULONG VdmStrict;
|
|
#endif
|
|
|
|
NTSTATUS
|
|
VdmpQueueInterrupt(
|
|
IN HANDLE ThreadHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queues a user mode APC to the specifed application thread
|
|
which will dispatch an interrupt.
|
|
|
|
if APC is already queued to specified thread
|
|
does nothing
|
|
|
|
if APC is queued to the wrong thread
|
|
dequeue it
|
|
|
|
Reset the user APC for the specifed thread
|
|
|
|
Insert the APC in the queue for the specifed thread
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - handle of thread to insert QueueIntApcRoutine
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KIRQL OldIrql;
|
|
PEPROCESS Process;
|
|
PETHREAD Thread;
|
|
NTSTATUS Status;
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_QUERY_INFORMATION,
|
|
PsThreadType,
|
|
KeGetPreviousMode(),
|
|
&Thread,
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Process = PsGetCurrentProcess();
|
|
if (Process != Thread->ThreadsProcess || Process->VdmObjects == NULL) {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Insert kernel APC.
|
|
//
|
|
// N.B. The delay interrupt lock is used to synchronize access to APC
|
|
// objects that are manipulated by VDM.
|
|
//
|
|
|
|
pVdmObjects = Process->VdmObjects;
|
|
ExAcquireSpinLock(&pVdmObjects->DelayIntSpinLock, &OldIrql);
|
|
if (!KeVdmInsertQueueApc(&pVdmObjects->QueuedIntApc,
|
|
&Thread->Tcb,
|
|
KernelMode,
|
|
VdmpQueueIntApcRoutine,
|
|
VdmpRundownRoutine,
|
|
VdmpQueueIntNormalRoutine, // normal routine
|
|
(PVOID)KernelMode, // NormalContext
|
|
VDM_HWINT_INCREMENT))
|
|
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
|
|
}
|
|
|
|
ObDereferenceObject(Thread);
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
VdmpQueueIntApcRoutine (
|
|
IN PKAPC Apc,
|
|
IN PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN PVOID *NormalContext,
|
|
IN PVOID *SystemArgument1,
|
|
IN PVOID *SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Kernel and User mode Special Apc routine to dispatch virtual
|
|
interrupts to the vdm.
|
|
|
|
For KernelMode routine:
|
|
if vdm is running in application mode
|
|
queue a UserModeApc to the same thread
|
|
else do nothing
|
|
|
|
For UserMode routine
|
|
if vdm is running in application mode dispatch virtual interrupts
|
|
else do nothing
|
|
|
|
Arguments:
|
|
|
|
Apc - Supplies a pointer to the APC object used to invoke this routine.
|
|
|
|
NormalRoutine - Supplies a pointer to a pointer to the normal routine
|
|
function that was specified when the APC was initialized.
|
|
|
|
NormalContext - Supplies a pointer to the processor mode
|
|
specifying that this is a Kernel Mode or UserMode apc
|
|
|
|
SystemArgument1 -
|
|
|
|
SystemArgument2 - NOT USED
|
|
Supplies a set of two pointers to two arguments that contain
|
|
untyped data.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG VdmState;
|
|
KIRQL OldIrql;
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
PKTRAP_FRAME TrapFrame;
|
|
PVDM_TIB VdmTib;
|
|
BOOLEAN AppMode;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER (SystemArgument1);
|
|
UNREFERENCED_PARAMETER (SystemArgument2);
|
|
|
|
//
|
|
// Clear address of thread object in APC object.
|
|
//
|
|
// N.B. The delay interrupt lock is used to synchronize access to APC
|
|
// objects that are manipulated by VDM.
|
|
//
|
|
|
|
pVdmObjects = PsGetCurrentProcess()->VdmObjects;
|
|
ExAcquireSpinLock(&pVdmObjects->DelayIntSpinLock, &OldIrql);
|
|
KeVdmClearApcThreadAddress(Apc);
|
|
ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
|
|
|
|
//
|
|
// Get the trap frame for the current thread if it is not terminating.
|
|
//
|
|
|
|
Thread = PsGetCurrentThread();
|
|
if (PsIsThreadTerminating(Thread)) {
|
|
return;
|
|
}
|
|
|
|
TrapFrame = VdmGetTrapFrame(&Thread->Tcb);
|
|
AppMode = (BOOLEAN)(TrapFrame->EFlags & EFLAGS_V86_MASK ||
|
|
TrapFrame->SegCs != (KGDT_R3_CODE | RPL_MASK));
|
|
|
|
try {
|
|
|
|
//
|
|
// If we are in the middle of screen switch, send the main thread
|
|
// back to the monitor context to be suspended there.
|
|
//
|
|
if (*(KPROCESSOR_MODE *)NormalContext == UserMode) {
|
|
if (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_HANDSHAKE && AppMode) {
|
|
Status = VdmpGetVdmTib(&VdmTib);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
*FIXED_NTVDMSTATE_LINEAR_PC_AT = *FIXED_NTVDMSTATE_LINEAR_PC_AT & ~VDM_HANDSHAKE;
|
|
VdmTib->EventInfo.Event = VdmHandShakeAck;
|
|
VdmTib->EventInfo.InstructionSize = 0;
|
|
VdmTib->EventInfo.IntAckInfo = 0;
|
|
VdmEndExecution(TrapFrame, VdmTib);
|
|
KeBoostPriorityThread (KeGetCurrentThread(), VDM_HWINT_INCREMENT);
|
|
}
|
|
return;
|
|
}
|
|
} else if (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_HANDSHAKE && AppMode) {
|
|
|
|
//
|
|
// If we are running at application mode and in the middle of screen
|
|
// switch, we will signal the event to let screen switch continue.
|
|
// This is fine because:
|
|
// 1. The incoming user apc will send the main thread back to monitor context
|
|
// 2. The kernel mode IO handlers are running at APC level. If we can get
|
|
// here, the IO handlers are done
|
|
//
|
|
HANDLE SuspendedHandle;
|
|
PKEVENT SuspendedEvent;
|
|
|
|
try {
|
|
SuspendedHandle = *pVdmObjects->pIcaUserData->phMainThreadSuspended;
|
|
Status = ObReferenceObjectByHandle (SuspendedHandle,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
UserMode,
|
|
&SuspendedEvent,
|
|
NULL);
|
|
if (NT_SUCCESS(Status)) {
|
|
KeSetEvent(SuspendedEvent, EVENT_INCREMENT, FALSE);
|
|
ObDereferenceObject(SuspendedEvent);
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
}
|
|
|
|
//
|
|
// if no pending interrupts, ignore this APC.
|
|
//
|
|
|
|
if (!(*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INTERRUPT_PENDING)) {
|
|
return;
|
|
}
|
|
|
|
if (VdmpDispatchableIntPending(TrapFrame->EFlags)) {
|
|
|
|
//
|
|
// if we are in v86 mode or segmented protected mode
|
|
// then queue the UserMode Apc, which will dispatch
|
|
// the hardware interrupt
|
|
//
|
|
|
|
if ((TrapFrame->EFlags & EFLAGS_V86_MASK) ||
|
|
(TrapFrame->SegCs != (KGDT_R3_CODE | RPL_MASK))) {
|
|
|
|
if (*(KPROCESSOR_MODE *)NormalContext == KernelMode) {
|
|
|
|
//
|
|
// Insert user APC.
|
|
//
|
|
// N.B. The delay interrupt lock is used to synchronize
|
|
// access to APC objects that are manipulated by VDM.
|
|
//
|
|
|
|
VdmState = *FIXED_NTVDMSTATE_LINEAR_PC_AT;
|
|
|
|
ExAcquireSpinLock(&pVdmObjects->DelayIntSpinLock,
|
|
&OldIrql);
|
|
|
|
KeVdmInsertQueueApc(&pVdmObjects->QueuedIntUserApc,
|
|
&Thread->Tcb,
|
|
UserMode,
|
|
VdmpQueueIntApcRoutine,
|
|
VdmpRundownRoutine,
|
|
NULL, // normal routine
|
|
(PVOID)UserMode, // NormalContext
|
|
VdmState & VDM_INT_HARDWARE
|
|
? VDM_HWINT_INCREMENT : 0);
|
|
|
|
ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
|
|
}
|
|
else {
|
|
ASSERT(*NormalContext == (PVOID)UserMode);
|
|
|
|
Status = VdmpGetVdmTib(&VdmTib);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return;
|
|
}
|
|
|
|
|
|
//VdmTib = (PsGetCurrentProcess()->VdmObjects)->VdmTib;
|
|
// VdmTib =
|
|
// ((PVDM_PROCESS_OBJECTS)(PsGetCurrentProcess()->VdmObjects))->VdmTib;
|
|
|
|
//
|
|
// If there are no hardware ints, dispatch timer ints
|
|
// else dispatch hw interrupts
|
|
//
|
|
if (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INT_TIMER &&
|
|
!(*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INT_HARDWARE))
|
|
{
|
|
VdmTib->EventInfo.Event = VdmIntAck;
|
|
VdmTib->EventInfo.InstructionSize = 0;
|
|
VdmTib->EventInfo.IntAckInfo = 0;
|
|
VdmEndExecution(TrapFrame, VdmTib);
|
|
}
|
|
else {
|
|
VdmDispatchInterrupts (TrapFrame, VdmTib);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// If we are not in application mode and wow is all blocked
|
|
// then Wake up WowExec by setting the wow idle event
|
|
//
|
|
|
|
if (*NormalRoutine && !(*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_WOWBLOCKED)) {
|
|
*NormalRoutine = NULL;
|
|
}
|
|
}
|
|
}
|
|
// WARNING this may set VIP for flat if VPI is ever set in CR4
|
|
else if ((TrapFrame->EFlags & EFLAGS_V86_MASK) &&
|
|
(KeI386VirtualIntExtensions & V86_VIRTUAL_INT_EXTENSIONS)) {
|
|
|
|
//
|
|
// The CPU traps EVERY instruction if VIF and VIP are both ON.
|
|
// Make sure that you set VIP ON only when there are pending
|
|
// interrupts, i.e. (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INTERRUPT_PENDING) != 0.
|
|
//
|
|
|
|
#if DBG
|
|
if (VdmStrict) {
|
|
ASSERT(*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INTERRUPT_PENDING);
|
|
}
|
|
#endif
|
|
ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
|
|
|
|
TrapFrame->EFlags |= EFLAGS_VIP;
|
|
}
|
|
}
|
|
except(VdmpExceptionHandler(GetExceptionInformation())) {
|
|
#if 0
|
|
VdmDispatchException(TrapFrame,
|
|
GetExceptionCode(),
|
|
(PVOID)TrapFrame->Eip,
|
|
0,0,0,0 // no parameters
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
VOID
|
|
VdmpQueueIntNormalRoutine (
|
|
IN PVOID NormalContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PETHREAD Thread;
|
|
PKEVENT Event;
|
|
NTSTATUS Status;
|
|
PKTRAP_FRAME TrapFrame;
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
HANDLE CapturedHandle;
|
|
|
|
UNREFERENCED_PARAMETER (NormalContext);
|
|
UNREFERENCED_PARAMETER (SystemArgument1);
|
|
UNREFERENCED_PARAMETER (SystemArgument2);
|
|
|
|
|
|
//
|
|
// Wake up WowExec by setting the wow idle event
|
|
//
|
|
|
|
pVdmObjects = PsGetCurrentProcess()->VdmObjects;
|
|
|
|
try {
|
|
CapturedHandle = *pVdmObjects->pIcaUserData->phWowIdleEvent;
|
|
}
|
|
except(VdmpExceptionHandler(GetExceptionInformation())) {
|
|
Thread = PsGetCurrentThread();
|
|
TrapFrame = VdmGetTrapFrame(&Thread->Tcb);
|
|
#if 0
|
|
VdmDispatchException(TrapFrame,
|
|
GetExceptionCode(),
|
|
(PVOID)TrapFrame->Eip,
|
|
0,0,0,0 // no parameters
|
|
);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
Status = ObReferenceObjectByHandle (CapturedHandle,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
UserMode,
|
|
&Event,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
KeSetEvent(Event, EVENT_INCREMENT, FALSE);
|
|
ObDereferenceObject(Event);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
VdmRundownDpcs (
|
|
IN PEPROCESS Process
|
|
)
|
|
{
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
PETHREAD Thread, MainThread;
|
|
PDELAYINTIRQ pDelayIntIrq;
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY Next;
|
|
|
|
pVdmObjects = Process->VdmObjects;
|
|
|
|
//
|
|
// Free up the DelayedIntList, canceling pending timers.
|
|
//
|
|
|
|
KeAcquireSpinLock (&pVdmObjects->DelayIntSpinLock, &OldIrql);
|
|
|
|
Next = pVdmObjects->DelayIntListHead.Flink;
|
|
|
|
while (Next != &pVdmObjects->DelayIntListHead) {
|
|
pDelayIntIrq = CONTAINING_RECORD(Next, DELAYINTIRQ, DelayIntListEntry);
|
|
Next = Next->Flink;
|
|
if (KeCancelTimer (&pDelayIntIrq->Timer)) {
|
|
Thread = pDelayIntIrq->Thread;
|
|
pDelayIntIrq->Thread = NULL;
|
|
if (Thread != NULL) {
|
|
ObDereferenceObject (Thread);
|
|
}
|
|
MainThread = pDelayIntIrq->MainThread;
|
|
pDelayIntIrq->MainThread = NULL;
|
|
if (MainThread != NULL) {
|
|
ObDereferenceObject (MainThread);
|
|
}
|
|
|
|
ObDereferenceObject (Process);
|
|
}
|
|
}
|
|
|
|
if (pVdmObjects->MainThread != NULL) {
|
|
ObDereferenceObject (pVdmObjects->MainThread);
|
|
pVdmObjects->MainThread = NULL;
|
|
}
|
|
|
|
KeReleaseSpinLock (&pVdmObjects->DelayIntSpinLock, OldIrql);
|
|
}
|
|
|
|
NTSTATUS
|
|
VdmpEnterIcaLock (
|
|
IN PRTL_CRITICAL_SECTION pIcaLock,
|
|
IN PLARGE_INTEGER Timeout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function enters a usermode critical section, with a fixed timeout
|
|
of several minutes.
|
|
|
|
Touching the critical section may cause an exception to be raised which
|
|
the caller must handle, since the critical section is in usermode
|
|
memory.
|
|
|
|
Arguments:
|
|
|
|
CriticalSection - supplies a pointer to a critical section.
|
|
|
|
Timeout - supplies a pointer to a large integer which specifies the timeout value
|
|
for waiting on critical section.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - wait was satisfied and the thread owns the CS
|
|
|
|
STATUS_INVALID_HANDLE - no semaphore available to wait on.
|
|
|
|
STATUS_TIMEOUT
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE UniqueThread;
|
|
NTSTATUS Status;
|
|
|
|
UniqueThread = NtCurrentTeb()->ClientId.UniqueThread;
|
|
|
|
if (pIcaLock->LockSemaphore == 0) {
|
|
|
|
//
|
|
// Lazy creates not permitted.
|
|
//
|
|
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (InterlockedIncrement (&pIcaLock->LockCount) == 0) {
|
|
|
|
//
|
|
// Set the current thread as the owner of critical section with
|
|
// recursion count of 1.
|
|
//
|
|
|
|
pIcaLock->OwningThread = UniqueThread;
|
|
pIcaLock->RecursionCount = 1;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If the current thread already owns the critical section, increment
|
|
// the recursion count.
|
|
//
|
|
|
|
if (pIcaLock->OwningThread == UniqueThread) {
|
|
pIcaLock->RecursionCount += 1;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Another thread owns the critical section so wait on the
|
|
// lock semaphore.
|
|
//
|
|
|
|
do {
|
|
|
|
Status = NtWaitForSingleObject (pIcaLock->LockSemaphore,
|
|
0,
|
|
Timeout);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
pIcaLock->OwningThread = UniqueThread;
|
|
pIcaLock->RecursionCount = 1;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If !NT_SUCCESS(Status), return that error.
|
|
//
|
|
// Otherwise some other less severe error occurred, in which case
|
|
// if the thread is terminating then fail.
|
|
//
|
|
// Note: we may wake for user APCs even though we are non alertable,
|
|
// because the vdm hw int dispatching code, and PsThread
|
|
// termination code forces these to occur.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Check for termination of self.
|
|
//
|
|
|
|
Status = VdmpIsThreadTerminating (UniqueThread);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
Status = VdmpIsThreadTerminating (pIcaLock->OwningThread);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
} while (TRUE);
|
|
}
|
|
|
|
NTSTATUS
|
|
VdmpLeaveIcaLock (
|
|
IN PRTL_CRITICAL_SECTION pIcaLock
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function leaves a usermode critical section.
|
|
|
|
Touching the critical section may cause an exception to be raised which
|
|
the caller must handle, since the critical section is in usermode
|
|
memory.
|
|
|
|
Arguments:
|
|
|
|
CriticalSection - supplies a pointer to a critical section.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
STATUS_TIMEOUT
|
|
|
|
STATUS_INVALID_OWNER
|
|
|
|
or NTSTATUS code from NtReleaseSemaphore
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE UniqueThread;
|
|
|
|
UniqueThread = NtCurrentTeb()->ClientId.UniqueThread;
|
|
|
|
if (pIcaLock->OwningThread != UniqueThread) {
|
|
return STATUS_INVALID_OWNER;
|
|
}
|
|
|
|
pIcaLock->RecursionCount -= 1;
|
|
|
|
if (pIcaLock->RecursionCount != 0) {
|
|
InterlockedDecrement (&pIcaLock->LockCount);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pIcaLock->OwningThread = NULL;
|
|
|
|
if (InterlockedDecrement (&pIcaLock->LockCount) < 0) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Threads are waiting on the lock semaphore, signal one now.
|
|
//
|
|
|
|
return NtSetEvent (pIcaLock->LockSemaphore, 0);
|
|
}
|
|
|
|
NTSTATUS
|
|
VdmDispatchInterrupts (
|
|
PKTRAP_FRAME TrapFrame,
|
|
PVDM_TIB VdmTib
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dispatches interrupts to the vdm.
|
|
Assumes that we are in application mode and NOT MONITOR context.
|
|
This routine may switch from application context back to monitor
|
|
context, if it cannot handle the interrupt (Ica in AEOI, or timer
|
|
int pending).
|
|
|
|
Arguments:
|
|
|
|
TrapFrame address of current trapframe
|
|
VdmTib address of current vdm tib
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG IretHookAddress;
|
|
ULONG InterruptNumber;
|
|
int IrqLineNum;
|
|
PVDMICAUSERDATA pIcaUserData;
|
|
PVDMVIRTUALICA pIcaAdapter;
|
|
VDMEVENTCLASS VdmEvent = VdmMaxEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
pIcaUserData = ((PVDM_PROCESS_OBJECTS)PsGetCurrentProcess()->VdmObjects)->pIcaUserData;
|
|
|
|
try {
|
|
|
|
//
|
|
// Take the Ica Lock, if this fails raise status as we can't
|
|
// safely recover the critical section state
|
|
//
|
|
|
|
Status = VdmpEnterIcaLock (pIcaUserData->pIcaLock, pIcaUserData->pIcaTimeout);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExRaiseStatus(Status);
|
|
}
|
|
|
|
if (*pIcaUserData->pUndelayIrq) {
|
|
VdmpRestartDelayedInterrupts(pIcaUserData);
|
|
}
|
|
|
|
VDIretry:
|
|
|
|
//
|
|
// Clear the VIP bit
|
|
//
|
|
|
|
if ((TrapFrame->EFlags & EFLAGS_V86_MASK) &&
|
|
(KeI386VirtualIntExtensions & V86_VIRTUAL_INT_EXTENSIONS)) {
|
|
|
|
ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
|
|
|
|
TrapFrame->EFlags &= ~EFLAGS_VIP;
|
|
}
|
|
|
|
|
|
//
|
|
// Mark the vdm state as hw int dispatched. Must use the lock as
|
|
// kernel mode DelayedIntApcRoutine changes the bit as well
|
|
//
|
|
|
|
InterlockedAnd (FIXED_NTVDMSTATE_LINEAR_PC_AT, ~VDM_INT_HARDWARE);
|
|
|
|
pIcaAdapter = pIcaUserData->pIcaMaster;
|
|
IrqLineNum = VdmpIcaAccept(pIcaUserData, pIcaAdapter);
|
|
|
|
if (IrqLineNum >= 0) {
|
|
UCHAR bit = (UCHAR)(1 << IrqLineNum);
|
|
|
|
if (pIcaUserData->pIcaMaster->ica_ssr & bit) {
|
|
pIcaAdapter = pIcaUserData->pIcaSlave;
|
|
IrqLineNum = VdmpIcaAccept(pIcaUserData, pIcaAdapter);
|
|
if (IrqLineNum < 0) {
|
|
pIcaUserData->pIcaMaster->ica_isr &= ~bit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Skip spurious ints
|
|
//
|
|
|
|
if (IrqLineNum < 0) {
|
|
|
|
//
|
|
// Check for delayed interrupts which need to be restarted
|
|
//
|
|
|
|
if (*pIcaUserData->pUndelayIrq &&
|
|
VdmpRestartDelayedInterrupts(pIcaUserData) != -1) {
|
|
goto VDIretry;
|
|
}
|
|
|
|
Status = VdmpLeaveIcaLock (pIcaUserData->pIcaLock);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExRaiseStatus(Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Capture the AutoEoi mode case for special handling
|
|
//
|
|
|
|
if (pIcaAdapter->ica_mode & ICA_AEOI) {
|
|
VdmEvent = VdmIntAck;
|
|
VdmTib->EventInfo.IntAckInfo = VDMINTACK_AEOI;
|
|
if (pIcaAdapter == pIcaUserData->pIcaSlave) {
|
|
VdmTib->EventInfo.IntAckInfo |= VDMINTACK_SLAVE;
|
|
}
|
|
}
|
|
|
|
InterruptNumber = IrqLineNum + pIcaAdapter->ica_base;
|
|
|
|
//
|
|
// Get the IretHookAddress ... if any
|
|
//
|
|
|
|
if (pIcaAdapter == pIcaUserData->pIcaSlave) {
|
|
IrqLineNum += 8;
|
|
}
|
|
|
|
IretHookAddress = GetIretHookAddress (TrapFrame,
|
|
pIcaUserData,
|
|
IrqLineNum);
|
|
|
|
if (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_TRACE_HISTORY) {
|
|
VdmTraceEvent (VDMTR_KERNEL_HW_INT,
|
|
(USHORT)InterruptNumber,
|
|
0,
|
|
TrapFrame);
|
|
}
|
|
|
|
//
|
|
// Push the interrupt frames
|
|
//
|
|
|
|
if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
|
|
|
|
PushRmInterrupt (TrapFrame,
|
|
IretHookAddress,
|
|
VdmTib,
|
|
InterruptNumber);
|
|
}
|
|
else {
|
|
Status = PushPmInterrupt (TrapFrame,
|
|
IretHookAddress,
|
|
VdmTib,
|
|
InterruptNumber);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
VdmpLeaveIcaLock (pIcaUserData->pIcaLock);
|
|
ExRaiseStatus (Status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Disable interrupts and the trap flag
|
|
//
|
|
|
|
if ((TrapFrame->EFlags & EFLAGS_V86_MASK) &&
|
|
(KeI386VirtualIntExtensions & V86_VIRTUAL_INT_EXTENSIONS)) {
|
|
TrapFrame->EFlags &= ~EFLAGS_VIF;
|
|
} else {
|
|
*FIXED_NTVDMSTATE_LINEAR_PC_AT &= ~VDM_VIRTUAL_INTERRUPTS;
|
|
}
|
|
|
|
ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
|
|
TrapFrame->EFlags &= ~(EFLAGS_NT_MASK | EFLAGS_TF_MASK);
|
|
|
|
KeBoostPriorityThread (KeGetCurrentThread(), VDM_HWINT_INCREMENT);
|
|
|
|
//
|
|
// Release the Ica lock
|
|
//
|
|
|
|
Status = VdmpLeaveIcaLock (pIcaUserData->pIcaLock);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExRaiseStatus (Status);
|
|
}
|
|
|
|
//
|
|
// check to see if we are supposed to switch back to monitor context
|
|
//
|
|
if (VdmEvent != VdmMaxEvent) {
|
|
VdmTib->EventInfo.Event = VdmIntAck;
|
|
VdmTib->EventInfo.InstructionSize = 0;
|
|
VdmEndExecution(TrapFrame, VdmTib);
|
|
}
|
|
}
|
|
except (VdmpExceptionHandler(GetExceptionInformation())) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
int
|
|
VdmpRestartDelayedInterrupts (
|
|
PVDMICAUSERDATA pIcaUserData
|
|
)
|
|
{
|
|
int line;
|
|
|
|
PAGED_CODE();
|
|
|
|
try {
|
|
*pIcaUserData->pUndelayIrq = 0;
|
|
|
|
line = VdmpIcaScan(pIcaUserData, pIcaUserData->pIcaSlave);
|
|
if (line != -1) {
|
|
// set the slave
|
|
pIcaUserData->pIcaSlave->ica_int_line = line;
|
|
pIcaUserData->pIcaSlave->ica_cpu_int = TRUE;
|
|
|
|
// set the master cascade
|
|
line = pIcaUserData->pIcaSlave->ica_ssr;
|
|
pIcaUserData->pIcaMaster->ica_irr |= 1 << line;
|
|
pIcaUserData->pIcaMaster->ica_count[line]++;
|
|
}
|
|
|
|
line = VdmpIcaScan(pIcaUserData, pIcaUserData->pIcaMaster);
|
|
|
|
if (line != -1) {
|
|
pIcaUserData->pIcaMaster->ica_cpu_int = TRUE;
|
|
pIcaUserData->pIcaMaster->ica_int_line = TRUE;
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
line = -1;
|
|
NOTHING;
|
|
}
|
|
|
|
return line;
|
|
}
|
|
|
|
int
|
|
VdmpIcaScan (
|
|
PVDMICAUSERDATA pIcaUserData,
|
|
PVDMVIRTUALICA pIcaAdapter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Similar to softpc\base\system\ica.c - scan_irr(),
|
|
|
|
Check the IRR, the IMR and the ISR to determine which interrupt
|
|
should be delivered.
|
|
|
|
A bit set in the IRR will generate an interrupt if:
|
|
IMR bit, DelayIret bit, DelayIrq bit AND ISR higher priority bits
|
|
are clear (unless Special Mask Mode, in which case ISR is ignored)
|
|
|
|
If there is no bit set, then return -1
|
|
|
|
|
|
Arguments:
|
|
PVDMICAUSERDATA pIcaUserData - addr of ica userdata
|
|
PVDMVIRTUALICA pIcaAdapter - addr of ica adapter
|
|
|
|
|
|
Return Value:
|
|
|
|
int IrqLineNum for the specific adapter (0 to 7)
|
|
-1 for none
|
|
|
|
--*/
|
|
|
|
{
|
|
int i,line;
|
|
UCHAR bit;
|
|
ULONG IrrImrDelay;
|
|
ULONG ActiveIsr;
|
|
|
|
PAGED_CODE();
|
|
|
|
IrrImrDelay = *pIcaUserData->pDelayIrq | *pIcaUserData->pDelayIret;
|
|
if (pIcaAdapter == pIcaUserData->pIcaSlave) {
|
|
IrrImrDelay >>= 8;
|
|
}
|
|
|
|
IrrImrDelay = pIcaAdapter->ica_irr & ~(pIcaAdapter->ica_imr | (UCHAR)IrrImrDelay);
|
|
|
|
if (IrrImrDelay) {
|
|
|
|
/*
|
|
* Does the current mode require the ica to prevent
|
|
* interrupts if that line is still active (ie in the isr)?
|
|
*
|
|
* Normal Case: Used by DOS and Win3.1/S the isr prevents interrupts.
|
|
* Special Mask Mode, Special Fully Nested Mode do not block
|
|
* interrupts using bits in the isr. SMM is the mode used
|
|
* by Windows95 and Win3.1/E.
|
|
*
|
|
*/
|
|
ActiveIsr = (pIcaAdapter->ica_mode & (ICA_SMM|ICA_SFNM))
|
|
? 0 : pIcaAdapter->ica_isr;
|
|
|
|
for(i = 0; i < 8; i++) {
|
|
line = (pIcaAdapter->ica_hipri + i) & 7;
|
|
bit = (UCHAR) (1 << line);
|
|
if (ActiveIsr & bit) {
|
|
break; /* No nested interrupt possible */
|
|
}
|
|
|
|
if (IrrImrDelay & bit) {
|
|
return line;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
VdmpIcaAccept(
|
|
PVDMICAUSERDATA pIcaUserData,
|
|
PVDMVIRTUALICA pIcaAdapter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does the equivalent of a cpu IntAck cycle retrieving the Irql Line Num
|
|
for interrupt dispatch, and setting the ica state to reflect that
|
|
the interrupt is in service.
|
|
|
|
Similar to softpc\base\system\ica.c - ica_accept() scan_irr(),
|
|
except that this code rejects interrupt dispatching if the ica
|
|
is in Auto-EOI as this may involve a new interrupt cycle, and
|
|
eoi hooks to be activated.
|
|
|
|
Arguments:
|
|
PVDMICAUSERDATA pIcaUserData - addr of ica userdata
|
|
PVDMVIRTUALICA pIcaAdapter - addr of ica adapter
|
|
|
|
|
|
Return Value:
|
|
|
|
ULONG IrqLineNum for the specific adapter (0 to 7)
|
|
returns -1 if there are no interrupts to generate (spurious ints
|
|
are normally done on line 7
|
|
|
|
--*/
|
|
|
|
{
|
|
int line;
|
|
UCHAR bit;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Drop the INT line, and scan the ica
|
|
//
|
|
pIcaAdapter->ica_cpu_int = FALSE;
|
|
|
|
try {
|
|
line = VdmpIcaScan(pIcaUserData, pIcaAdapter);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return -1;
|
|
}
|
|
|
|
if (line < 0) {
|
|
return -1;
|
|
}
|
|
|
|
bit = (UCHAR)(1 << line);
|
|
pIcaAdapter->ica_isr |= bit;
|
|
|
|
//
|
|
// decrement the count and clear the IRR bit
|
|
// ensure the count doesn't wrap past zero.
|
|
//
|
|
|
|
if (--(pIcaAdapter->ica_count[line]) <= 0) {
|
|
pIcaAdapter->ica_irr &= ~bit;
|
|
pIcaAdapter->ica_count[line] = 0;
|
|
}
|
|
|
|
return line;
|
|
}
|
|
|
|
ULONG
|
|
GetIretHookAddress(
|
|
PKTRAP_FRAME TrapFrame,
|
|
PVDMICAUSERDATA pIcaUserData,
|
|
int IrqNum
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the IretHookAddress from the real mode\protect mode
|
|
iret hook bop table. This function is equivalent to
|
|
softpc\base\system\ica.c - ica_iret_hook_needed()
|
|
|
|
Arguments:
|
|
|
|
TrapFrame - address of current trapframe
|
|
pIcaUserData - addr of ica data
|
|
IrqNum - IrqLineNum
|
|
|
|
Return Value:
|
|
|
|
ULONG IretHookAddress. seg:offset or sel:offset Iret Hook,
|
|
0 if none
|
|
--*/
|
|
|
|
{
|
|
ULONG IrqMask;
|
|
ULONG AddrBopTable;
|
|
int IretBopSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
IrqMask = 1 << IrqNum;
|
|
if (!(IrqMask & *pIcaUserData->pIretHooked) ||
|
|
!*pIcaUserData->pAddrIretBopTable )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
|
|
AddrBopTable = *pIcaUserData->pAddrIretBopTable;
|
|
IretBopSize = VDM_RM_IRETBOPSIZE;
|
|
}
|
|
else {
|
|
AddrBopTable = (VDM_PM_IRETBOPSEG << 16) | VDM_PM_IRETBOPOFF;
|
|
IretBopSize = VDM_PM_IRETBOPSIZE;
|
|
}
|
|
|
|
*pIcaUserData->pDelayIret |= IrqMask;
|
|
|
|
return AddrBopTable + IretBopSize * IrqNum;
|
|
}
|
|
|
|
VOID
|
|
PushRmInterrupt(
|
|
PKTRAP_FRAME TrapFrame,
|
|
ULONG IretHookAddress,
|
|
PVDM_TIB VdmTib,
|
|
ULONG InterruptNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pushes RealMode interrupt frame onto the UserMode stack in the TrapFrame
|
|
|
|
Arguments:
|
|
|
|
TrapFrame - address of current trapframe
|
|
IretHookAddress - address of Iret Hook, 0 if none
|
|
VdmTib - address of current vdm tib
|
|
InterruptNumber - interrupt number to reflect
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG UserSS;
|
|
USHORT UserSP;
|
|
USHORT NewCS;
|
|
USHORT NewIP;
|
|
PVDM_INTERRUPTHANDLER IntHandler;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get pointers to current stack
|
|
//
|
|
|
|
UserSS = TrapFrame->HardwareSegSs << 4;
|
|
UserSP = (USHORT) TrapFrame->HardwareEsp;
|
|
|
|
//
|
|
// load interrupt stack frame, pushing flags, Cs and ip
|
|
//
|
|
|
|
try {
|
|
ProbeForReadSmallStructure (UserSS + UserSP - 3 * sizeof (USHORT),
|
|
3 * sizeof (USHORT),
|
|
sizeof (UCHAR));
|
|
UserSP -= 2;
|
|
*(PUSHORT)(UserSS + UserSP) = (USHORT)TrapFrame->EFlags;
|
|
UserSP -= 2;
|
|
*(PUSHORT)(UserSS + UserSP) = (USHORT)TrapFrame->SegCs;
|
|
UserSP -= 2;
|
|
*(PUSHORT)(UserSS + UserSP) = (USHORT)TrapFrame->Eip;
|
|
|
|
//
|
|
// load IretHook stack frame if one exists
|
|
//
|
|
|
|
if (IretHookAddress) {
|
|
ProbeForReadSmallStructure (UserSS + UserSP - 3 * sizeof (USHORT),
|
|
3 * sizeof (USHORT),
|
|
sizeof (UCHAR));
|
|
UserSP -= 2;
|
|
*(PUSHORT)(UserSS + UserSP) = (USHORT)(TrapFrame->EFlags & ~EFLAGS_TF_MASK);
|
|
UserSP -= 2;
|
|
*(PUSHORT)(UserSS + UserSP) = (USHORT)(IretHookAddress >> 16);
|
|
UserSP -= 2;
|
|
*(PUSHORT)(UserSS + UserSP) = (USHORT)IretHookAddress;
|
|
}
|
|
|
|
//
|
|
// Set new sp, ip, and cs.
|
|
//
|
|
|
|
IntHandler = &VdmTib->VdmInterruptTable[InterruptNumber];
|
|
ProbeForReadSmallStructure (&IntHandler[InterruptNumber],
|
|
sizeof (VDM_INTERRUPTHANDLER),
|
|
sizeof (UCHAR));
|
|
if (IntHandler->Flags & VDM_INT_HOOKED) {
|
|
NewCS = (USHORT) (VdmTib->DpmiInfo.DosxRmReflector >> 16);
|
|
NewIP = (USHORT) VdmTib->DpmiInfo.DosxRmReflector;
|
|
|
|
//
|
|
// now encode the interrupt number into CS
|
|
//
|
|
|
|
NewCS = (USHORT) (NewCS - InterruptNumber);
|
|
NewIP = (USHORT) (NewIP + (InterruptNumber*16));
|
|
|
|
} else {
|
|
PUSHORT pIvtEntry = (PUSHORT) (InterruptNumber * 4);
|
|
|
|
ProbeForReadSmallStructure (pIvtEntry,
|
|
sizeof (USHORT) * 2,
|
|
sizeof (UCHAR));
|
|
NewIP = *pIvtEntry++;
|
|
NewCS = *pIvtEntry;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return;
|
|
}
|
|
|
|
TrapFrame->HardwareEsp = UserSP;
|
|
TrapFrame->Eip = NewIP;
|
|
|
|
ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
|
|
|
|
if ((TrapFrame->EFlags & EFLAGS_V86_MASK) == 0) {
|
|
NewCS = SANITIZE_SEG (NewCS, UserMode);
|
|
if (NewCS < 8) {
|
|
NewCS = KGDT_R3_CODE | RPL_MASK;
|
|
}
|
|
}
|
|
TrapFrame->SegCs = NewCS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PushPmInterrupt(
|
|
PKTRAP_FRAME TrapFrame,
|
|
ULONG IretHookAddress,
|
|
PVDM_TIB VdmTib,
|
|
ULONG InterruptNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pushes ProtectMode interrupt frame onto the UserMode stack in the TrapFrame
|
|
Raises an exception if an invalid stack is found
|
|
|
|
Arguments:
|
|
|
|
TrapFrame - address of current trapframe
|
|
IretHookAddress - address of Iret Hook, 0 if none
|
|
VdmTib - address of current vdm tib
|
|
InterruptNumber - interrupt number to reflect
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Flags,Base,Limit;
|
|
ULONG VdmSp, VdmSpOrg;
|
|
PUSHORT VdmStackPointer;
|
|
BOOLEAN Frame32 = (BOOLEAN) VdmTib->DpmiInfo.Flags;
|
|
PVDM_INTERRUPTHANDLER IntHandler;
|
|
USHORT NewCS;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Switch to "locked" dpmi stack if lock count is zero
|
|
// This emulates the win3.1 Begin_Use_Locked_PM_Stack function.
|
|
//
|
|
|
|
try {
|
|
if (!VdmTib->DpmiInfo.LockCount++) {
|
|
VdmTib->DpmiInfo.SaveEsp = TrapFrame->HardwareEsp;
|
|
VdmTib->DpmiInfo.SaveEip = TrapFrame->Eip;
|
|
VdmTib->DpmiInfo.SaveSsSelector = (USHORT) TrapFrame->HardwareSegSs;
|
|
TrapFrame->HardwareEsp = 0x1000;
|
|
TrapFrame->HardwareSegSs = (ULONG) VdmTib->DpmiInfo.SsSelector | 0x7;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode ();
|
|
}
|
|
|
|
//
|
|
// Use Sp or Esp ?
|
|
//
|
|
if (!Ki386GetSelectorParameters((USHORT)TrapFrame->HardwareSegSs,
|
|
&Flags, &Base, &Limit)) {
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
//
|
|
// Adjust the limit for page granularity
|
|
//
|
|
if (Flags & SEL_TYPE_2GIG) {
|
|
Limit = (Limit << 12) | 0xfff;
|
|
}
|
|
if (Limit != 0xffffffff) Limit++;
|
|
|
|
VdmSp = (Flags & SEL_TYPE_BIG) ? TrapFrame->HardwareEsp
|
|
: (USHORT)TrapFrame->HardwareEsp;
|
|
|
|
//
|
|
// Get pointer to current stack
|
|
//
|
|
VdmStackPointer = (PUSHORT)(Base + VdmSp);
|
|
|
|
|
|
//
|
|
// Create enough room for iret hook frame
|
|
//
|
|
VdmSpOrg = VdmSp;
|
|
if (IretHookAddress) {
|
|
if (Frame32) {
|
|
VdmSp -= 3*sizeof(ULONG);
|
|
} else {
|
|
VdmSp -= 3*sizeof(USHORT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create enough room for 2 iret frames
|
|
//
|
|
|
|
if (Frame32) {
|
|
VdmSp -= 6*sizeof(ULONG);
|
|
} else {
|
|
VdmSp -= 6*sizeof(USHORT);
|
|
}
|
|
|
|
//
|
|
// Set Final Value of Sp\Esp, do this before checking stack
|
|
// limits so that invalid esp is visible to debuggers
|
|
//
|
|
if (Flags & SEL_TYPE_BIG) {
|
|
TrapFrame->HardwareEsp = VdmSp;
|
|
} else {
|
|
TrapFrame->HardwareEsp = (USHORT)VdmSp;
|
|
}
|
|
|
|
|
|
//
|
|
// Check stack limits
|
|
// If any of the following conditions are TRUE
|
|
// - New stack pointer wraps (not enuf space)
|
|
// - If normal stack and Sp not below limit
|
|
// - If Expand Down stack and Sp not above limit
|
|
//
|
|
// Then raise a Stack Fault
|
|
//
|
|
if ( VdmSp >= VdmSpOrg ||
|
|
!(Flags & SEL_TYPE_ED) && VdmSpOrg > Limit ||
|
|
(Flags & SEL_TYPE_ED) && VdmSp < Limit ) {
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
//
|
|
// Build the Hw Int iret frame
|
|
//
|
|
|
|
try {
|
|
if (Frame32) {
|
|
//
|
|
// Probe the stack pointer to make sure its good. We probe for read here
|
|
// as we are faster. The code is going to write the addresses anyway.
|
|
//
|
|
ProbeForReadSmallStructure (VdmStackPointer - 6 * sizeof (ULONG),
|
|
6 * sizeof (ULONG),
|
|
sizeof (UCHAR));
|
|
|
|
VdmStackPointer -= 2;
|
|
*(PULONG)VdmStackPointer = TrapFrame->EFlags;
|
|
|
|
VdmStackPointer -= 2;
|
|
*(PUSHORT)VdmStackPointer = (USHORT)TrapFrame->SegCs;
|
|
|
|
VdmStackPointer -= 2;
|
|
*(PULONG)VdmStackPointer = TrapFrame->Eip;
|
|
|
|
VdmStackPointer -= 2;
|
|
*(PULONG)VdmStackPointer = TrapFrame->EFlags & ~EFLAGS_TF_MASK;
|
|
|
|
VdmStackPointer -= 2;
|
|
*(PULONG)VdmStackPointer = VdmTib->DpmiInfo.DosxIntIretD >> 16;
|
|
|
|
VdmStackPointer -= 2;
|
|
*(PULONG)VdmStackPointer = VdmTib->DpmiInfo.DosxIntIretD & 0xffff;
|
|
|
|
} else {
|
|
ProbeForReadSmallStructure (VdmStackPointer - 6 * sizeof (USHORT),
|
|
6 * sizeof (USHORT),
|
|
sizeof (UCHAR));
|
|
|
|
VdmStackPointer -= 1;
|
|
*VdmStackPointer = (USHORT)TrapFrame->EFlags;
|
|
|
|
VdmStackPointer -= 1;
|
|
*VdmStackPointer = (USHORT)TrapFrame->SegCs;
|
|
|
|
VdmStackPointer -= 1;
|
|
*VdmStackPointer = (USHORT)TrapFrame->Eip;
|
|
|
|
VdmStackPointer -= 1;
|
|
*VdmStackPointer = (USHORT)(TrapFrame->EFlags & ~EFLAGS_TF_MASK);
|
|
|
|
VdmStackPointer -= 2;
|
|
*(PULONG)VdmStackPointer = VdmTib->DpmiInfo.DosxIntIret;
|
|
}
|
|
|
|
//
|
|
// Point cs and ip at interrupt handler
|
|
//
|
|
IntHandler = &VdmTib->VdmInterruptTable[InterruptNumber];
|
|
ProbeForReadSmallStructure (&IntHandler[InterruptNumber],
|
|
sizeof (VDM_INTERRUPTHANDLER),
|
|
sizeof (UCHAR));
|
|
NewCS = IntHandler->CsSelector | 0x7;
|
|
if ((TrapFrame->EFlags & EFLAGS_V86_MASK) == 0) {
|
|
NewCS = SANITIZE_SEG (NewCS, UserMode);
|
|
if (NewCS < 8) {
|
|
NewCS = KGDT_R3_CODE | RPL_MASK;
|
|
}
|
|
}
|
|
TrapFrame->SegCs = NewCS;
|
|
TrapFrame->Eip = IntHandler->Eip;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode ();
|
|
}
|
|
|
|
//
|
|
// Turn off trace bit so we don't trace the iret hook
|
|
//
|
|
ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
|
|
|
|
TrapFrame->EFlags &= ~EFLAGS_TF_MASK;
|
|
|
|
//
|
|
// Build the Irethook Iret frame, if one exists
|
|
//
|
|
if (IretHookAddress) {
|
|
ULONG SegCs, Eip;
|
|
|
|
//
|
|
// Point cs and eip at the iret hook, so when we build
|
|
// the frame below, the correct contents are set
|
|
//
|
|
SegCs = IretHookAddress >> 16;
|
|
Eip = IretHookAddress & 0xFFFF;
|
|
|
|
|
|
try {
|
|
if (Frame32) {
|
|
|
|
ProbeForReadSmallStructure (VdmStackPointer - 3 * sizeof (ULONG),
|
|
3 * sizeof (ULONG),
|
|
sizeof (UCHAR));
|
|
|
|
VdmStackPointer -= 2;
|
|
*(PULONG)VdmStackPointer = TrapFrame->EFlags;
|
|
|
|
VdmStackPointer -= 2;
|
|
*VdmStackPointer = (USHORT)SegCs;
|
|
|
|
VdmStackPointer -= 2;
|
|
*(PULONG)VdmStackPointer = Eip;
|
|
|
|
} else {
|
|
|
|
ProbeForReadSmallStructure (VdmStackPointer - 3 * sizeof (USHORT),
|
|
3 * sizeof (USHORT),
|
|
sizeof (UCHAR));
|
|
|
|
VdmStackPointer -= 1;
|
|
*VdmStackPointer = (USHORT)TrapFrame->EFlags;
|
|
|
|
VdmStackPointer -= 1;
|
|
*VdmStackPointer = (USHORT)SegCs;
|
|
|
|
VdmStackPointer -= 1;
|
|
*VdmStackPointer = (USHORT)Eip;
|
|
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode ();
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
VdmpDelayInterrupt (
|
|
PVDMDELAYINTSDATA pdsd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets a timer to dispatch the delayed interrupt through KeSetTimer.
|
|
When the timer fires a user mode APC is queued to queue the interrupt.
|
|
|
|
This function uses lazy allocation routines to allocate internal
|
|
data structures (nonpaged pool) on a per Irq basis, and needs to
|
|
be notified when specific Irq Lines no longer need Delayed
|
|
Interrupt services.
|
|
|
|
The caller must own the IcaLock to synchronize access to the
|
|
Irq lists.
|
|
|
|
WARNING: - Until the Delayed interrupt fires or is cancelled,
|
|
the specific Irq line will not generate any interrupts.
|
|
|
|
- The APC routine, does not take the HostIca lock, when
|
|
unblocking the IrqLine. Devices which use delayed Interrupts
|
|
should not queue ANY additional interrupts for the same IRQ
|
|
line until the delayed interrupt has fired or been cancelled.
|
|
|
|
Arguments:
|
|
|
|
pdsd.Delay Delay Interval in usecs
|
|
if Delay is 0xFFFFFFFF then per Irq Line nonpaged
|
|
data structures are freed. No Timers are set.
|
|
else the Delay is used as the timer delay.
|
|
|
|
pdsd.DelayIrqLine IrqLine Number
|
|
|
|
pdsd.hThread Thread Handle of CurrentMonitorTeb
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
VDMDELAYINTSDATA Capturedpdsd;
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
PLIST_ENTRY Next;
|
|
PEPROCESS Process;
|
|
PDELAYINTIRQ pDelayIntIrq;
|
|
PDELAYINTIRQ NewIrq;
|
|
PETHREAD Thread, MainThread;
|
|
NTSTATUS Status;
|
|
KIRQL OldIrql;
|
|
ULONG IrqLine;
|
|
ULONG Delay;
|
|
PULONG pDelayIrq;
|
|
PULONG pUndelayIrq;
|
|
LARGE_INTEGER liDelay;
|
|
LOGICAL FreeIrqLine;
|
|
LOGICAL AlreadyInUse;
|
|
|
|
//
|
|
// Get a pointer to pVdmObjects
|
|
//
|
|
Process = PsGetCurrentProcess();
|
|
pVdmObjects = Process->VdmObjects;
|
|
|
|
if (pVdmObjects == NULL) {
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Thread = MainThread = NULL;
|
|
FreeIrqLine = TRUE;
|
|
AlreadyInUse = FALSE;
|
|
|
|
try {
|
|
|
|
//
|
|
// Probe the parameters
|
|
//
|
|
|
|
ProbeForRead(pdsd, sizeof(VDMDELAYINTSDATA), sizeof(ULONG));
|
|
RtlCopyMemory (&Capturedpdsd, pdsd, sizeof (VDMDELAYINTSDATA));
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// Form a BitMask for the IrqLine Number
|
|
//
|
|
|
|
IrqLine = 1 << Capturedpdsd.DelayIrqLine;
|
|
if (!IrqLine) {
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
|
|
ExAcquireFastMutex(&pVdmObjects->DelayIntFastMutex);
|
|
|
|
pDelayIrq = pVdmObjects->pIcaUserData->pDelayIrq;
|
|
pUndelayIrq = pVdmObjects->pIcaUserData->pUndelayIrq;
|
|
|
|
try {
|
|
|
|
ProbeForWriteUlong(pDelayIrq);
|
|
ProbeForWriteUlong(pUndelayIrq);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
ExReleaseFastMutex(&pVdmObjects->DelayIntFastMutex);
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
pDelayIntIrq = NULL; // satisfy no_opt compilation
|
|
|
|
//
|
|
// Convert the Delay parameter into hundredths of nanosecs
|
|
//
|
|
|
|
Delay = Capturedpdsd.Delay;
|
|
|
|
//
|
|
// Check to see if we need to reset the timer resolution
|
|
//
|
|
|
|
if (Delay == 0xFFFFFFFF) {
|
|
ZwSetTimerResolution(KeMaximumIncrement, FALSE, &Delay);
|
|
NewIrq = NULL;
|
|
goto FindIrq;
|
|
}
|
|
|
|
FreeIrqLine = FALSE;
|
|
|
|
//
|
|
// Convert delay to hundreths of nanosecs
|
|
// and ensure min delay of 1 msec
|
|
//
|
|
|
|
Delay = Delay < 1000 ? 10000 : Delay * 10;
|
|
|
|
//
|
|
// If the delay time is close to the system's clock rate
|
|
// then adjust the system's clock rate and if needed
|
|
// the delay time so that the timer will fire before the
|
|
// the due time.
|
|
//
|
|
|
|
if (Delay < 150000) {
|
|
|
|
ULONG ul = Delay >> 1;
|
|
|
|
if (ul < KeTimeIncrement && KeTimeIncrement > KeMinimumIncrement) {
|
|
ZwSetTimerResolution(ul, TRUE, (PULONG)&liDelay.LowPart);
|
|
}
|
|
|
|
if (Delay < KeTimeIncrement) {
|
|
// can't set system clock rate low enuf, so use half delay
|
|
Delay >>= 1;
|
|
}
|
|
else if (Delay < (KeTimeIncrement << 1)) {
|
|
// Real close to the system clock rate, lower delay
|
|
// proportionally, to avoid missing clock cycles.
|
|
Delay -= KeTimeIncrement >> 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reference the Target Thread
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle (Capturedpdsd.hThread,
|
|
THREAD_QUERY_INFORMATION,
|
|
PsThreadType,
|
|
KeGetPreviousMode(),
|
|
&Thread,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExReleaseFastMutex(&pVdmObjects->DelayIntFastMutex);
|
|
return Status;
|
|
}
|
|
|
|
MainThread = pVdmObjects->MainThread;
|
|
|
|
ObReferenceObject (MainThread);
|
|
|
|
NewIrq = NULL;
|
|
|
|
FindIrq:
|
|
|
|
ExAcquireSpinLock(&pVdmObjects->DelayIntSpinLock, &OldIrql);
|
|
|
|
//
|
|
// Search the DelayedIntList for a matching Irq Line.
|
|
//
|
|
|
|
Next = pVdmObjects->DelayIntListHead.Flink;
|
|
while (Next != &pVdmObjects->DelayIntListHead) {
|
|
pDelayIntIrq = CONTAINING_RECORD(Next, DELAYINTIRQ, DelayIntListEntry);
|
|
if (pDelayIntIrq->IrqLine == IrqLine) {
|
|
break;
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
if (Next == &pVdmObjects->DelayIntListHead) {
|
|
|
|
pDelayIntIrq = NULL;
|
|
|
|
if (FreeIrqLine) {
|
|
goto VidExit;
|
|
}
|
|
|
|
if (NewIrq == NULL) {
|
|
|
|
ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
|
|
|
|
//
|
|
// If a DelayIntIrq does not exist for this irql, allocate one
|
|
// from nonpaged pool and initialize it
|
|
//
|
|
|
|
NewIrq = ExAllocatePoolWithTag (NonPagedPool,
|
|
sizeof(DELAYINTIRQ),
|
|
' MDV');
|
|
|
|
if (!NewIrq) {
|
|
Status = STATUS_NO_MEMORY;
|
|
AlreadyInUse = TRUE;
|
|
goto VidExit2;
|
|
}
|
|
|
|
try {
|
|
PsChargePoolQuota(Process, NonPagedPool, sizeof(DELAYINTIRQ));
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
ExFreePool(NewIrq);
|
|
AlreadyInUse = TRUE;
|
|
goto VidExit2;
|
|
}
|
|
|
|
RtlZeroMemory(NewIrq, sizeof(DELAYINTIRQ));
|
|
NewIrq->IrqLine = IrqLine;
|
|
|
|
KeInitializeTimer(&NewIrq->Timer);
|
|
|
|
KeInitializeDpc(&NewIrq->Dpc,
|
|
VdmpDelayIntDpcRoutine,
|
|
Process);
|
|
|
|
goto FindIrq;
|
|
}
|
|
|
|
InsertTailList (&pVdmObjects->DelayIntListHead,
|
|
&NewIrq->DelayIntListEntry);
|
|
|
|
pDelayIntIrq = NewIrq;
|
|
}
|
|
else if (NewIrq != NULL) {
|
|
ExFreePool (NewIrq);
|
|
PsReturnPoolQuota (Process, NonPagedPool, sizeof(DELAYINTIRQ));
|
|
}
|
|
|
|
if (Delay == 0xFFFFFFFF) {
|
|
if (pDelayIntIrq->InUse == VDMDELAY_KTIMER) {
|
|
pDelayIntIrq->InUse = VDMDELAY_NOTINUSE;
|
|
pDelayIntIrq = NULL;
|
|
}
|
|
}
|
|
else if (pDelayIntIrq->InUse == VDMDELAY_NOTINUSE) {
|
|
liDelay = RtlEnlargedIntegerMultiply(Delay, -1);
|
|
if (KeSetTimerEx (&pDelayIntIrq->Timer, liDelay, 0, &pDelayIntIrq->Dpc) == FALSE) {
|
|
ObReferenceObject(Process);
|
|
}
|
|
}
|
|
|
|
VidExit:
|
|
|
|
if (pDelayIntIrq && !pDelayIntIrq->InUse) {
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// Save PETHREAD of Target thread for the dpc routine
|
|
// the DPC routine will deref the threads.
|
|
//
|
|
pDelayIntIrq->InUse = VDMDELAY_KTIMER;
|
|
pDelayIntIrq->Thread = Thread;
|
|
Thread = NULL;
|
|
pDelayIntIrq->MainThread = MainThread;
|
|
MainThread = NULL;
|
|
}
|
|
else {
|
|
pDelayIntIrq->InUse = VDMDELAY_NOTINUSE;
|
|
pDelayIntIrq->Thread = NULL;
|
|
FreeIrqLine = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
AlreadyInUse = TRUE;
|
|
}
|
|
|
|
|
|
ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
|
|
|
|
VidExit2:
|
|
|
|
try {
|
|
if (FreeIrqLine) {
|
|
*pDelayIrq &= ~IrqLine;
|
|
InterlockedOr ((PLONG)pUndelayIrq, IrqLine);
|
|
}
|
|
else if (!AlreadyInUse) { // TakeIrqLine
|
|
*pDelayIrq |= IrqLine;
|
|
InterlockedAnd ((PLONG)pUndelayIrq, ~IrqLine);
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
ExReleaseFastMutex(&pVdmObjects->DelayIntFastMutex);
|
|
|
|
if (Thread) {
|
|
ObDereferenceObject(Thread);
|
|
}
|
|
|
|
if (MainThread) {
|
|
ObDereferenceObject(MainThread);
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
VOID
|
|
VdmpDelayIntDpcRoutine (
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the DPC routine that is called when a DelayedInterrupt
|
|
timer expires. Its function is to insert the associated APC into the
|
|
target thread's APC queue.
|
|
|
|
Arguments:
|
|
|
|
Dpc - Supplies a pointer to a control object of type DPC.
|
|
|
|
DeferredContext - Supplies a pointer to the Target EProcess
|
|
|
|
SystemArgument1, SystemArgument2 - Supplies a set of two pointers to
|
|
two arguments that contain untyped data that are
|
|
NOT USED.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LOGICAL FreeEntireVdm;
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
PEPROCESS Process;
|
|
PETHREAD Thread, MainThread;
|
|
PLIST_ENTRY Next;
|
|
PDELAYINTIRQ pDelayIntIrq;
|
|
|
|
UNREFERENCED_PARAMETER (SystemArgument1);
|
|
UNREFERENCED_PARAMETER (SystemArgument2);
|
|
|
|
FreeEntireVdm = FALSE;
|
|
|
|
//
|
|
// Get address of Process VdmObjects
|
|
//
|
|
|
|
Process = (PEPROCESS)DeferredContext;
|
|
pVdmObjects = (PVDM_PROCESS_OBJECTS)Process->VdmObjects;
|
|
|
|
ASSERT (KeGetCurrentIrql () == DISPATCH_LEVEL);
|
|
ExAcquireSpinLockAtDpcLevel(&pVdmObjects->DelayIntSpinLock);
|
|
|
|
//
|
|
// Search the DelayedIntList for the matching Dpc.
|
|
//
|
|
|
|
Next = pVdmObjects->DelayIntListHead.Flink;
|
|
while (Next != &pVdmObjects->DelayIntListHead) {
|
|
pDelayIntIrq = CONTAINING_RECORD(Next,DELAYINTIRQ,DelayIntListEntry);
|
|
if (&pDelayIntIrq->Dpc == Dpc) {
|
|
Thread = pDelayIntIrq->Thread;
|
|
pDelayIntIrq->Thread = NULL;
|
|
MainThread = pDelayIntIrq->MainThread;
|
|
pDelayIntIrq->MainThread = NULL;
|
|
if (pDelayIntIrq->InUse) {
|
|
|
|
if ((Thread && KeVdmInsertQueueApc(&pDelayIntIrq->Apc,
|
|
&Thread->Tcb,
|
|
KernelMode,
|
|
VdmpDelayIntApcRoutine,
|
|
VdmpRundownRoutine,
|
|
VdmpQueueIntNormalRoutine, // normal routine
|
|
NULL, // NormalContext
|
|
VDM_HWINT_INCREMENT
|
|
))
|
|
||
|
|
|
|
(MainThread && KeVdmInsertQueueApc(&pDelayIntIrq->Apc,
|
|
&MainThread->Tcb,
|
|
KernelMode,
|
|
VdmpDelayIntApcRoutine,
|
|
VdmpRundownRoutine,
|
|
VdmpQueueIntNormalRoutine, // normal routine
|
|
NULL, // NormalContext
|
|
VDM_HWINT_INCREMENT
|
|
)))
|
|
{
|
|
pDelayIntIrq->InUse = VDMDELAY_KAPC;
|
|
}
|
|
else {
|
|
// This hwinterrupt line is blocked forever.
|
|
pDelayIntIrq->InUse = VDMDELAY_NOTINUSE;
|
|
}
|
|
}
|
|
|
|
ExReleaseSpinLockFromDpcLevel(&pVdmObjects->DelayIntSpinLock);
|
|
|
|
if (Thread) {
|
|
ObDereferenceObject (Thread);
|
|
}
|
|
|
|
if (MainThread) {
|
|
ObDereferenceObject (MainThread);
|
|
}
|
|
|
|
ObDereferenceObject (Process);
|
|
return;
|
|
}
|
|
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
ExReleaseSpinLockFromDpcLevel(&pVdmObjects->DelayIntSpinLock);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
VdmpDelayIntApcRoutine (
|
|
IN PKAPC Apc,
|
|
IN PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN PVOID *NormalContext,
|
|
IN PVOID *SystemArgument1,
|
|
IN PVOID *SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the special APC routine that is called to
|
|
dispatch a delayed interrupt. This routine clears the IrqLine
|
|
bit, VdmpQueueIntApcRoutine will restart interrupts.
|
|
|
|
Arguments:
|
|
|
|
Apc - Supplies a pointer to the APC object used to invoke this routine.
|
|
|
|
NormalRoutine - Supplies a pointer to a pointer to an optional
|
|
normal routine, which is executed when wow is blocked.
|
|
|
|
NormalContext - Supplies a pointer to a pointer to an arbitrary data
|
|
structure that was specified when the APC was initialized and is
|
|
NOT USED.
|
|
|
|
SystemArgument1, SystemArgument2 - Supplies a set of two pointers to
|
|
two arguments that contain untyped data that are
|
|
NOT USED.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY Next;
|
|
PDELAYINTIRQ pDelayIntIrq;
|
|
KPROCESSOR_MODE ProcessorMode;
|
|
PULONG pDelayIrq;
|
|
PULONG pUndelayIrq;
|
|
PULONG pDelayIret;
|
|
ULONG IrqLine;
|
|
LOGICAL FreeIrqLine;
|
|
LOGICAL QueueApc;
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
|
|
UNREFERENCED_PARAMETER (NormalContext);
|
|
|
|
FreeIrqLine = FALSE;
|
|
IrqLine = 0; // satisfy no_opt compilation
|
|
|
|
//
|
|
// Clear address of thread object in APC object.
|
|
//
|
|
// N.B. The delay interrupt lock is used to synchronize access to APC
|
|
// objects that are manipulated by VDM.
|
|
//
|
|
|
|
pVdmObjects = PsGetCurrentProcess ()->VdmObjects;
|
|
ExAcquireFastMutex (&pVdmObjects->DelayIntFastMutex);
|
|
ExAcquireSpinLock (&pVdmObjects->DelayIntSpinLock, &OldIrql);
|
|
KeVdmClearApcThreadAddress (Apc);
|
|
|
|
//
|
|
// Search the DelayedIntList for the pDelayIntIrq.
|
|
//
|
|
|
|
Next = pVdmObjects->DelayIntListHead.Flink;
|
|
while (Next != &pVdmObjects->DelayIntListHead) {
|
|
|
|
pDelayIntIrq = CONTAINING_RECORD(Next,DELAYINTIRQ,DelayIntListEntry);
|
|
if (&pDelayIntIrq->Apc == Apc) {
|
|
|
|
//
|
|
// Found the IrqLine in the DelayedIntList, restart interrupts.
|
|
//
|
|
|
|
if (pDelayIntIrq->InUse) {
|
|
pDelayIntIrq->InUse = VDMDELAY_NOTINUSE;
|
|
IrqLine = pDelayIntIrq->IrqLine;
|
|
FreeIrqLine = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
ExReleaseSpinLock (&pVdmObjects->DelayIntSpinLock, OldIrql);
|
|
|
|
if (FreeIrqLine == FALSE) {
|
|
ExReleaseFastMutex (&pVdmObjects->DelayIntFastMutex);
|
|
return;
|
|
}
|
|
|
|
pDelayIrq = pVdmObjects->pIcaUserData->pDelayIrq;
|
|
pUndelayIrq = pVdmObjects->pIcaUserData->pUndelayIrq;
|
|
pDelayIret = pVdmObjects->pIcaUserData->pDelayIret;
|
|
|
|
QueueApc = FALSE;
|
|
|
|
try {
|
|
|
|
//
|
|
// These variables are being modified without holding the
|
|
// ICA lock. This should be OK because none of the ntvdm
|
|
// devices (timer, mouse etc. should ever do a delayed int
|
|
// while a previous delayed interrupt is still pending.
|
|
//
|
|
|
|
*pDelayIrq &= ~IrqLine;
|
|
InterlockedOr ((PLONG)pUndelayIrq, IrqLine);
|
|
|
|
//
|
|
// If we are waiting for an iret hook we have nothing left to do
|
|
// since the iret hook will restart interrupts.
|
|
//
|
|
|
|
if (!(IrqLine & *pDelayIret)) {
|
|
|
|
//
|
|
// set hardware int pending
|
|
//
|
|
|
|
InterlockedOr (FIXED_NTVDMSTATE_LINEAR_PC_AT, VDM_INT_HARDWARE);
|
|
|
|
//
|
|
// Queue a usermode APC to dispatch interrupts, note
|
|
// try protection is not needed.
|
|
//
|
|
|
|
if (NormalRoutine) {
|
|
QueueApc = TRUE;
|
|
}
|
|
}
|
|
}
|
|
except(VdmpExceptionHandler(GetExceptionInformation())) {
|
|
NOTHING;
|
|
}
|
|
|
|
if (QueueApc == TRUE) {
|
|
ProcessorMode = KernelMode;
|
|
VdmpQueueIntApcRoutine(Apc,
|
|
NormalRoutine,
|
|
(PVOID *)&ProcessorMode,
|
|
SystemArgument1,
|
|
SystemArgument2);
|
|
}
|
|
|
|
ExReleaseFastMutex(&pVdmObjects->DelayIntFastMutex);
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
VdmpDispatchableIntPending(
|
|
ULONG EFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether or not there is a dispatchable
|
|
virtual interrupt to dispatch.
|
|
|
|
Arguments:
|
|
|
|
EFlags -- supplies a pointer to the EFlags to be checked
|
|
|
|
Return Value:
|
|
|
|
True -- a virtual interrupt should be dispatched
|
|
False -- no virtual interrupt should be dispatched
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// The accesses to FIXED_NTVDMSTATE_LINEAR_PC_AT may be invalid so
|
|
// wrap this in an exception handler.
|
|
//
|
|
|
|
try {
|
|
|
|
if (EFlags & EFLAGS_V86_MASK) {
|
|
if (KeI386VirtualIntExtensions & V86_VIRTUAL_INT_EXTENSIONS) {
|
|
if(0 != (EFlags & EFLAGS_VIF)) {
|
|
return TRUE;
|
|
}
|
|
} else if (0 != (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_VIRTUAL_INTERRUPTS)) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
if ((*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_VIRTUAL_INTERRUPTS) == 0) {
|
|
VdmCheckPMCliTimeStamp();
|
|
}
|
|
|
|
//
|
|
// Check again. The call to VdmCheckPMCliTimeStamp may enable it.
|
|
//
|
|
|
|
if (0 != (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_VIRTUAL_INTERRUPTS)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
NOTHING;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS
|
|
VdmpIsThreadTerminating(
|
|
HANDLE ThreadId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the specified thread is terminating or not.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
True --
|
|
False -
|
|
|
|
--*/
|
|
|
|
{
|
|
CLIENT_ID Cid;
|
|
PETHREAD Thread;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If the owning thread juest exited the IcaLock the
|
|
// OwningThread Tid may be NULL, return success, since
|
|
// we don't know what the owning thread's state was.
|
|
//
|
|
|
|
if (!ThreadId) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Cid.UniqueProcess = NtCurrentTeb()->ClientId.UniqueProcess;
|
|
Cid.UniqueThread = ThreadId;
|
|
|
|
Status = PsLookupProcessThreadByCid (&Cid, NULL, &Thread);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = PsIsThreadTerminating(Thread) ? STATUS_THREAD_IS_TERMINATING
|
|
: STATUS_SUCCESS;
|
|
ObDereferenceObject(Thread);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
VdmpRundownRoutine (
|
|
IN PKAPC Apc
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The function is the rundown routine for VDM APCs and is called on thread
|
|
termination. The fact that this function is called means that none of the
|
|
APC objects specified by the process VDM structure will not get freed.
|
|
They must be freed when the process terminates.
|
|
|
|
Arguments:
|
|
|
|
Apc - Supplies a pointer to an APC object to be rundown.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Clear the Irqline, but don't requeue the APC.
|
|
//
|
|
|
|
VdmpDelayIntApcRoutine(Apc, NULL, NULL, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
int
|
|
VdmpExceptionHandler (
|
|
IN PEXCEPTION_POINTERS ExceptionInfo
|
|
)
|
|
{
|
|
|
|
#if DBG
|
|
PEXCEPTION_RECORD ExceptionRecord;
|
|
ULONG NumberParameters;
|
|
PULONG ExceptionInformation;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
|
|
ExceptionRecord = ExceptionInfo->ExceptionRecord;
|
|
DbgPrint("VdmExRecord ExCode %x Flags %x Address %x\n",
|
|
ExceptionRecord->ExceptionCode,
|
|
ExceptionRecord->ExceptionFlags,
|
|
ExceptionRecord->ExceptionAddress
|
|
);
|
|
|
|
NumberParameters = ExceptionRecord->NumberParameters;
|
|
if (NumberParameters) {
|
|
DbgPrint("VdmExRecord Parameters:\n");
|
|
|
|
ExceptionInformation = ExceptionRecord->ExceptionInformation;
|
|
while (NumberParameters--) {
|
|
DbgPrint("\t%x\n", *ExceptionInformation);
|
|
}
|
|
}
|
|
|
|
#else
|
|
UNREFERENCED_PARAMETER (ExceptionInfo);
|
|
#endif
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|