Leaked source code of windows server 2003
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

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. vdmints.c
  5. Abstract:
  6. Vdm kernel Virtual interrupt support
  7. Author:
  8. 13-Oct-1993 Jonathan Lew (Jonle)
  9. Notes:
  10. Revision History:
  11. --*/
  12. #include "vdmp.h"
  13. #include <ntos.h>
  14. #include <zwapi.h>
  15. //
  16. // Define thread priority boost for vdm hardware interrupt.
  17. //
  18. #define VDM_HWINT_INCREMENT EVENT_INCREMENT
  19. //
  20. // internal function prototypes
  21. //
  22. VOID
  23. VdmpQueueIntApcRoutine (
  24. IN PKAPC Apc,
  25. IN PKNORMAL_ROUTINE *NormalRoutine,
  26. IN PVOID *NormalContext,
  27. IN PVOID *SystemArgument1,
  28. IN PVOID *SystemArgument2
  29. );
  30. VOID
  31. VdmpQueueIntNormalRoutine (
  32. IN PVOID NormalContext,
  33. IN PVOID SystemArgument1,
  34. IN PVOID SystemArgument2
  35. );
  36. VOID
  37. VdmpDelayIntDpcRoutine (
  38. IN PKDPC Dpc,
  39. IN PVOID DeferredContext,
  40. IN PVOID SystemArgument1,
  41. IN PVOID SystemArgument2
  42. );
  43. VOID
  44. VdmpDelayIntApcRoutine (
  45. IN PKAPC Apc,
  46. IN PKNORMAL_ROUTINE *NormalRoutine,
  47. IN PVOID *NormalContext,
  48. IN PVOID *SystemArgument1,
  49. IN PVOID *SystemArgument2
  50. );
  51. int
  52. VdmpRestartDelayedInterrupts (
  53. PVDMICAUSERDATA pIcaUserData
  54. );
  55. int
  56. VdmpIcaScan (
  57. PVDMICAUSERDATA pIcaUserData,
  58. PVDMVIRTUALICA pIcaAdapter
  59. );
  60. int
  61. VdmpIcaAccept (
  62. PVDMICAUSERDATA pIcaUserData,
  63. PVDMVIRTUALICA pIcaAdapter
  64. );
  65. ULONG
  66. GetIretHookAddress (
  67. PKTRAP_FRAME TrapFrame,
  68. PVDMICAUSERDATA pIcaUserData,
  69. int IrqNum
  70. );
  71. VOID
  72. PushRmInterrupt (
  73. PKTRAP_FRAME TrapFrame,
  74. ULONG IretHookAddress,
  75. PVDM_TIB VdmTib,
  76. ULONG InterruptNumber
  77. );
  78. NTSTATUS
  79. PushPmInterrupt (
  80. PKTRAP_FRAME TrapFrame,
  81. ULONG IretHookAddress,
  82. PVDM_TIB VdmTib,
  83. ULONG InterruptNumber
  84. );
  85. VOID
  86. VdmpRundownRoutine (
  87. IN PKAPC Apc
  88. );
  89. NTSTATUS
  90. VdmpEnterIcaLock (
  91. IN PRTL_CRITICAL_SECTION pIcaLock,
  92. IN PLARGE_INTEGER Timeout
  93. );
  94. NTSTATUS
  95. VdmpLeaveIcaLock (
  96. IN PRTL_CRITICAL_SECTION pIcaLock
  97. );
  98. int
  99. VdmpExceptionHandler (
  100. IN PEXCEPTION_POINTERS ExceptionInfo
  101. );
  102. #pragma alloc_text(PAGE, VdmpQueueIntNormalRoutine)
  103. #pragma alloc_text(PAGE, VdmDispatchInterrupts)
  104. #pragma alloc_text(PAGE, VdmpRestartDelayedInterrupts)
  105. #pragma alloc_text(PAGE, VdmpIcaScan)
  106. #pragma alloc_text(PAGE, VdmpIcaAccept)
  107. #pragma alloc_text(PAGE, GetIretHookAddress)
  108. #pragma alloc_text(PAGE, PushRmInterrupt)
  109. #pragma alloc_text(PAGE, PushPmInterrupt)
  110. #pragma alloc_text(PAGE, VdmpDispatchableIntPending)
  111. #pragma alloc_text(PAGE, VdmpIsThreadTerminating)
  112. #pragma alloc_text(PAGE, VdmpRundownRoutine)
  113. #pragma alloc_text(PAGE, VdmpExceptionHandler)
  114. #pragma alloc_text(PAGE, VdmpEnterIcaLock)
  115. #pragma alloc_text(PAGE, VdmpLeaveIcaLock)
  116. extern POBJECT_TYPE ExSemaphoreObjectType;
  117. extern POBJECT_TYPE ExEventObjectType;
  118. #if DBG
  119. //
  120. // Make this variable nonzero to enable stricter ntvdm checking. Note this
  121. // cannot be left on by default because a malicious app can provoke the asserts.
  122. //
  123. ULONG VdmStrict;
  124. #endif
  125. NTSTATUS
  126. VdmpQueueInterrupt(
  127. IN HANDLE ThreadHandle
  128. )
  129. /*++
  130. Routine Description:
  131. Queues a user mode APC to the specifed application thread
  132. which will dispatch an interrupt.
  133. if APC is already queued to specified thread
  134. does nothing
  135. if APC is queued to the wrong thread
  136. dequeue it
  137. Reset the user APC for the specifed thread
  138. Insert the APC in the queue for the specifed thread
  139. Arguments:
  140. ThreadHandle - handle of thread to insert QueueIntApcRoutine
  141. Return Value:
  142. NTSTATUS.
  143. --*/
  144. {
  145. KIRQL OldIrql;
  146. PEPROCESS Process;
  147. PETHREAD Thread;
  148. NTSTATUS Status;
  149. PVDM_PROCESS_OBJECTS pVdmObjects;
  150. PAGED_CODE();
  151. Status = ObReferenceObjectByHandle(ThreadHandle,
  152. THREAD_QUERY_INFORMATION,
  153. PsThreadType,
  154. KeGetPreviousMode(),
  155. &Thread,
  156. NULL
  157. );
  158. if (!NT_SUCCESS(Status)) {
  159. return Status;
  160. }
  161. Process = PsGetCurrentProcess();
  162. if (Process != Thread->ThreadsProcess || Process->VdmObjects == NULL) {
  163. Status = STATUS_INVALID_PARAMETER_1;
  164. }
  165. else {
  166. //
  167. // Insert kernel APC.
  168. //
  169. // N.B. The delay interrupt lock is used to synchronize access to APC
  170. // objects that are manipulated by VDM.
  171. //
  172. pVdmObjects = Process->VdmObjects;
  173. ExAcquireSpinLock(&pVdmObjects->DelayIntSpinLock, &OldIrql);
  174. if (!KeVdmInsertQueueApc(&pVdmObjects->QueuedIntApc,
  175. &Thread->Tcb,
  176. KernelMode,
  177. VdmpQueueIntApcRoutine,
  178. VdmpRundownRoutine,
  179. VdmpQueueIntNormalRoutine, // normal routine
  180. (PVOID)KernelMode, // NormalContext
  181. VDM_HWINT_INCREMENT))
  182. {
  183. Status = STATUS_UNSUCCESSFUL;
  184. }
  185. else
  186. {
  187. Status = STATUS_SUCCESS;
  188. }
  189. ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
  190. }
  191. ObDereferenceObject(Thread);
  192. return Status;
  193. }
  194. VOID
  195. VdmpQueueIntApcRoutine (
  196. IN PKAPC Apc,
  197. IN PKNORMAL_ROUTINE *NormalRoutine,
  198. IN PVOID *NormalContext,
  199. IN PVOID *SystemArgument1,
  200. IN PVOID *SystemArgument2
  201. )
  202. /*++
  203. Routine Description:
  204. Kernel and User mode Special Apc routine to dispatch virtual
  205. interrupts to the vdm.
  206. For KernelMode routine:
  207. if vdm is running in application mode
  208. queue a UserModeApc to the same thread
  209. else do nothing
  210. For UserMode routine
  211. if vdm is running in application mode dispatch virtual interrupts
  212. else do nothing
  213. Arguments:
  214. Apc - Supplies a pointer to the APC object used to invoke this routine.
  215. NormalRoutine - Supplies a pointer to a pointer to the normal routine
  216. function that was specified when the APC was initialized.
  217. NormalContext - Supplies a pointer to the processor mode
  218. specifying that this is a Kernel Mode or UserMode apc
  219. SystemArgument1 -
  220. SystemArgument2 - NOT USED
  221. Supplies a set of two pointers to two arguments that contain
  222. untyped data.
  223. Return Value:
  224. None.
  225. --*/
  226. {
  227. LONG VdmState;
  228. KIRQL OldIrql;
  229. PVDM_PROCESS_OBJECTS pVdmObjects;
  230. NTSTATUS Status;
  231. PETHREAD Thread;
  232. PKTRAP_FRAME TrapFrame;
  233. PVDM_TIB VdmTib;
  234. BOOLEAN AppMode;
  235. PAGED_CODE();
  236. UNREFERENCED_PARAMETER (SystemArgument1);
  237. UNREFERENCED_PARAMETER (SystemArgument2);
  238. //
  239. // Clear address of thread object in APC object.
  240. //
  241. // N.B. The delay interrupt lock is used to synchronize access to APC
  242. // objects that are manipulated by VDM.
  243. //
  244. pVdmObjects = PsGetCurrentProcess()->VdmObjects;
  245. ExAcquireSpinLock(&pVdmObjects->DelayIntSpinLock, &OldIrql);
  246. KeVdmClearApcThreadAddress(Apc);
  247. ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
  248. //
  249. // Get the trap frame for the current thread if it is not terminating.
  250. //
  251. Thread = PsGetCurrentThread();
  252. if (PsIsThreadTerminating(Thread)) {
  253. return;
  254. }
  255. TrapFrame = VdmGetTrapFrame(&Thread->Tcb);
  256. AppMode = (BOOLEAN)(TrapFrame->EFlags & EFLAGS_V86_MASK ||
  257. TrapFrame->SegCs != (KGDT_R3_CODE | RPL_MASK));
  258. try {
  259. //
  260. // If we are in the middle of screen switch, send the main thread
  261. // back to the monitor context to be suspended there.
  262. //
  263. if (*(KPROCESSOR_MODE *)NormalContext == UserMode) {
  264. if (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_HANDSHAKE && AppMode) {
  265. Status = VdmpGetVdmTib(&VdmTib);
  266. if (NT_SUCCESS(Status)) {
  267. *FIXED_NTVDMSTATE_LINEAR_PC_AT = *FIXED_NTVDMSTATE_LINEAR_PC_AT & ~VDM_HANDSHAKE;
  268. VdmTib->EventInfo.Event = VdmHandShakeAck;
  269. VdmTib->EventInfo.InstructionSize = 0;
  270. VdmTib->EventInfo.IntAckInfo = 0;
  271. VdmEndExecution(TrapFrame, VdmTib);
  272. KeBoostPriorityThread (KeGetCurrentThread(), VDM_HWINT_INCREMENT);
  273. }
  274. return;
  275. }
  276. } else if (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_HANDSHAKE && AppMode) {
  277. //
  278. // If we are running at application mode and in the middle of screen
  279. // switch, we will signal the event to let screen switch continue.
  280. // This is fine because:
  281. // 1. The incoming user apc will send the main thread back to monitor context
  282. // 2. The kernel mode IO handlers are running at APC level. If we can get
  283. // here, the IO handlers are done
  284. //
  285. HANDLE SuspendedHandle;
  286. PKEVENT SuspendedEvent;
  287. try {
  288. SuspendedHandle = *pVdmObjects->pIcaUserData->phMainThreadSuspended;
  289. Status = ObReferenceObjectByHandle (SuspendedHandle,
  290. EVENT_MODIFY_STATE,
  291. ExEventObjectType,
  292. UserMode,
  293. &SuspendedEvent,
  294. NULL);
  295. if (NT_SUCCESS(Status)) {
  296. KeSetEvent(SuspendedEvent, EVENT_INCREMENT, FALSE);
  297. ObDereferenceObject(SuspendedEvent);
  298. }
  299. } except(EXCEPTION_EXECUTE_HANDLER) {
  300. }
  301. }
  302. //
  303. // if no pending interrupts, ignore this APC.
  304. //
  305. if (!(*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INTERRUPT_PENDING)) {
  306. return;
  307. }
  308. if (VdmpDispatchableIntPending(TrapFrame->EFlags)) {
  309. //
  310. // if we are in v86 mode or segmented protected mode
  311. // then queue the UserMode Apc, which will dispatch
  312. // the hardware interrupt
  313. //
  314. if ((TrapFrame->EFlags & EFLAGS_V86_MASK) ||
  315. (TrapFrame->SegCs != (KGDT_R3_CODE | RPL_MASK))) {
  316. if (*(KPROCESSOR_MODE *)NormalContext == KernelMode) {
  317. //
  318. // Insert user APC.
  319. //
  320. // N.B. The delay interrupt lock is used to synchronize
  321. // access to APC objects that are manipulated by VDM.
  322. //
  323. VdmState = *FIXED_NTVDMSTATE_LINEAR_PC_AT;
  324. ExAcquireSpinLock(&pVdmObjects->DelayIntSpinLock,
  325. &OldIrql);
  326. KeVdmInsertQueueApc(&pVdmObjects->QueuedIntUserApc,
  327. &Thread->Tcb,
  328. UserMode,
  329. VdmpQueueIntApcRoutine,
  330. VdmpRundownRoutine,
  331. NULL, // normal routine
  332. (PVOID)UserMode, // NormalContext
  333. VdmState & VDM_INT_HARDWARE
  334. ? VDM_HWINT_INCREMENT : 0);
  335. ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
  336. }
  337. else {
  338. ASSERT(*NormalContext == (PVOID)UserMode);
  339. Status = VdmpGetVdmTib(&VdmTib);
  340. if (!NT_SUCCESS(Status)) {
  341. return;
  342. }
  343. //VdmTib = (PsGetCurrentProcess()->VdmObjects)->VdmTib;
  344. // VdmTib =
  345. // ((PVDM_PROCESS_OBJECTS)(PsGetCurrentProcess()->VdmObjects))->VdmTib;
  346. //
  347. // If there are no hardware ints, dispatch timer ints
  348. // else dispatch hw interrupts
  349. //
  350. if (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INT_TIMER &&
  351. !(*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INT_HARDWARE))
  352. {
  353. VdmTib->EventInfo.Event = VdmIntAck;
  354. VdmTib->EventInfo.InstructionSize = 0;
  355. VdmTib->EventInfo.IntAckInfo = 0;
  356. VdmEndExecution(TrapFrame, VdmTib);
  357. }
  358. else {
  359. VdmDispatchInterrupts (TrapFrame, VdmTib);
  360. }
  361. }
  362. }
  363. else {
  364. //
  365. // If we are not in application mode and wow is all blocked
  366. // then Wake up WowExec by setting the wow idle event
  367. //
  368. if (*NormalRoutine && !(*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_WOWBLOCKED)) {
  369. *NormalRoutine = NULL;
  370. }
  371. }
  372. }
  373. // WARNING this may set VIP for flat if VPI is ever set in CR4
  374. else if ((TrapFrame->EFlags & EFLAGS_V86_MASK) &&
  375. (KeI386VirtualIntExtensions & V86_VIRTUAL_INT_EXTENSIONS)) {
  376. //
  377. // The CPU traps EVERY instruction if VIF and VIP are both ON.
  378. // Make sure that you set VIP ON only when there are pending
  379. // interrupts, i.e. (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INTERRUPT_PENDING) != 0.
  380. //
  381. #if DBG
  382. if (VdmStrict) {
  383. ASSERT(*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_INTERRUPT_PENDING);
  384. }
  385. #endif
  386. ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
  387. TrapFrame->EFlags |= EFLAGS_VIP;
  388. }
  389. }
  390. except(VdmpExceptionHandler(GetExceptionInformation())) {
  391. #if 0
  392. VdmDispatchException(TrapFrame,
  393. GetExceptionCode(),
  394. (PVOID)TrapFrame->Eip,
  395. 0,0,0,0 // no parameters
  396. );
  397. #endif
  398. }
  399. }
  400. VOID
  401. VdmpQueueIntNormalRoutine (
  402. IN PVOID NormalContext,
  403. IN PVOID SystemArgument1,
  404. IN PVOID SystemArgument2
  405. )
  406. /*++
  407. Routine Description:
  408. Arguments:
  409. Return Value:
  410. None.
  411. --*/
  412. {
  413. PETHREAD Thread;
  414. PKEVENT Event;
  415. NTSTATUS Status;
  416. PKTRAP_FRAME TrapFrame;
  417. PVDM_PROCESS_OBJECTS pVdmObjects;
  418. HANDLE CapturedHandle;
  419. UNREFERENCED_PARAMETER (NormalContext);
  420. UNREFERENCED_PARAMETER (SystemArgument1);
  421. UNREFERENCED_PARAMETER (SystemArgument2);
  422. //
  423. // Wake up WowExec by setting the wow idle event
  424. //
  425. pVdmObjects = PsGetCurrentProcess()->VdmObjects;
  426. try {
  427. CapturedHandle = *pVdmObjects->pIcaUserData->phWowIdleEvent;
  428. }
  429. except(VdmpExceptionHandler(GetExceptionInformation())) {
  430. Thread = PsGetCurrentThread();
  431. TrapFrame = VdmGetTrapFrame(&Thread->Tcb);
  432. #if 0
  433. VdmDispatchException(TrapFrame,
  434. GetExceptionCode(),
  435. (PVOID)TrapFrame->Eip,
  436. 0,0,0,0 // no parameters
  437. );
  438. #endif
  439. return;
  440. }
  441. Status = ObReferenceObjectByHandle (CapturedHandle,
  442. EVENT_MODIFY_STATE,
  443. ExEventObjectType,
  444. UserMode,
  445. &Event,
  446. NULL);
  447. if (NT_SUCCESS(Status)) {
  448. KeSetEvent(Event, EVENT_INCREMENT, FALSE);
  449. ObDereferenceObject(Event);
  450. }
  451. }
  452. VOID
  453. VdmRundownDpcs (
  454. IN PEPROCESS Process
  455. )
  456. {
  457. PVDM_PROCESS_OBJECTS pVdmObjects;
  458. PETHREAD Thread, MainThread;
  459. PDELAYINTIRQ pDelayIntIrq;
  460. KIRQL OldIrql;
  461. PLIST_ENTRY Next;
  462. pVdmObjects = Process->VdmObjects;
  463. //
  464. // Free up the DelayedIntList, canceling pending timers.
  465. //
  466. KeAcquireSpinLock (&pVdmObjects->DelayIntSpinLock, &OldIrql);
  467. Next = pVdmObjects->DelayIntListHead.Flink;
  468. while (Next != &pVdmObjects->DelayIntListHead) {
  469. pDelayIntIrq = CONTAINING_RECORD(Next, DELAYINTIRQ, DelayIntListEntry);
  470. Next = Next->Flink;
  471. if (KeCancelTimer (&pDelayIntIrq->Timer)) {
  472. Thread = pDelayIntIrq->Thread;
  473. pDelayIntIrq->Thread = NULL;
  474. if (Thread != NULL) {
  475. ObDereferenceObject (Thread);
  476. }
  477. MainThread = pDelayIntIrq->MainThread;
  478. pDelayIntIrq->MainThread = NULL;
  479. if (MainThread != NULL) {
  480. ObDereferenceObject (MainThread);
  481. }
  482. ObDereferenceObject (Process);
  483. }
  484. }
  485. if (pVdmObjects->MainThread != NULL) {
  486. ObDereferenceObject (pVdmObjects->MainThread);
  487. pVdmObjects->MainThread = NULL;
  488. }
  489. KeReleaseSpinLock (&pVdmObjects->DelayIntSpinLock, OldIrql);
  490. }
  491. NTSTATUS
  492. VdmpEnterIcaLock (
  493. IN PRTL_CRITICAL_SECTION pIcaLock,
  494. IN PLARGE_INTEGER Timeout
  495. )
  496. /*++
  497. Routine Description:
  498. This function enters a usermode critical section, with a fixed timeout
  499. of several minutes.
  500. Touching the critical section may cause an exception to be raised which
  501. the caller must handle, since the critical section is in usermode
  502. memory.
  503. Arguments:
  504. CriticalSection - supplies a pointer to a critical section.
  505. Timeout - supplies a pointer to a large integer which specifies the timeout value
  506. for waiting on critical section.
  507. Return Value:
  508. STATUS_SUCCESS - wait was satisfied and the thread owns the CS
  509. STATUS_INVALID_HANDLE - no semaphore available to wait on.
  510. STATUS_TIMEOUT
  511. --*/
  512. {
  513. HANDLE UniqueThread;
  514. NTSTATUS Status;
  515. UniqueThread = NtCurrentTeb()->ClientId.UniqueThread;
  516. if (pIcaLock->LockSemaphore == 0) {
  517. //
  518. // Lazy creates not permitted.
  519. //
  520. return STATUS_INVALID_HANDLE;
  521. }
  522. if (InterlockedIncrement (&pIcaLock->LockCount) == 0) {
  523. //
  524. // Set the current thread as the owner of critical section with
  525. // recursion count of 1.
  526. //
  527. pIcaLock->OwningThread = UniqueThread;
  528. pIcaLock->RecursionCount = 1;
  529. return STATUS_SUCCESS;
  530. }
  531. //
  532. // If the current thread already owns the critical section, increment
  533. // the recursion count.
  534. //
  535. if (pIcaLock->OwningThread == UniqueThread) {
  536. pIcaLock->RecursionCount += 1;
  537. return STATUS_SUCCESS;
  538. }
  539. //
  540. // Another thread owns the critical section so wait on the
  541. // lock semaphore.
  542. //
  543. do {
  544. Status = NtWaitForSingleObject (pIcaLock->LockSemaphore,
  545. 0,
  546. Timeout);
  547. if (Status == STATUS_SUCCESS) {
  548. pIcaLock->OwningThread = UniqueThread;
  549. pIcaLock->RecursionCount = 1;
  550. return STATUS_SUCCESS;
  551. }
  552. //
  553. // If !NT_SUCCESS(Status), return that error.
  554. //
  555. // Otherwise some other less severe error occurred, in which case
  556. // if the thread is terminating then fail.
  557. //
  558. // Note: we may wake for user APCs even though we are non alertable,
  559. // because the vdm hw int dispatching code, and PsThread
  560. // termination code forces these to occur.
  561. //
  562. if (!NT_SUCCESS(Status)) {
  563. return Status;
  564. }
  565. //
  566. // Check for termination of self.
  567. //
  568. Status = VdmpIsThreadTerminating (UniqueThread);
  569. if (Status != STATUS_SUCCESS) {
  570. return Status;
  571. }
  572. Status = VdmpIsThreadTerminating (pIcaLock->OwningThread);
  573. if (Status != STATUS_SUCCESS) {
  574. return Status;
  575. }
  576. } while (TRUE);
  577. }
  578. NTSTATUS
  579. VdmpLeaveIcaLock (
  580. IN PRTL_CRITICAL_SECTION pIcaLock
  581. )
  582. /*++
  583. Routine Description:
  584. This function leaves a usermode critical section.
  585. Touching the critical section may cause an exception to be raised which
  586. the caller must handle, since the critical section is in usermode
  587. memory.
  588. Arguments:
  589. CriticalSection - supplies a pointer to a critical section.
  590. Return Value:
  591. STATUS_SUCCESS
  592. STATUS_TIMEOUT
  593. STATUS_INVALID_OWNER
  594. or NTSTATUS code from NtReleaseSemaphore
  595. --*/
  596. {
  597. HANDLE UniqueThread;
  598. UniqueThread = NtCurrentTeb()->ClientId.UniqueThread;
  599. if (pIcaLock->OwningThread != UniqueThread) {
  600. return STATUS_INVALID_OWNER;
  601. }
  602. pIcaLock->RecursionCount -= 1;
  603. if (pIcaLock->RecursionCount != 0) {
  604. InterlockedDecrement (&pIcaLock->LockCount);
  605. return STATUS_SUCCESS;
  606. }
  607. pIcaLock->OwningThread = NULL;
  608. if (InterlockedDecrement (&pIcaLock->LockCount) < 0) {
  609. return STATUS_SUCCESS;
  610. }
  611. //
  612. // Threads are waiting on the lock semaphore, signal one now.
  613. //
  614. return NtSetEvent (pIcaLock->LockSemaphore, 0);
  615. }
  616. NTSTATUS
  617. VdmDispatchInterrupts (
  618. PKTRAP_FRAME TrapFrame,
  619. PVDM_TIB VdmTib
  620. )
  621. /*++
  622. Routine Description:
  623. This routine dispatches interrupts to the vdm.
  624. Assumes that we are in application mode and NOT MONITOR context.
  625. This routine may switch from application context back to monitor
  626. context, if it cannot handle the interrupt (Ica in AEOI, or timer
  627. int pending).
  628. Arguments:
  629. TrapFrame address of current trapframe
  630. VdmTib address of current vdm tib
  631. Return Value:
  632. None.
  633. --*/
  634. {
  635. NTSTATUS Status;
  636. ULONG IretHookAddress;
  637. ULONG InterruptNumber;
  638. int IrqLineNum;
  639. PVDMICAUSERDATA pIcaUserData;
  640. PVDMVIRTUALICA pIcaAdapter;
  641. VDMEVENTCLASS VdmEvent = VdmMaxEvent;
  642. PAGED_CODE();
  643. pIcaUserData = ((PVDM_PROCESS_OBJECTS)PsGetCurrentProcess()->VdmObjects)->pIcaUserData;
  644. try {
  645. //
  646. // Take the Ica Lock, if this fails raise status as we can't
  647. // safely recover the critical section state
  648. //
  649. Status = VdmpEnterIcaLock (pIcaUserData->pIcaLock, pIcaUserData->pIcaTimeout);
  650. if (!NT_SUCCESS(Status)) {
  651. ExRaiseStatus(Status);
  652. }
  653. if (*pIcaUserData->pUndelayIrq) {
  654. VdmpRestartDelayedInterrupts(pIcaUserData);
  655. }
  656. VDIretry:
  657. //
  658. // Clear the VIP bit
  659. //
  660. if ((TrapFrame->EFlags & EFLAGS_V86_MASK) &&
  661. (KeI386VirtualIntExtensions & V86_VIRTUAL_INT_EXTENSIONS)) {
  662. ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
  663. TrapFrame->EFlags &= ~EFLAGS_VIP;
  664. }
  665. //
  666. // Mark the vdm state as hw int dispatched. Must use the lock as
  667. // kernel mode DelayedIntApcRoutine changes the bit as well
  668. //
  669. InterlockedAnd (FIXED_NTVDMSTATE_LINEAR_PC_AT, ~VDM_INT_HARDWARE);
  670. pIcaAdapter = pIcaUserData->pIcaMaster;
  671. IrqLineNum = VdmpIcaAccept(pIcaUserData, pIcaAdapter);
  672. if (IrqLineNum >= 0) {
  673. UCHAR bit = (UCHAR)(1 << IrqLineNum);
  674. if (pIcaUserData->pIcaMaster->ica_ssr & bit) {
  675. pIcaAdapter = pIcaUserData->pIcaSlave;
  676. IrqLineNum = VdmpIcaAccept(pIcaUserData, pIcaAdapter);
  677. if (IrqLineNum < 0) {
  678. pIcaUserData->pIcaMaster->ica_isr &= ~bit;
  679. }
  680. }
  681. }
  682. //
  683. // Skip spurious ints
  684. //
  685. if (IrqLineNum < 0) {
  686. //
  687. // Check for delayed interrupts which need to be restarted
  688. //
  689. if (*pIcaUserData->pUndelayIrq &&
  690. VdmpRestartDelayedInterrupts(pIcaUserData) != -1) {
  691. goto VDIretry;
  692. }
  693. Status = VdmpLeaveIcaLock (pIcaUserData->pIcaLock);
  694. if (!NT_SUCCESS(Status)) {
  695. ExRaiseStatus(Status);
  696. }
  697. return Status;
  698. }
  699. //
  700. // Capture the AutoEoi mode case for special handling
  701. //
  702. if (pIcaAdapter->ica_mode & ICA_AEOI) {
  703. VdmEvent = VdmIntAck;
  704. VdmTib->EventInfo.IntAckInfo = VDMINTACK_AEOI;
  705. if (pIcaAdapter == pIcaUserData->pIcaSlave) {
  706. VdmTib->EventInfo.IntAckInfo |= VDMINTACK_SLAVE;
  707. }
  708. }
  709. InterruptNumber = IrqLineNum + pIcaAdapter->ica_base;
  710. //
  711. // Get the IretHookAddress ... if any
  712. //
  713. if (pIcaAdapter == pIcaUserData->pIcaSlave) {
  714. IrqLineNum += 8;
  715. }
  716. IretHookAddress = GetIretHookAddress (TrapFrame,
  717. pIcaUserData,
  718. IrqLineNum);
  719. if (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_TRACE_HISTORY) {
  720. VdmTraceEvent (VDMTR_KERNEL_HW_INT,
  721. (USHORT)InterruptNumber,
  722. 0,
  723. TrapFrame);
  724. }
  725. //
  726. // Push the interrupt frames
  727. //
  728. if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
  729. PushRmInterrupt (TrapFrame,
  730. IretHookAddress,
  731. VdmTib,
  732. InterruptNumber);
  733. }
  734. else {
  735. Status = PushPmInterrupt (TrapFrame,
  736. IretHookAddress,
  737. VdmTib,
  738. InterruptNumber);
  739. if (!NT_SUCCESS(Status)) {
  740. VdmpLeaveIcaLock (pIcaUserData->pIcaLock);
  741. ExRaiseStatus (Status);
  742. }
  743. }
  744. //
  745. // Disable interrupts and the trap flag
  746. //
  747. if ((TrapFrame->EFlags & EFLAGS_V86_MASK) &&
  748. (KeI386VirtualIntExtensions & V86_VIRTUAL_INT_EXTENSIONS)) {
  749. TrapFrame->EFlags &= ~EFLAGS_VIF;
  750. } else {
  751. *FIXED_NTVDMSTATE_LINEAR_PC_AT &= ~VDM_VIRTUAL_INTERRUPTS;
  752. }
  753. ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
  754. TrapFrame->EFlags &= ~(EFLAGS_NT_MASK | EFLAGS_TF_MASK);
  755. KeBoostPriorityThread (KeGetCurrentThread(), VDM_HWINT_INCREMENT);
  756. //
  757. // Release the Ica lock
  758. //
  759. Status = VdmpLeaveIcaLock (pIcaUserData->pIcaLock);
  760. if (!NT_SUCCESS(Status)) {
  761. ExRaiseStatus (Status);
  762. }
  763. //
  764. // check to see if we are supposed to switch back to monitor context
  765. //
  766. if (VdmEvent != VdmMaxEvent) {
  767. VdmTib->EventInfo.Event = VdmIntAck;
  768. VdmTib->EventInfo.InstructionSize = 0;
  769. VdmEndExecution(TrapFrame, VdmTib);
  770. }
  771. }
  772. except (VdmpExceptionHandler(GetExceptionInformation())) {
  773. Status = GetExceptionCode();
  774. }
  775. return Status;
  776. }
  777. int
  778. VdmpRestartDelayedInterrupts (
  779. PVDMICAUSERDATA pIcaUserData
  780. )
  781. {
  782. int line;
  783. PAGED_CODE();
  784. try {
  785. *pIcaUserData->pUndelayIrq = 0;
  786. line = VdmpIcaScan(pIcaUserData, pIcaUserData->pIcaSlave);
  787. if (line != -1) {
  788. // set the slave
  789. pIcaUserData->pIcaSlave->ica_int_line = line;
  790. pIcaUserData->pIcaSlave->ica_cpu_int = TRUE;
  791. // set the master cascade
  792. line = pIcaUserData->pIcaSlave->ica_ssr;
  793. pIcaUserData->pIcaMaster->ica_irr |= 1 << line;
  794. pIcaUserData->pIcaMaster->ica_count[line]++;
  795. }
  796. line = VdmpIcaScan(pIcaUserData, pIcaUserData->pIcaMaster);
  797. if (line != -1) {
  798. pIcaUserData->pIcaMaster->ica_cpu_int = TRUE;
  799. pIcaUserData->pIcaMaster->ica_int_line = TRUE;
  800. }
  801. }
  802. except(EXCEPTION_EXECUTE_HANDLER) {
  803. line = -1;
  804. NOTHING;
  805. }
  806. return line;
  807. }
  808. int
  809. VdmpIcaScan (
  810. PVDMICAUSERDATA pIcaUserData,
  811. PVDMVIRTUALICA pIcaAdapter
  812. )
  813. /*++
  814. Routine Description:
  815. Similar to softpc\base\system\ica.c - scan_irr(),
  816. Check the IRR, the IMR and the ISR to determine which interrupt
  817. should be delivered.
  818. A bit set in the IRR will generate an interrupt if:
  819. IMR bit, DelayIret bit, DelayIrq bit AND ISR higher priority bits
  820. are clear (unless Special Mask Mode, in which case ISR is ignored)
  821. If there is no bit set, then return -1
  822. Arguments:
  823. PVDMICAUSERDATA pIcaUserData - addr of ica userdata
  824. PVDMVIRTUALICA pIcaAdapter - addr of ica adapter
  825. Return Value:
  826. int IrqLineNum for the specific adapter (0 to 7)
  827. -1 for none
  828. --*/
  829. {
  830. int i,line;
  831. UCHAR bit;
  832. ULONG IrrImrDelay;
  833. ULONG ActiveIsr;
  834. PAGED_CODE();
  835. IrrImrDelay = *pIcaUserData->pDelayIrq | *pIcaUserData->pDelayIret;
  836. if (pIcaAdapter == pIcaUserData->pIcaSlave) {
  837. IrrImrDelay >>= 8;
  838. }
  839. IrrImrDelay = pIcaAdapter->ica_irr & ~(pIcaAdapter->ica_imr | (UCHAR)IrrImrDelay);
  840. if (IrrImrDelay) {
  841. /*
  842. * Does the current mode require the ica to prevent
  843. * interrupts if that line is still active (ie in the isr)?
  844. *
  845. * Normal Case: Used by DOS and Win3.1/S the isr prevents interrupts.
  846. * Special Mask Mode, Special Fully Nested Mode do not block
  847. * interrupts using bits in the isr. SMM is the mode used
  848. * by Windows95 and Win3.1/E.
  849. *
  850. */
  851. ActiveIsr = (pIcaAdapter->ica_mode & (ICA_SMM|ICA_SFNM))
  852. ? 0 : pIcaAdapter->ica_isr;
  853. for(i = 0; i < 8; i++) {
  854. line = (pIcaAdapter->ica_hipri + i) & 7;
  855. bit = (UCHAR) (1 << line);
  856. if (ActiveIsr & bit) {
  857. break; /* No nested interrupt possible */
  858. }
  859. if (IrrImrDelay & bit) {
  860. return line;
  861. }
  862. }
  863. }
  864. return -1;
  865. }
  866. int
  867. VdmpIcaAccept(
  868. PVDMICAUSERDATA pIcaUserData,
  869. PVDMVIRTUALICA pIcaAdapter
  870. )
  871. /*++
  872. Routine Description:
  873. Does the equivalent of a cpu IntAck cycle retrieving the Irql Line Num
  874. for interrupt dispatch, and setting the ica state to reflect that
  875. the interrupt is in service.
  876. Similar to softpc\base\system\ica.c - ica_accept() scan_irr(),
  877. except that this code rejects interrupt dispatching if the ica
  878. is in Auto-EOI as this may involve a new interrupt cycle, and
  879. eoi hooks to be activated.
  880. Arguments:
  881. PVDMICAUSERDATA pIcaUserData - addr of ica userdata
  882. PVDMVIRTUALICA pIcaAdapter - addr of ica adapter
  883. Return Value:
  884. ULONG IrqLineNum for the specific adapter (0 to 7)
  885. returns -1 if there are no interrupts to generate (spurious ints
  886. are normally done on line 7
  887. --*/
  888. {
  889. int line;
  890. UCHAR bit;
  891. PAGED_CODE();
  892. //
  893. // Drop the INT line, and scan the ica
  894. //
  895. pIcaAdapter->ica_cpu_int = FALSE;
  896. try {
  897. line = VdmpIcaScan(pIcaUserData, pIcaAdapter);
  898. } except (EXCEPTION_EXECUTE_HANDLER) {
  899. return -1;
  900. }
  901. if (line < 0) {
  902. return -1;
  903. }
  904. bit = (UCHAR)(1 << line);
  905. pIcaAdapter->ica_isr |= bit;
  906. //
  907. // decrement the count and clear the IRR bit
  908. // ensure the count doesn't wrap past zero.
  909. //
  910. if (--(pIcaAdapter->ica_count[line]) <= 0) {
  911. pIcaAdapter->ica_irr &= ~bit;
  912. pIcaAdapter->ica_count[line] = 0;
  913. }
  914. return line;
  915. }
  916. ULONG
  917. GetIretHookAddress(
  918. PKTRAP_FRAME TrapFrame,
  919. PVDMICAUSERDATA pIcaUserData,
  920. int IrqNum
  921. )
  922. /*++
  923. Routine Description:
  924. Retrieves the IretHookAddress from the real mode\protect mode
  925. iret hook bop table. This function is equivalent to
  926. softpc\base\system\ica.c - ica_iret_hook_needed()
  927. Arguments:
  928. TrapFrame - address of current trapframe
  929. pIcaUserData - addr of ica data
  930. IrqNum - IrqLineNum
  931. Return Value:
  932. ULONG IretHookAddress. seg:offset or sel:offset Iret Hook,
  933. 0 if none
  934. --*/
  935. {
  936. ULONG IrqMask;
  937. ULONG AddrBopTable;
  938. int IretBopSize;
  939. PAGED_CODE();
  940. IrqMask = 1 << IrqNum;
  941. if (!(IrqMask & *pIcaUserData->pIretHooked) ||
  942. !*pIcaUserData->pAddrIretBopTable )
  943. {
  944. return 0;
  945. }
  946. if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
  947. AddrBopTable = *pIcaUserData->pAddrIretBopTable;
  948. IretBopSize = VDM_RM_IRETBOPSIZE;
  949. }
  950. else {
  951. AddrBopTable = (VDM_PM_IRETBOPSEG << 16) | VDM_PM_IRETBOPOFF;
  952. IretBopSize = VDM_PM_IRETBOPSIZE;
  953. }
  954. *pIcaUserData->pDelayIret |= IrqMask;
  955. return AddrBopTable + IretBopSize * IrqNum;
  956. }
  957. VOID
  958. PushRmInterrupt(
  959. PKTRAP_FRAME TrapFrame,
  960. ULONG IretHookAddress,
  961. PVDM_TIB VdmTib,
  962. ULONG InterruptNumber
  963. )
  964. /*++
  965. Routine Description:
  966. Pushes RealMode interrupt frame onto the UserMode stack in the TrapFrame
  967. Arguments:
  968. TrapFrame - address of current trapframe
  969. IretHookAddress - address of Iret Hook, 0 if none
  970. VdmTib - address of current vdm tib
  971. InterruptNumber - interrupt number to reflect
  972. Return Value:
  973. None.
  974. --*/
  975. {
  976. ULONG UserSS;
  977. USHORT UserSP;
  978. USHORT NewCS;
  979. USHORT NewIP;
  980. PVDM_INTERRUPTHANDLER IntHandler;
  981. PAGED_CODE();
  982. //
  983. // Get pointers to current stack
  984. //
  985. UserSS = TrapFrame->HardwareSegSs << 4;
  986. UserSP = (USHORT) TrapFrame->HardwareEsp;
  987. //
  988. // load interrupt stack frame, pushing flags, Cs and ip
  989. //
  990. try {
  991. ProbeForReadSmallStructure (UserSS + UserSP - 3 * sizeof (USHORT),
  992. 3 * sizeof (USHORT),
  993. sizeof (UCHAR));
  994. UserSP -= 2;
  995. *(PUSHORT)(UserSS + UserSP) = (USHORT)TrapFrame->EFlags;
  996. UserSP -= 2;
  997. *(PUSHORT)(UserSS + UserSP) = (USHORT)TrapFrame->SegCs;
  998. UserSP -= 2;
  999. *(PUSHORT)(UserSS + UserSP) = (USHORT)TrapFrame->Eip;
  1000. //
  1001. // load IretHook stack frame if one exists
  1002. //
  1003. if (IretHookAddress) {
  1004. ProbeForReadSmallStructure (UserSS + UserSP - 3 * sizeof (USHORT),
  1005. 3 * sizeof (USHORT),
  1006. sizeof (UCHAR));
  1007. UserSP -= 2;
  1008. *(PUSHORT)(UserSS + UserSP) = (USHORT)(TrapFrame->EFlags & ~EFLAGS_TF_MASK);
  1009. UserSP -= 2;
  1010. *(PUSHORT)(UserSS + UserSP) = (USHORT)(IretHookAddress >> 16);
  1011. UserSP -= 2;
  1012. *(PUSHORT)(UserSS + UserSP) = (USHORT)IretHookAddress;
  1013. }
  1014. //
  1015. // Set new sp, ip, and cs.
  1016. //
  1017. IntHandler = &VdmTib->VdmInterruptTable[InterruptNumber];
  1018. ProbeForReadSmallStructure (&IntHandler[InterruptNumber],
  1019. sizeof (VDM_INTERRUPTHANDLER),
  1020. sizeof (UCHAR));
  1021. if (IntHandler->Flags & VDM_INT_HOOKED) {
  1022. NewCS = (USHORT) (VdmTib->DpmiInfo.DosxRmReflector >> 16);
  1023. NewIP = (USHORT) VdmTib->DpmiInfo.DosxRmReflector;
  1024. //
  1025. // now encode the interrupt number into CS
  1026. //
  1027. NewCS = (USHORT) (NewCS - InterruptNumber);
  1028. NewIP = (USHORT) (NewIP + (InterruptNumber*16));
  1029. } else {
  1030. PUSHORT pIvtEntry = (PUSHORT) (InterruptNumber * 4);
  1031. ProbeForReadSmallStructure (pIvtEntry,
  1032. sizeof (USHORT) * 2,
  1033. sizeof (UCHAR));
  1034. NewIP = *pIvtEntry++;
  1035. NewCS = *pIvtEntry;
  1036. }
  1037. } except (EXCEPTION_EXECUTE_HANDLER) {
  1038. return;
  1039. }
  1040. TrapFrame->HardwareEsp = UserSP;
  1041. TrapFrame->Eip = NewIP;
  1042. ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
  1043. if ((TrapFrame->EFlags & EFLAGS_V86_MASK) == 0) {
  1044. NewCS = SANITIZE_SEG (NewCS, UserMode);
  1045. if (NewCS < 8) {
  1046. NewCS = KGDT_R3_CODE | RPL_MASK;
  1047. }
  1048. }
  1049. TrapFrame->SegCs = NewCS;
  1050. }
  1051. NTSTATUS
  1052. PushPmInterrupt(
  1053. PKTRAP_FRAME TrapFrame,
  1054. ULONG IretHookAddress,
  1055. PVDM_TIB VdmTib,
  1056. ULONG InterruptNumber
  1057. )
  1058. /*++
  1059. Routine Description:
  1060. Pushes ProtectMode interrupt frame onto the UserMode stack in the TrapFrame
  1061. Raises an exception if an invalid stack is found
  1062. Arguments:
  1063. TrapFrame - address of current trapframe
  1064. IretHookAddress - address of Iret Hook, 0 if none
  1065. VdmTib - address of current vdm tib
  1066. InterruptNumber - interrupt number to reflect
  1067. Return Value:
  1068. None.
  1069. --*/
  1070. {
  1071. ULONG Flags,Base,Limit;
  1072. ULONG VdmSp, VdmSpOrg;
  1073. PUSHORT VdmStackPointer;
  1074. BOOLEAN Frame32 = (BOOLEAN) VdmTib->DpmiInfo.Flags;
  1075. PVDM_INTERRUPTHANDLER IntHandler;
  1076. USHORT NewCS;
  1077. PAGED_CODE();
  1078. //
  1079. // Switch to "locked" dpmi stack if lock count is zero
  1080. // This emulates the win3.1 Begin_Use_Locked_PM_Stack function.
  1081. //
  1082. try {
  1083. if (!VdmTib->DpmiInfo.LockCount++) {
  1084. VdmTib->DpmiInfo.SaveEsp = TrapFrame->HardwareEsp;
  1085. VdmTib->DpmiInfo.SaveEip = TrapFrame->Eip;
  1086. VdmTib->DpmiInfo.SaveSsSelector = (USHORT) TrapFrame->HardwareSegSs;
  1087. TrapFrame->HardwareEsp = 0x1000;
  1088. TrapFrame->HardwareSegSs = (ULONG) VdmTib->DpmiInfo.SsSelector | 0x7;
  1089. }
  1090. } except (EXCEPTION_EXECUTE_HANDLER) {
  1091. return GetExceptionCode ();
  1092. }
  1093. //
  1094. // Use Sp or Esp ?
  1095. //
  1096. if (!Ki386GetSelectorParameters((USHORT)TrapFrame->HardwareSegSs,
  1097. &Flags, &Base, &Limit)) {
  1098. return STATUS_ACCESS_VIOLATION;
  1099. }
  1100. //
  1101. // Adjust the limit for page granularity
  1102. //
  1103. if (Flags & SEL_TYPE_2GIG) {
  1104. Limit = (Limit << 12) | 0xfff;
  1105. }
  1106. if (Limit != 0xffffffff) Limit++;
  1107. VdmSp = (Flags & SEL_TYPE_BIG) ? TrapFrame->HardwareEsp
  1108. : (USHORT)TrapFrame->HardwareEsp;
  1109. //
  1110. // Get pointer to current stack
  1111. //
  1112. VdmStackPointer = (PUSHORT)(Base + VdmSp);
  1113. //
  1114. // Create enough room for iret hook frame
  1115. //
  1116. VdmSpOrg = VdmSp;
  1117. if (IretHookAddress) {
  1118. if (Frame32) {
  1119. VdmSp -= 3*sizeof(ULONG);
  1120. } else {
  1121. VdmSp -= 3*sizeof(USHORT);
  1122. }
  1123. }
  1124. //
  1125. // Create enough room for 2 iret frames
  1126. //
  1127. if (Frame32) {
  1128. VdmSp -= 6*sizeof(ULONG);
  1129. } else {
  1130. VdmSp -= 6*sizeof(USHORT);
  1131. }
  1132. //
  1133. // Set Final Value of Sp\Esp, do this before checking stack
  1134. // limits so that invalid esp is visible to debuggers
  1135. //
  1136. if (Flags & SEL_TYPE_BIG) {
  1137. TrapFrame->HardwareEsp = VdmSp;
  1138. } else {
  1139. TrapFrame->HardwareEsp = (USHORT)VdmSp;
  1140. }
  1141. //
  1142. // Check stack limits
  1143. // If any of the following conditions are TRUE
  1144. // - New stack pointer wraps (not enuf space)
  1145. // - If normal stack and Sp not below limit
  1146. // - If Expand Down stack and Sp not above limit
  1147. //
  1148. // Then raise a Stack Fault
  1149. //
  1150. if ( VdmSp >= VdmSpOrg ||
  1151. !(Flags & SEL_TYPE_ED) && VdmSpOrg > Limit ||
  1152. (Flags & SEL_TYPE_ED) && VdmSp < Limit ) {
  1153. return STATUS_ACCESS_VIOLATION;
  1154. }
  1155. //
  1156. // Build the Hw Int iret frame
  1157. //
  1158. try {
  1159. if (Frame32) {
  1160. //
  1161. // Probe the stack pointer to make sure its good. We probe for read here
  1162. // as we are faster. The code is going to write the addresses anyway.
  1163. //
  1164. ProbeForReadSmallStructure (VdmStackPointer - 6 * sizeof (ULONG),
  1165. 6 * sizeof (ULONG),
  1166. sizeof (UCHAR));
  1167. VdmStackPointer -= 2;
  1168. *(PULONG)VdmStackPointer = TrapFrame->EFlags;
  1169. VdmStackPointer -= 2;
  1170. *(PUSHORT)VdmStackPointer = (USHORT)TrapFrame->SegCs;
  1171. VdmStackPointer -= 2;
  1172. *(PULONG)VdmStackPointer = TrapFrame->Eip;
  1173. VdmStackPointer -= 2;
  1174. *(PULONG)VdmStackPointer = TrapFrame->EFlags & ~EFLAGS_TF_MASK;
  1175. VdmStackPointer -= 2;
  1176. *(PULONG)VdmStackPointer = VdmTib->DpmiInfo.DosxIntIretD >> 16;
  1177. VdmStackPointer -= 2;
  1178. *(PULONG)VdmStackPointer = VdmTib->DpmiInfo.DosxIntIretD & 0xffff;
  1179. } else {
  1180. ProbeForReadSmallStructure (VdmStackPointer - 6 * sizeof (USHORT),
  1181. 6 * sizeof (USHORT),
  1182. sizeof (UCHAR));
  1183. VdmStackPointer -= 1;
  1184. *VdmStackPointer = (USHORT)TrapFrame->EFlags;
  1185. VdmStackPointer -= 1;
  1186. *VdmStackPointer = (USHORT)TrapFrame->SegCs;
  1187. VdmStackPointer -= 1;
  1188. *VdmStackPointer = (USHORT)TrapFrame->Eip;
  1189. VdmStackPointer -= 1;
  1190. *VdmStackPointer = (USHORT)(TrapFrame->EFlags & ~EFLAGS_TF_MASK);
  1191. VdmStackPointer -= 2;
  1192. *(PULONG)VdmStackPointer = VdmTib->DpmiInfo.DosxIntIret;
  1193. }
  1194. //
  1195. // Point cs and ip at interrupt handler
  1196. //
  1197. IntHandler = &VdmTib->VdmInterruptTable[InterruptNumber];
  1198. ProbeForReadSmallStructure (&IntHandler[InterruptNumber],
  1199. sizeof (VDM_INTERRUPTHANDLER),
  1200. sizeof (UCHAR));
  1201. NewCS = IntHandler->CsSelector | 0x7;
  1202. if ((TrapFrame->EFlags & EFLAGS_V86_MASK) == 0) {
  1203. NewCS = SANITIZE_SEG (NewCS, UserMode);
  1204. if (NewCS < 8) {
  1205. NewCS = KGDT_R3_CODE | RPL_MASK;
  1206. }
  1207. }
  1208. TrapFrame->SegCs = NewCS;
  1209. TrapFrame->Eip = IntHandler->Eip;
  1210. } except (EXCEPTION_EXECUTE_HANDLER) {
  1211. return GetExceptionCode ();
  1212. }
  1213. //
  1214. // Turn off trace bit so we don't trace the iret hook
  1215. //
  1216. ASSERT (KeGetCurrentIrql () >= APC_LEVEL);
  1217. TrapFrame->EFlags &= ~EFLAGS_TF_MASK;
  1218. //
  1219. // Build the Irethook Iret frame, if one exists
  1220. //
  1221. if (IretHookAddress) {
  1222. ULONG SegCs, Eip;
  1223. //
  1224. // Point cs and eip at the iret hook, so when we build
  1225. // the frame below, the correct contents are set
  1226. //
  1227. SegCs = IretHookAddress >> 16;
  1228. Eip = IretHookAddress & 0xFFFF;
  1229. try {
  1230. if (Frame32) {
  1231. ProbeForReadSmallStructure (VdmStackPointer - 3 * sizeof (ULONG),
  1232. 3 * sizeof (ULONG),
  1233. sizeof (UCHAR));
  1234. VdmStackPointer -= 2;
  1235. *(PULONG)VdmStackPointer = TrapFrame->EFlags;
  1236. VdmStackPointer -= 2;
  1237. *VdmStackPointer = (USHORT)SegCs;
  1238. VdmStackPointer -= 2;
  1239. *(PULONG)VdmStackPointer = Eip;
  1240. } else {
  1241. ProbeForReadSmallStructure (VdmStackPointer - 3 * sizeof (USHORT),
  1242. 3 * sizeof (USHORT),
  1243. sizeof (UCHAR));
  1244. VdmStackPointer -= 1;
  1245. *VdmStackPointer = (USHORT)TrapFrame->EFlags;
  1246. VdmStackPointer -= 1;
  1247. *VdmStackPointer = (USHORT)SegCs;
  1248. VdmStackPointer -= 1;
  1249. *VdmStackPointer = (USHORT)Eip;
  1250. }
  1251. } except (EXCEPTION_EXECUTE_HANDLER) {
  1252. return GetExceptionCode ();
  1253. }
  1254. }
  1255. return STATUS_SUCCESS;
  1256. }
  1257. NTSTATUS
  1258. VdmpDelayInterrupt (
  1259. PVDMDELAYINTSDATA pdsd
  1260. )
  1261. /*++
  1262. Routine Description:
  1263. Sets a timer to dispatch the delayed interrupt through KeSetTimer.
  1264. When the timer fires a user mode APC is queued to queue the interrupt.
  1265. This function uses lazy allocation routines to allocate internal
  1266. data structures (nonpaged pool) on a per Irq basis, and needs to
  1267. be notified when specific Irq Lines no longer need Delayed
  1268. Interrupt services.
  1269. The caller must own the IcaLock to synchronize access to the
  1270. Irq lists.
  1271. WARNING: - Until the Delayed interrupt fires or is cancelled,
  1272. the specific Irq line will not generate any interrupts.
  1273. - The APC routine, does not take the HostIca lock, when
  1274. unblocking the IrqLine. Devices which use delayed Interrupts
  1275. should not queue ANY additional interrupts for the same IRQ
  1276. line until the delayed interrupt has fired or been cancelled.
  1277. Arguments:
  1278. pdsd.Delay Delay Interval in usecs
  1279. if Delay is 0xFFFFFFFF then per Irq Line nonpaged
  1280. data structures are freed. No Timers are set.
  1281. else the Delay is used as the timer delay.
  1282. pdsd.DelayIrqLine IrqLine Number
  1283. pdsd.hThread Thread Handle of CurrentMonitorTeb
  1284. Return Value:
  1285. NTSTATUS.
  1286. --*/
  1287. {
  1288. VDMDELAYINTSDATA Capturedpdsd;
  1289. PVDM_PROCESS_OBJECTS pVdmObjects;
  1290. PLIST_ENTRY Next;
  1291. PEPROCESS Process;
  1292. PDELAYINTIRQ pDelayIntIrq;
  1293. PDELAYINTIRQ NewIrq;
  1294. PETHREAD Thread, MainThread;
  1295. NTSTATUS Status;
  1296. KIRQL OldIrql;
  1297. ULONG IrqLine;
  1298. ULONG Delay;
  1299. PULONG pDelayIrq;
  1300. PULONG pUndelayIrq;
  1301. LARGE_INTEGER liDelay;
  1302. LOGICAL FreeIrqLine;
  1303. LOGICAL AlreadyInUse;
  1304. //
  1305. // Get a pointer to pVdmObjects
  1306. //
  1307. Process = PsGetCurrentProcess();
  1308. pVdmObjects = Process->VdmObjects;
  1309. if (pVdmObjects == NULL) {
  1310. return STATUS_INVALID_PARAMETER_1;
  1311. }
  1312. Status = STATUS_SUCCESS;
  1313. Thread = MainThread = NULL;
  1314. FreeIrqLine = TRUE;
  1315. AlreadyInUse = FALSE;
  1316. try {
  1317. //
  1318. // Probe the parameters
  1319. //
  1320. ProbeForRead(pdsd, sizeof(VDMDELAYINTSDATA), sizeof(ULONG));
  1321. RtlCopyMemory (&Capturedpdsd, pdsd, sizeof (VDMDELAYINTSDATA));
  1322. } except(EXCEPTION_EXECUTE_HANDLER) {
  1323. return GetExceptionCode();
  1324. }
  1325. //
  1326. // Form a BitMask for the IrqLine Number
  1327. //
  1328. IrqLine = 1 << Capturedpdsd.DelayIrqLine;
  1329. if (!IrqLine) {
  1330. return STATUS_INVALID_PARAMETER_2;
  1331. }
  1332. ExAcquireFastMutex(&pVdmObjects->DelayIntFastMutex);
  1333. pDelayIrq = pVdmObjects->pIcaUserData->pDelayIrq;
  1334. pUndelayIrq = pVdmObjects->pIcaUserData->pUndelayIrq;
  1335. try {
  1336. ProbeForWriteUlong(pDelayIrq);
  1337. ProbeForWriteUlong(pUndelayIrq);
  1338. } except(EXCEPTION_EXECUTE_HANDLER) {
  1339. ExReleaseFastMutex(&pVdmObjects->DelayIntFastMutex);
  1340. return GetExceptionCode();
  1341. }
  1342. pDelayIntIrq = NULL; // satisfy no_opt compilation
  1343. //
  1344. // Convert the Delay parameter into hundredths of nanosecs
  1345. //
  1346. Delay = Capturedpdsd.Delay;
  1347. //
  1348. // Check to see if we need to reset the timer resolution
  1349. //
  1350. if (Delay == 0xFFFFFFFF) {
  1351. ZwSetTimerResolution(KeMaximumIncrement, FALSE, &Delay);
  1352. NewIrq = NULL;
  1353. goto FindIrq;
  1354. }
  1355. FreeIrqLine = FALSE;
  1356. //
  1357. // Convert delay to hundreths of nanosecs
  1358. // and ensure min delay of 1 msec
  1359. //
  1360. Delay = Delay < 1000 ? 10000 : Delay * 10;
  1361. //
  1362. // If the delay time is close to the system's clock rate
  1363. // then adjust the system's clock rate and if needed
  1364. // the delay time so that the timer will fire before the
  1365. // the due time.
  1366. //
  1367. if (Delay < 150000) {
  1368. ULONG ul = Delay >> 1;
  1369. if (ul < KeTimeIncrement && KeTimeIncrement > KeMinimumIncrement) {
  1370. ZwSetTimerResolution(ul, TRUE, (PULONG)&liDelay.LowPart);
  1371. }
  1372. if (Delay < KeTimeIncrement) {
  1373. // can't set system clock rate low enuf, so use half delay
  1374. Delay >>= 1;
  1375. }
  1376. else if (Delay < (KeTimeIncrement << 1)) {
  1377. // Real close to the system clock rate, lower delay
  1378. // proportionally, to avoid missing clock cycles.
  1379. Delay -= KeTimeIncrement >> 1;
  1380. }
  1381. }
  1382. //
  1383. // Reference the Target Thread
  1384. //
  1385. Status = ObReferenceObjectByHandle (Capturedpdsd.hThread,
  1386. THREAD_QUERY_INFORMATION,
  1387. PsThreadType,
  1388. KeGetPreviousMode(),
  1389. &Thread,
  1390. NULL);
  1391. if (!NT_SUCCESS(Status)) {
  1392. ExReleaseFastMutex(&pVdmObjects->DelayIntFastMutex);
  1393. return Status;
  1394. }
  1395. MainThread = pVdmObjects->MainThread;
  1396. ObReferenceObject (MainThread);
  1397. NewIrq = NULL;
  1398. FindIrq:
  1399. ExAcquireSpinLock(&pVdmObjects->DelayIntSpinLock, &OldIrql);
  1400. //
  1401. // Search the DelayedIntList for a matching Irq Line.
  1402. //
  1403. Next = pVdmObjects->DelayIntListHead.Flink;
  1404. while (Next != &pVdmObjects->DelayIntListHead) {
  1405. pDelayIntIrq = CONTAINING_RECORD(Next, DELAYINTIRQ, DelayIntListEntry);
  1406. if (pDelayIntIrq->IrqLine == IrqLine) {
  1407. break;
  1408. }
  1409. Next = Next->Flink;
  1410. }
  1411. if (Next == &pVdmObjects->DelayIntListHead) {
  1412. pDelayIntIrq = NULL;
  1413. if (FreeIrqLine) {
  1414. goto VidExit;
  1415. }
  1416. if (NewIrq == NULL) {
  1417. ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
  1418. //
  1419. // If a DelayIntIrq does not exist for this irql, allocate one
  1420. // from nonpaged pool and initialize it
  1421. //
  1422. NewIrq = ExAllocatePoolWithTag (NonPagedPool,
  1423. sizeof(DELAYINTIRQ),
  1424. ' MDV');
  1425. if (!NewIrq) {
  1426. Status = STATUS_NO_MEMORY;
  1427. AlreadyInUse = TRUE;
  1428. goto VidExit2;
  1429. }
  1430. try {
  1431. PsChargePoolQuota(Process, NonPagedPool, sizeof(DELAYINTIRQ));
  1432. }
  1433. except(EXCEPTION_EXECUTE_HANDLER) {
  1434. Status = GetExceptionCode();
  1435. ExFreePool(NewIrq);
  1436. AlreadyInUse = TRUE;
  1437. goto VidExit2;
  1438. }
  1439. RtlZeroMemory(NewIrq, sizeof(DELAYINTIRQ));
  1440. NewIrq->IrqLine = IrqLine;
  1441. KeInitializeTimer(&NewIrq->Timer);
  1442. KeInitializeDpc(&NewIrq->Dpc,
  1443. VdmpDelayIntDpcRoutine,
  1444. Process);
  1445. goto FindIrq;
  1446. }
  1447. InsertTailList (&pVdmObjects->DelayIntListHead,
  1448. &NewIrq->DelayIntListEntry);
  1449. pDelayIntIrq = NewIrq;
  1450. }
  1451. else if (NewIrq != NULL) {
  1452. ExFreePool (NewIrq);
  1453. PsReturnPoolQuota (Process, NonPagedPool, sizeof(DELAYINTIRQ));
  1454. }
  1455. if (Delay == 0xFFFFFFFF) {
  1456. if (pDelayIntIrq->InUse == VDMDELAY_KTIMER) {
  1457. pDelayIntIrq->InUse = VDMDELAY_NOTINUSE;
  1458. pDelayIntIrq = NULL;
  1459. }
  1460. }
  1461. else if (pDelayIntIrq->InUse == VDMDELAY_NOTINUSE) {
  1462. liDelay = RtlEnlargedIntegerMultiply(Delay, -1);
  1463. if (KeSetTimerEx (&pDelayIntIrq->Timer, liDelay, 0, &pDelayIntIrq->Dpc) == FALSE) {
  1464. ObReferenceObject(Process);
  1465. }
  1466. }
  1467. VidExit:
  1468. if (pDelayIntIrq && !pDelayIntIrq->InUse) {
  1469. if (NT_SUCCESS(Status)) {
  1470. //
  1471. // Save PETHREAD of Target thread for the dpc routine
  1472. // the DPC routine will deref the threads.
  1473. //
  1474. pDelayIntIrq->InUse = VDMDELAY_KTIMER;
  1475. pDelayIntIrq->Thread = Thread;
  1476. Thread = NULL;
  1477. pDelayIntIrq->MainThread = MainThread;
  1478. MainThread = NULL;
  1479. }
  1480. else {
  1481. pDelayIntIrq->InUse = VDMDELAY_NOTINUSE;
  1482. pDelayIntIrq->Thread = NULL;
  1483. FreeIrqLine = TRUE;
  1484. }
  1485. }
  1486. else {
  1487. AlreadyInUse = TRUE;
  1488. }
  1489. ExReleaseSpinLock(&pVdmObjects->DelayIntSpinLock, OldIrql);
  1490. VidExit2:
  1491. try {
  1492. if (FreeIrqLine) {
  1493. *pDelayIrq &= ~IrqLine;
  1494. InterlockedOr ((PLONG)pUndelayIrq, IrqLine);
  1495. }
  1496. else if (!AlreadyInUse) { // TakeIrqLine
  1497. *pDelayIrq |= IrqLine;
  1498. InterlockedAnd ((PLONG)pUndelayIrq, ~IrqLine);
  1499. }
  1500. }
  1501. except(EXCEPTION_EXECUTE_HANDLER) {
  1502. Status = GetExceptionCode();
  1503. }
  1504. ExReleaseFastMutex(&pVdmObjects->DelayIntFastMutex);
  1505. if (Thread) {
  1506. ObDereferenceObject(Thread);
  1507. }
  1508. if (MainThread) {
  1509. ObDereferenceObject(MainThread);
  1510. }
  1511. return Status;
  1512. }
  1513. VOID
  1514. VdmpDelayIntDpcRoutine (
  1515. IN PKDPC Dpc,
  1516. IN PVOID DeferredContext,
  1517. IN PVOID SystemArgument1,
  1518. IN PVOID SystemArgument2
  1519. )
  1520. /*++
  1521. Routine Description:
  1522. This function is the DPC routine that is called when a DelayedInterrupt
  1523. timer expires. Its function is to insert the associated APC into the
  1524. target thread's APC queue.
  1525. Arguments:
  1526. Dpc - Supplies a pointer to a control object of type DPC.
  1527. DeferredContext - Supplies a pointer to the Target EProcess
  1528. SystemArgument1, SystemArgument2 - Supplies a set of two pointers to
  1529. two arguments that contain untyped data that are
  1530. NOT USED.
  1531. Return Value:
  1532. None.
  1533. --*/
  1534. {
  1535. LOGICAL FreeEntireVdm;
  1536. PVDM_PROCESS_OBJECTS pVdmObjects;
  1537. PEPROCESS Process;
  1538. PETHREAD Thread, MainThread;
  1539. PLIST_ENTRY Next;
  1540. PDELAYINTIRQ pDelayIntIrq;
  1541. UNREFERENCED_PARAMETER (SystemArgument1);
  1542. UNREFERENCED_PARAMETER (SystemArgument2);
  1543. FreeEntireVdm = FALSE;
  1544. //
  1545. // Get address of Process VdmObjects
  1546. //
  1547. Process = (PEPROCESS)DeferredContext;
  1548. pVdmObjects = (PVDM_PROCESS_OBJECTS)Process->VdmObjects;
  1549. ASSERT (KeGetCurrentIrql () == DISPATCH_LEVEL);
  1550. ExAcquireSpinLockAtDpcLevel(&pVdmObjects->DelayIntSpinLock);
  1551. //
  1552. // Search the DelayedIntList for the matching Dpc.
  1553. //
  1554. Next = pVdmObjects->DelayIntListHead.Flink;
  1555. while (Next != &pVdmObjects->DelayIntListHead) {
  1556. pDelayIntIrq = CONTAINING_RECORD(Next,DELAYINTIRQ,DelayIntListEntry);
  1557. if (&pDelayIntIrq->Dpc == Dpc) {
  1558. Thread = pDelayIntIrq->Thread;
  1559. pDelayIntIrq->Thread = NULL;
  1560. MainThread = pDelayIntIrq->MainThread;
  1561. pDelayIntIrq->MainThread = NULL;
  1562. if (pDelayIntIrq->InUse) {
  1563. if ((Thread && KeVdmInsertQueueApc(&pDelayIntIrq->Apc,
  1564. &Thread->Tcb,
  1565. KernelMode,
  1566. VdmpDelayIntApcRoutine,
  1567. VdmpRundownRoutine,
  1568. VdmpQueueIntNormalRoutine, // normal routine
  1569. NULL, // NormalContext
  1570. VDM_HWINT_INCREMENT
  1571. ))
  1572. ||
  1573. (MainThread && KeVdmInsertQueueApc(&pDelayIntIrq->Apc,
  1574. &MainThread->Tcb,
  1575. KernelMode,
  1576. VdmpDelayIntApcRoutine,
  1577. VdmpRundownRoutine,
  1578. VdmpQueueIntNormalRoutine, // normal routine
  1579. NULL, // NormalContext
  1580. VDM_HWINT_INCREMENT
  1581. )))
  1582. {
  1583. pDelayIntIrq->InUse = VDMDELAY_KAPC;
  1584. }
  1585. else {
  1586. // This hwinterrupt line is blocked forever.
  1587. pDelayIntIrq->InUse = VDMDELAY_NOTINUSE;
  1588. }
  1589. }
  1590. ExReleaseSpinLockFromDpcLevel(&pVdmObjects->DelayIntSpinLock);
  1591. if (Thread) {
  1592. ObDereferenceObject (Thread);
  1593. }
  1594. if (MainThread) {
  1595. ObDereferenceObject (MainThread);
  1596. }
  1597. ObDereferenceObject (Process);
  1598. return;
  1599. }
  1600. Next = Next->Flink;
  1601. }
  1602. ExReleaseSpinLockFromDpcLevel(&pVdmObjects->DelayIntSpinLock);
  1603. return;
  1604. }
  1605. VOID
  1606. VdmpDelayIntApcRoutine (
  1607. IN PKAPC Apc,
  1608. IN PKNORMAL_ROUTINE *NormalRoutine,
  1609. IN PVOID *NormalContext,
  1610. IN PVOID *SystemArgument1,
  1611. IN PVOID *SystemArgument2
  1612. )
  1613. /*++
  1614. Routine Description:
  1615. This function is the special APC routine that is called to
  1616. dispatch a delayed interrupt. This routine clears the IrqLine
  1617. bit, VdmpQueueIntApcRoutine will restart interrupts.
  1618. Arguments:
  1619. Apc - Supplies a pointer to the APC object used to invoke this routine.
  1620. NormalRoutine - Supplies a pointer to a pointer to an optional
  1621. normal routine, which is executed when wow is blocked.
  1622. NormalContext - Supplies a pointer to a pointer to an arbitrary data
  1623. structure that was specified when the APC was initialized and is
  1624. NOT USED.
  1625. SystemArgument1, SystemArgument2 - Supplies a set of two pointers to
  1626. two arguments that contain untyped data that are
  1627. NOT USED.
  1628. Return Value:
  1629. None.
  1630. --*/
  1631. {
  1632. KIRQL OldIrql;
  1633. PLIST_ENTRY Next;
  1634. PDELAYINTIRQ pDelayIntIrq;
  1635. KPROCESSOR_MODE ProcessorMode;
  1636. PULONG pDelayIrq;
  1637. PULONG pUndelayIrq;
  1638. PULONG pDelayIret;
  1639. ULONG IrqLine;
  1640. LOGICAL FreeIrqLine;
  1641. LOGICAL QueueApc;
  1642. PVDM_PROCESS_OBJECTS pVdmObjects;
  1643. UNREFERENCED_PARAMETER (NormalContext);
  1644. FreeIrqLine = FALSE;
  1645. IrqLine = 0; // satisfy no_opt compilation
  1646. //
  1647. // Clear address of thread object in APC object.
  1648. //
  1649. // N.B. The delay interrupt lock is used to synchronize access to APC
  1650. // objects that are manipulated by VDM.
  1651. //
  1652. pVdmObjects = PsGetCurrentProcess ()->VdmObjects;
  1653. ExAcquireFastMutex (&pVdmObjects->DelayIntFastMutex);
  1654. ExAcquireSpinLock (&pVdmObjects->DelayIntSpinLock, &OldIrql);
  1655. KeVdmClearApcThreadAddress (Apc);
  1656. //
  1657. // Search the DelayedIntList for the pDelayIntIrq.
  1658. //
  1659. Next = pVdmObjects->DelayIntListHead.Flink;
  1660. while (Next != &pVdmObjects->DelayIntListHead) {
  1661. pDelayIntIrq = CONTAINING_RECORD(Next,DELAYINTIRQ,DelayIntListEntry);
  1662. if (&pDelayIntIrq->Apc == Apc) {
  1663. //
  1664. // Found the IrqLine in the DelayedIntList, restart interrupts.
  1665. //
  1666. if (pDelayIntIrq->InUse) {
  1667. pDelayIntIrq->InUse = VDMDELAY_NOTINUSE;
  1668. IrqLine = pDelayIntIrq->IrqLine;
  1669. FreeIrqLine = TRUE;
  1670. }
  1671. break;
  1672. }
  1673. Next = Next->Flink;
  1674. }
  1675. ExReleaseSpinLock (&pVdmObjects->DelayIntSpinLock, OldIrql);
  1676. if (FreeIrqLine == FALSE) {
  1677. ExReleaseFastMutex (&pVdmObjects->DelayIntFastMutex);
  1678. return;
  1679. }
  1680. pDelayIrq = pVdmObjects->pIcaUserData->pDelayIrq;
  1681. pUndelayIrq = pVdmObjects->pIcaUserData->pUndelayIrq;
  1682. pDelayIret = pVdmObjects->pIcaUserData->pDelayIret;
  1683. QueueApc = FALSE;
  1684. try {
  1685. //
  1686. // These variables are being modified without holding the
  1687. // ICA lock. This should be OK because none of the ntvdm
  1688. // devices (timer, mouse etc. should ever do a delayed int
  1689. // while a previous delayed interrupt is still pending.
  1690. //
  1691. *pDelayIrq &= ~IrqLine;
  1692. InterlockedOr ((PLONG)pUndelayIrq, IrqLine);
  1693. //
  1694. // If we are waiting for an iret hook we have nothing left to do
  1695. // since the iret hook will restart interrupts.
  1696. //
  1697. if (!(IrqLine & *pDelayIret)) {
  1698. //
  1699. // set hardware int pending
  1700. //
  1701. InterlockedOr (FIXED_NTVDMSTATE_LINEAR_PC_AT, VDM_INT_HARDWARE);
  1702. //
  1703. // Queue a usermode APC to dispatch interrupts, note
  1704. // try protection is not needed.
  1705. //
  1706. if (NormalRoutine) {
  1707. QueueApc = TRUE;
  1708. }
  1709. }
  1710. }
  1711. except(VdmpExceptionHandler(GetExceptionInformation())) {
  1712. NOTHING;
  1713. }
  1714. if (QueueApc == TRUE) {
  1715. ProcessorMode = KernelMode;
  1716. VdmpQueueIntApcRoutine(Apc,
  1717. NormalRoutine,
  1718. (PVOID *)&ProcessorMode,
  1719. SystemArgument1,
  1720. SystemArgument2);
  1721. }
  1722. ExReleaseFastMutex(&pVdmObjects->DelayIntFastMutex);
  1723. return;
  1724. }
  1725. BOOLEAN
  1726. VdmpDispatchableIntPending(
  1727. ULONG EFlags
  1728. )
  1729. /*++
  1730. Routine Description:
  1731. This routine determines whether or not there is a dispatchable
  1732. virtual interrupt to dispatch.
  1733. Arguments:
  1734. EFlags -- supplies a pointer to the EFlags to be checked
  1735. Return Value:
  1736. True -- a virtual interrupt should be dispatched
  1737. False -- no virtual interrupt should be dispatched
  1738. --*/
  1739. {
  1740. PAGED_CODE();
  1741. //
  1742. // The accesses to FIXED_NTVDMSTATE_LINEAR_PC_AT may be invalid so
  1743. // wrap this in an exception handler.
  1744. //
  1745. try {
  1746. if (EFlags & EFLAGS_V86_MASK) {
  1747. if (KeI386VirtualIntExtensions & V86_VIRTUAL_INT_EXTENSIONS) {
  1748. if(0 != (EFlags & EFLAGS_VIF)) {
  1749. return TRUE;
  1750. }
  1751. } else if (0 != (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_VIRTUAL_INTERRUPTS)) {
  1752. return TRUE;
  1753. }
  1754. } else {
  1755. if ((*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_VIRTUAL_INTERRUPTS) == 0) {
  1756. VdmCheckPMCliTimeStamp();
  1757. }
  1758. //
  1759. // Check again. The call to VdmCheckPMCliTimeStamp may enable it.
  1760. //
  1761. if (0 != (*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_VIRTUAL_INTERRUPTS)) {
  1762. return TRUE;
  1763. }
  1764. }
  1765. }
  1766. except (EXCEPTION_EXECUTE_HANDLER) {
  1767. NOTHING;
  1768. }
  1769. return FALSE;
  1770. }
  1771. NTSTATUS
  1772. VdmpIsThreadTerminating(
  1773. HANDLE ThreadId
  1774. )
  1775. /*++
  1776. Routine Description:
  1777. This routine determines if the specified thread is terminating or not.
  1778. Arguments:
  1779. Return Value:
  1780. True --
  1781. False -
  1782. --*/
  1783. {
  1784. CLIENT_ID Cid;
  1785. PETHREAD Thread;
  1786. NTSTATUS Status;
  1787. PAGED_CODE();
  1788. //
  1789. // If the owning thread juest exited the IcaLock the
  1790. // OwningThread Tid may be NULL, return success, since
  1791. // we don't know what the owning thread's state was.
  1792. //
  1793. if (!ThreadId) {
  1794. return STATUS_SUCCESS;
  1795. }
  1796. Cid.UniqueProcess = NtCurrentTeb()->ClientId.UniqueProcess;
  1797. Cid.UniqueThread = ThreadId;
  1798. Status = PsLookupProcessThreadByCid (&Cid, NULL, &Thread);
  1799. if (NT_SUCCESS(Status)) {
  1800. Status = PsIsThreadTerminating(Thread) ? STATUS_THREAD_IS_TERMINATING
  1801. : STATUS_SUCCESS;
  1802. ObDereferenceObject(Thread);
  1803. }
  1804. return Status;
  1805. }
  1806. VOID
  1807. VdmpRundownRoutine (
  1808. IN PKAPC Apc
  1809. )
  1810. /*++
  1811. Routine Description:
  1812. The function is the rundown routine for VDM APCs and is called on thread
  1813. termination. The fact that this function is called means that none of the
  1814. APC objects specified by the process VDM structure will not get freed.
  1815. They must be freed when the process terminates.
  1816. Arguments:
  1817. Apc - Supplies a pointer to an APC object to be rundown.
  1818. Return Value:
  1819. None.
  1820. --*/
  1821. {
  1822. //
  1823. // Clear the Irqline, but don't requeue the APC.
  1824. //
  1825. VdmpDelayIntApcRoutine(Apc, NULL, NULL, NULL, NULL);
  1826. return;
  1827. }
  1828. int
  1829. VdmpExceptionHandler (
  1830. IN PEXCEPTION_POINTERS ExceptionInfo
  1831. )
  1832. {
  1833. #if DBG
  1834. PEXCEPTION_RECORD ExceptionRecord;
  1835. ULONG NumberParameters;
  1836. PULONG ExceptionInformation;
  1837. #endif
  1838. PAGED_CODE();
  1839. #if DBG
  1840. ExceptionRecord = ExceptionInfo->ExceptionRecord;
  1841. DbgPrint("VdmExRecord ExCode %x Flags %x Address %x\n",
  1842. ExceptionRecord->ExceptionCode,
  1843. ExceptionRecord->ExceptionFlags,
  1844. ExceptionRecord->ExceptionAddress
  1845. );
  1846. NumberParameters = ExceptionRecord->NumberParameters;
  1847. if (NumberParameters) {
  1848. DbgPrint("VdmExRecord Parameters:\n");
  1849. ExceptionInformation = ExceptionRecord->ExceptionInformation;
  1850. while (NumberParameters--) {
  1851. DbgPrint("\t%x\n", *ExceptionInformation);
  1852. }
  1853. }
  1854. #else
  1855. UNREFERENCED_PARAMETER (ExceptionInfo);
  1856. #endif
  1857. return EXCEPTION_EXECUTE_HANDLER;
  1858. }