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.

750 lines
17 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. vrinit.c
  5. Abstract:
  6. Contains Vdm Redir (Vr) 32-bit side initialization and uninitialization
  7. routines
  8. Contents:
  9. VrInitialized
  10. VrInitialize
  11. VrUninitialize
  12. VrRaiseInterrupt
  13. VrDismissInterrupt
  14. VrQueueCompletionHandler
  15. VrHandleAsyncCompletion
  16. VrCheckPmNetbiosAnr
  17. VrEoiAndDismissInterrupt
  18. VrSuspendHook
  19. VrResumeHook
  20. Author:
  21. Richard L Firth (rfirth) 13-Sep-1991
  22. Environment:
  23. 32-bit flat address space
  24. Revision History:
  25. 13-Sep-1991 RFirth
  26. Created
  27. --*/
  28. #include <nt.h>
  29. #include <ntrtl.h> // ASSERT, DbgPrint
  30. #include <nturtl.h>
  31. #include <windows.h>
  32. #include <softpc.h> // x86 virtual machine definitions
  33. #include <vrdlctab.h>
  34. #include <vdmredir.h> // common Vdm Redir stuff
  35. #include <vrinit.h>
  36. #include <nb30.h>
  37. #include <netb.h>
  38. #include <dlcapi.h>
  39. #include <vrdefld.h>
  40. #include "vrdlc.h"
  41. #include "vrdebug.h"
  42. #define BOOL // kludge for mips build
  43. #include <insignia.h> // Required for ica.h
  44. #include <xt.h> // Required for ica.h
  45. #include <ica.h>
  46. #include <vrica.h> // call_ica_hw_interrupt
  47. //
  48. // external functions
  49. //
  50. extern BOOL VDDInstallUserHook(HANDLE, FARPROC, FARPROC, FARPROC, FARPROC);
  51. //
  52. // prototypes
  53. //
  54. VOID
  55. VrSuspendHook(
  56. VOID
  57. );
  58. VOID
  59. VrResumeHook(
  60. VOID
  61. );
  62. //
  63. // data
  64. //
  65. static BOOLEAN IsVrInitialized = FALSE; // set when TSR loaded
  66. extern DWORD VrPeekNamedPipeTickCount;
  67. extern CRITICAL_SECTION VrNmpRequestQueueCritSec;
  68. extern CRITICAL_SECTION VrNamedPipeCancelCritSec;
  69. //
  70. // Async Event Disposition. The following critical sections, queue and counter
  71. // plus the routines VrRaiseInterrupt, VrDismissInterrupt,
  72. // VrQueueCompletionHandler and VrHandleAsyncCompletion comprise the async
  73. // event disposition processing.
  74. //
  75. // We employ these to dispose of the asynchronous event completions in the order
  76. // they occur. Also we keep calls to call_ica_hw_interrupt serialized: the reason
  77. // for this is that the ICA is not guaranteed to generate an interrupt. Rather
  78. // than blast the ICA with interrupt requests, we generate one only when we
  79. // know we have completed the previous disposition
  80. //
  81. CRITICAL_SECTION AsyncEventQueueCritSec;
  82. VR_ASYNC_DISPOSITION* AsyncEventQueueHead = NULL;
  83. VR_ASYNC_DISPOSITION* AsyncEventQueueTail = NULL;
  84. CRITICAL_SECTION QueuedInterruptCritSec;
  85. LONG QueuedInterrupts = -1;
  86. LONG FrozenInterrupts = 0;
  87. //
  88. // FrozenVdmContext - TRUE if the 16-bit context has been suspended. When this
  89. // happens, we need to queue any hardware interrupt requests until the 16-bit
  90. // context has been resumed
  91. //
  92. BOOLEAN FrozenVdmContext = FALSE;
  93. //
  94. // routines
  95. //
  96. BOOLEAN
  97. VrInitialized(
  98. VOID
  99. )
  100. /*++
  101. Routine Description:
  102. Returns whether the VdmRedir support has been initialized yet (ie redir.exe
  103. TSR loaded in DOS emulation memory). Principally here because VdmRedir is
  104. now a DLL loaded at run-time via LoadLibrary
  105. Arguments:
  106. None.
  107. Return Value:
  108. BOOLEAN
  109. TRUE VdmRedir support is active
  110. FALSE VdmRedir support inactive
  111. --*/
  112. {
  113. return IsVrInitialized;
  114. }
  115. BOOLEAN
  116. VrInitialize(
  117. VOID
  118. )
  119. /*++
  120. Routine Description:
  121. Performs 32-bit side initialization when the redir TSR is loaded
  122. Arguments:
  123. None. ES:BX in VDM context is place to return computer name, CX is size
  124. of buffer in VDM context for computer name
  125. Return Value:
  126. None.
  127. --*/
  128. {
  129. LPBYTE lpVdmVrInitialized;
  130. #if DBG
  131. DIAGNOSTIC_INFO info;
  132. VrDebugInit();
  133. DIAGNOSTIC_ENTRY("VrInitialize", DG_NONE, &info);
  134. #endif
  135. //
  136. // if we are already initialized return TRUE. Not sure if this should
  137. // really happen?
  138. //
  139. if (IsVrInitialized) {
  140. return TRUE;
  141. }
  142. //
  143. // register our hooks
  144. //
  145. if (!VDDInstallUserHook(GetModuleHandle("VDMREDIR"),
  146. (FARPROC)NULL, // 16-bit process create hook
  147. (FARPROC)NULL, // 16-bit process terminate hook
  148. (FARPROC)VrSuspendHook,
  149. (FARPROC)VrResumeHook
  150. )) {
  151. return FALSE;
  152. }
  153. //
  154. // do the rest of the initialization - none of this can fail
  155. //
  156. InitializeCriticalSection(&VrNmpRequestQueueCritSec);
  157. InitializeCriticalSection(&AsyncEventQueueCritSec);
  158. InitializeCriticalSection(&QueuedInterruptCritSec);
  159. InitializeCriticalSection(&VrNamedPipeCancelCritSec);
  160. VrNetbios5cInitialize();
  161. VrDlcInitialize();
  162. IsVrInitialized = TRUE;
  163. //
  164. // deferred loading: we need to let the VDM redir know that the 32-bit
  165. // support is loaded. Set the VrInitialized flag in the VDM Redir at
  166. // the known address
  167. //
  168. lpVdmVrInitialized = LPBYTE_FROM_WORDS(getCS(), (DWORD)(&(((VDM_LOAD_INFO*)0)->VrInitialized)));
  169. *lpVdmVrInitialized = 1;
  170. //
  171. // VrPeekNamedPipe idle processing
  172. //
  173. VrPeekNamedPipeTickCount = GetTickCount();
  174. setCF(0); // no carry == successful initialization
  175. //
  176. // inform 32-bit caller of successful initialization
  177. //
  178. return TRUE;
  179. }
  180. VOID
  181. VrUninitialize(
  182. VOID
  183. )
  184. /*++
  185. Routine Description:
  186. Performs 32-bit side uninitialization when the redir TSR is removed
  187. Arguments:
  188. None.
  189. Return Value:
  190. None.
  191. --*/
  192. {
  193. IF_DEBUG(DLL) {
  194. DPUT("VrUninitialize\n");
  195. }
  196. if (IsVrInitialized) {
  197. DeleteCriticalSection(&VrNmpRequestQueueCritSec);
  198. DeleteCriticalSection(&AsyncEventQueueCritSec);
  199. DeleteCriticalSection(&QueuedInterruptCritSec);
  200. DeleteCriticalSection(&VrNamedPipeCancelCritSec);
  201. }
  202. IsVrInitialized = FALSE;
  203. setCF(0); // no carry == successful uninitialization
  204. }
  205. VOID
  206. VrRaiseInterrupt(
  207. VOID
  208. )
  209. /*++
  210. Routine Description:
  211. Generates a simulated hardware interrupt by calling the ICA routine. Access
  212. to the ICA is serialized here: we maintain a count. If the count goes from
  213. -1 to 0, we call the ICA function to generate the interrupt in the VDM. Any
  214. other value just queues the interrupt by incrementing the counter. When the
  215. corresponding VrDismissInterrupt call is made, a queued interrupt will be
  216. generated. This stops us losing simulated h/w interrupts to the VDM
  217. Arguments:
  218. None.
  219. Return Value:
  220. None.
  221. --*/
  222. {
  223. EnterCriticalSection(&QueuedInterruptCritSec);
  224. ++QueuedInterrupts;
  225. if (QueuedInterrupts == 0) {
  226. if (!FrozenVdmContext) {
  227. IF_DEBUG(CRITICAL) {
  228. CRITDUMP(("*** VrRaiseInterrupt: Interrupting VDM ***\n"));
  229. }
  230. IF_DEBUG(HW_INTERRUPTS) {
  231. DBGPRINT("VrRaiseInterrupt: interrupting VDM\n");
  232. }
  233. call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
  234. } else {
  235. IF_DEBUG(HW_INTERRUPTS) {
  236. DBGPRINT("*** VrRaiseInterrupt: VDM is Frozen, not interrupting ***\n");
  237. }
  238. }
  239. }
  240. IF_DEBUG(CRITICAL) {
  241. CRITDUMP(("*** VrRaiseInterrupt (%d) ***\n", QueuedInterrupts));
  242. }
  243. IF_DEBUG(HW_INTERRUPTS) {
  244. DBGPRINT("*** VrRaiseInterrupt (%d) ***\n", QueuedInterrupts);
  245. }
  246. LeaveCriticalSection(&QueuedInterruptCritSec);
  247. }
  248. VOID
  249. VrDismissInterrupt(
  250. VOID
  251. )
  252. /*++
  253. Routine Description:
  254. Companion routine to VrRaiseInterrupt: this function is called when the
  255. async event which called VrRaiseInterrupt has been disposed. If other calls
  256. to VrRaiseInterrupt have been made in the interim then QueuedInterrupts will
  257. be >0. In this case we re-issue the call to call_ica_hw_interrupt() which
  258. will generate an new simulated h/w interrupt in the VDM.
  259. Note: This routine is called from the individual disposition routine, not
  260. from the disposition dispatch routine (VrHandleAsyncCompletion)
  261. Arguments:
  262. None.
  263. Return Value:
  264. None.
  265. --*/
  266. {
  267. EnterCriticalSection(&QueuedInterruptCritSec);
  268. if (!FrozenVdmContext) {
  269. --QueuedInterrupts;
  270. if (QueuedInterrupts >= 0) {
  271. IF_DEBUG(CRITICAL) {
  272. CRITDUMP(("*** VrDismissInterrupt: interrupting VDM ***\n"));
  273. }
  274. IF_DEBUG(HW_INTERRUPTS) {
  275. DBGPRINT("VrDismissInterrupt: interrupting VDM\n");
  276. }
  277. call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
  278. }
  279. } else {
  280. IF_DEBUG(HW_INTERRUPTS) {
  281. DBGPRINT("*** VrDismissInterrupt: VDM is Frozen??? ***\n");
  282. }
  283. }
  284. IF_DEBUG(CRITICAL) {
  285. CRITDUMP(("*** VrDismissInterrupt (%d) ***\n", QueuedInterrupts));
  286. }
  287. IF_DEBUG(HW_INTERRUPTS) {
  288. DBGPRINT("*** VrDismissInterrupt (%d) ***\n", QueuedInterrupts);
  289. }
  290. LeaveCriticalSection(&QueuedInterruptCritSec);
  291. }
  292. VOID
  293. VrQueueCompletionHandler(
  294. IN VOID (*AsyncDispositionRoutine)(VOID)
  295. )
  296. /*++
  297. Routine Description:
  298. Adds an async event disposition packet to the queue of pending completions
  299. (event waiting to be fully completed by the VDM async event ISR/BOP). We
  300. keep these in a singly-linked queue so that we avoid giving priority to one
  301. completion handler while polling
  302. Arguments:
  303. AsyncDispositionRoutine - address of routine which will dispose of the
  304. async completion event
  305. Return Value:
  306. None.
  307. --*/
  308. {
  309. VR_ASYNC_DISPOSITION* pDisposition;
  310. pDisposition = (VR_ASYNC_DISPOSITION*)LocalAlloc(LMEM_FIXED,
  311. sizeof(VR_ASYNC_DISPOSITION)
  312. );
  313. if (pDisposition == NULL) {
  314. IF_DEBUG(CRITICAL) {
  315. CRITDUMP(("*** VrQueueCompletionHandler: ERROR: Failed to alloc Q packet ***\n"));
  316. }
  317. IF_DEBUG(HW_INTERRUPTS) {
  318. DBGPRINT("!!! VrQueueCompletionHandler: failed to allocate memory\n");
  319. }
  320. return;
  321. }
  322. EnterCriticalSection(&AsyncEventQueueCritSec);
  323. pDisposition->Next = NULL;
  324. pDisposition->AsyncDispositionRoutine = AsyncDispositionRoutine;
  325. if (AsyncEventQueueHead == NULL) {
  326. AsyncEventQueueHead = pDisposition;
  327. } else {
  328. AsyncEventQueueTail->Next = pDisposition;
  329. }
  330. AsyncEventQueueTail = pDisposition;
  331. LeaveCriticalSection(&AsyncEventQueueCritSec);
  332. IF_DEBUG(CRITICAL) {
  333. CRITDUMP(("VrQueueCompletionHandler: Handler %08x queued @ %08x\n",
  334. AsyncDispositionRoutine,
  335. pDisposition
  336. ));
  337. }
  338. IF_DEBUG(HW_INTERRUPTS) {
  339. DBGPRINT("VrQueueCompletionHandler: Handler %08x queued @ %08x\n",
  340. AsyncDispositionRoutine,
  341. pDisposition
  342. );
  343. }
  344. }
  345. VOID
  346. VrHandleAsyncCompletion(
  347. VOID
  348. )
  349. /*++
  350. Routine Description:
  351. Called by VrDispatch for the async completion event BOP. Dequeues the
  352. disposition packet from the head of the queue and calls the disposition
  353. routine
  354. Arguments:
  355. None.
  356. Return Value:
  357. None.
  358. --*/
  359. {
  360. VR_ASYNC_DISPOSITION* pDisposition;
  361. VOID (*AsyncDispositionRoutine)(VOID);
  362. EnterCriticalSection(&AsyncEventQueueCritSec);
  363. pDisposition = AsyncEventQueueHead;
  364. AsyncDispositionRoutine = pDisposition->AsyncDispositionRoutine;
  365. AsyncEventQueueHead = pDisposition->Next;
  366. IF_DEBUG(CRITICAL) {
  367. CRITDUMP(("VrHandleAsyncCompletion: Handler %08x dequeued @ %08x\n",
  368. AsyncDispositionRoutine,
  369. pDisposition
  370. ));
  371. }
  372. IF_DEBUG(HW_INTERRUPTS) {
  373. DBGPRINT("VrHandleAsyncCompletion: freeing @ %08x && calling handler %08x\n",
  374. pDisposition,
  375. AsyncDispositionRoutine
  376. );
  377. }
  378. LocalFree((HLOCAL)pDisposition);
  379. LeaveCriticalSection(&AsyncEventQueueCritSec);
  380. AsyncDispositionRoutine();
  381. }
  382. VOID
  383. VrCheckPmNetbiosAnr(
  384. VOID
  385. )
  386. /*++
  387. Routine Description:
  388. If the disposition routine queued at the head of the disposition list is
  389. VrNetbios5cInterrupt, indicating that the next asynchronous event to be
  390. completed is an async Netbios call, then set the 16-bit Zero Flag to TRUE
  391. if the NCB originated in 16-bit protect mode
  392. Assumes:
  393. 1. This function is called after the corresponding interrupt has been
  394. delivered
  395. 2. There is something on the AsyncEventQueue
  396. Arguments:
  397. None.
  398. Return Value:
  399. None.
  400. ZF in 16-bit context flags word:
  401. TRUE - the next thing to complete is not an NCB, OR, it originated
  402. in 16-bit real-mode
  403. FALSE - the next event to be disposed of IS an async Netbios request,
  404. the NCB for which originated in 16-bit protect mode
  405. --*/
  406. {
  407. BOOLEAN result;
  408. EnterCriticalSection(&AsyncEventQueueCritSec);
  409. if (AsyncEventQueueHead->AsyncDispositionRoutine == VrNetbios5cInterrupt) {
  410. result = IsPmNcbAtQueueHead();
  411. } else {
  412. result = FALSE;
  413. }
  414. IF_DEBUG(HW_INTERRUPTS) {
  415. DBGPRINT("VrCheckPmNetbiosAnr: %s\n", result ? "TRUE" : "FALSE");
  416. }
  417. //
  418. // set ZF: TRUE means event at head of list not PM NCB completion, or no
  419. // NCB completion event on list
  420. //
  421. setZF(!result);
  422. LeaveCriticalSection(&AsyncEventQueueCritSec);
  423. }
  424. VOID
  425. VrEoiAndDismissInterrupt(
  426. VOID
  427. )
  428. /*++
  429. Routine Description:
  430. Performs an EOI then calls VrDismissInterrupt which checks for pending
  431. interrupt requests.
  432. Called when we handle the simulated h/w interrupt entirely in protect mode
  433. (original call was from a WOW app). In this case, the p-m interrupt handler
  434. doesn't perform out a0,20; out 20,20 (non-specific EOI to PIC 0 & PIC 1),
  435. but calls this handler which gets SoftPc to handle the EOIs to the virtual
  436. PICs. This is quicker because we don't take any restricted-opcode faults
  437. (the out instruction in real mode causes a GPF because the code doesn't
  438. have enough privilege to execute I/O instructions. SoftPc takes a look and
  439. sees that the code is trying to talk to the PIC. It then performs the
  440. necessary mangling of the PIC state)
  441. Arguments:
  442. None.
  443. Return Value:
  444. None.
  445. --*/
  446. {
  447. int line;
  448. extern VOID SoftPcEoi(int, int*);
  449. IF_DEBUG(HW_INTERRUPTS) {
  450. DBGPRINT("VrEoiAndDismissInterrupt\n");
  451. }
  452. #ifndef NEC_98
  453. line = -1;
  454. SoftPcEoi(1, &line); // non-specific EOI to slave PIC
  455. #endif
  456. line = -1;
  457. SoftPcEoi(0, &line); // non-specific EOI to master PIC
  458. VrDismissInterrupt();
  459. }
  460. VOID
  461. VrSuspendHook(
  462. VOID
  463. )
  464. /*++
  465. Routine Description:
  466. This is the hook called by NTVDM.EXE for the VDD handle owned by VDMREDIR.DLL.
  467. The hook is called when NTVDM is about to execute a 32-bit process and it
  468. suspends the 16-bit context
  469. Within the queued interrupt critical section we note that the 16-bit context
  470. has been frozen and snapshot the outstanding interrupt request count
  471. N.B. We don't expect that this function will be called again before an
  472. intervening call to VrResumeHook
  473. Arguments:
  474. None.
  475. Return Value:
  476. None.
  477. --*/
  478. {
  479. EnterCriticalSection(&QueuedInterruptCritSec);
  480. FrozenVdmContext = TRUE;
  481. FrozenInterrupts = QueuedInterrupts;
  482. IF_DEBUG(HW_INTERRUPTS) {
  483. DBGPRINT("VrSuspendHook - FrozenInterrupts = %d\n", FrozenInterrupts);
  484. }
  485. LeaveCriticalSection(&QueuedInterruptCritSec);
  486. }
  487. VOID
  488. VrResumeHook(
  489. VOID
  490. )
  491. /*++
  492. Routine Description:
  493. This hook is called when NTVDM resumes the 16-bit context after executing a
  494. 32-bit process
  495. Within the queued interrupt critical section we note that the 16-bit context
  496. has been resumed and we compare the current queued interrupt request count
  497. with the snapshot value we took when the context was suspended. If during the
  498. intervening period, interrupt requests have been made, we call
  499. VrDismissInterrupt to generate the next interrupt
  500. N.B. We don't expect that this function will be called again before an
  501. intervening call to VrSuspendHook
  502. Arguments:
  503. None.
  504. Return Value:
  505. None.
  506. --*/
  507. {
  508. EnterCriticalSection(&QueuedInterruptCritSec);
  509. IF_DEBUG(HW_INTERRUPTS) {
  510. DBGPRINT("VrResumeHook - FrozenInterrupts = %d QueuedInterrupts = %d\n",
  511. FrozenInterrupts,
  512. QueuedInterrupts
  513. );
  514. }
  515. FrozenVdmContext = FALSE;
  516. if (QueuedInterrupts > FrozenInterrupts) {
  517. //
  518. // interrupts were queued while the 16-bit context was suspended. If
  519. // the QueuedInterrupts count was -1 when we took the snapshot then
  520. // we must interrupt the VDM. The count has already been updated to
  521. // account for the interrupt, but none was delivered. Do it here
  522. //
  523. // if (FrozenInterrupts == -1) {
  524. IF_DEBUG(HW_INTERRUPTS) {
  525. DBGPRINT("*** VrResumeHook: interrupting VDM ***\n");
  526. }
  527. call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
  528. // }
  529. }
  530. LeaveCriticalSection(&QueuedInterruptCritSec);
  531. }