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.

1670 lines
43 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. VDM.C
  5. Abstract:
  6. This module contains support routines for the x86 monitor for
  7. running Dos applications in V86 mode.
  8. Author:
  9. Dave Hastings (daveh) 20 Mar 1991
  10. Environment:
  11. The code in this module is all x86 specific.
  12. Notes:
  13. In its current implementation, this code is less robust than it needs
  14. to be. This will be fixed. Specifically, parameter verification needs
  15. to be done. (daveh 7/15/91)
  16. Support for 32 bit segments (2/2/92)
  17. Revision History:
  18. 20-Mar-1991 daveh
  19. created
  20. --*/
  21. #include "ki.h"
  22. #pragma hdrstop
  23. #include "vdmntos.h"
  24. #include "..\..\vdm\i386\vdmp.h"
  25. #define VDM_IO_TEST 0
  26. #if VDM_IO_TEST
  27. VOID
  28. TestIoHandlerStuff(
  29. VOID
  30. );
  31. #endif
  32. BOOLEAN
  33. Ki386VdmDispatchIo(
  34. IN ULONG PortNumber,
  35. IN ULONG Size,
  36. IN BOOLEAN Read,
  37. IN UCHAR InstructionSize,
  38. IN PKTRAP_FRAME TrapFrame
  39. );
  40. BOOLEAN
  41. Ki386VdmDispatchStringIo(
  42. IN ULONG PortNumber,
  43. IN ULONG Size,
  44. IN BOOLEAN Rep,
  45. IN BOOLEAN Read,
  46. IN ULONG Count,
  47. IN ULONG Address,
  48. IN UCHAR InstructionSize,
  49. IN PKTRAP_FRAME TrapFrame
  50. );
  51. BOOLEAN
  52. VdmDispatchIoToHandler(
  53. IN PVDM_IO_HANDLER VdmIoHandler,
  54. IN ULONG Context,
  55. IN ULONG PortNumber,
  56. IN ULONG Size,
  57. IN BOOLEAN Read,
  58. IN OUT PULONG Data
  59. );
  60. BOOLEAN
  61. VdmDispatchUnalignedIoToHandler(
  62. IN PVDM_IO_HANDLER VdmIoHandler,
  63. IN ULONG Context,
  64. IN ULONG PortNumber,
  65. IN ULONG Size,
  66. IN BOOLEAN Read,
  67. IN OUT PULONG Data
  68. );
  69. BOOLEAN
  70. VdmDispatchStringIoToHandler(
  71. IN PVDM_IO_HANDLER VdmIoHandler,
  72. IN ULONG Context,
  73. IN ULONG PortNumber,
  74. IN ULONG Size,
  75. IN ULONG Count,
  76. IN BOOLEAN Read,
  77. IN ULONG Data
  78. );
  79. BOOLEAN
  80. VdmCallStringIoHandler(
  81. IN PVDM_IO_HANDLER VdmIoHandler,
  82. IN PVOID StringIoRoutine,
  83. IN ULONG Context,
  84. IN ULONG PortNumber,
  85. IN ULONG Size,
  86. IN ULONG Count,
  87. IN BOOLEAN Read,
  88. IN ULONG Data
  89. );
  90. BOOLEAN
  91. VdmConvertToLinearAddress(
  92. IN ULONG SegmentedAddress,
  93. IN PVOID *LinearAddress
  94. );
  95. VOID
  96. KeI386VdmInitialize(
  97. VOID
  98. );
  99. ULONG
  100. Ki386VdmEnablePentiumExtentions(
  101. ULONG
  102. );
  103. VOID
  104. Ki386AdlibEmulation(
  105. IN ULONG PortNumber,
  106. IN BOOLEAN Read,
  107. IN PKTRAP_FRAME TrapFrame
  108. );
  109. #pragma alloc_text(PAGE, Ki386VdmDispatchIo)
  110. #pragma alloc_text(PAGE, Ki386VdmDispatchStringIo)
  111. #pragma alloc_text(PAGE, VdmDispatchIoToHandler)
  112. #pragma alloc_text(PAGE, VdmDispatchUnalignedIoToHandler)
  113. #pragma alloc_text(PAGE, VdmDispatchStringIoToHandler)
  114. #pragma alloc_text(PAGE, VdmCallStringIoHandler)
  115. #pragma alloc_text(PAGE, VdmConvertToLinearAddress)
  116. #pragma alloc_text(PAGE, Ki386AdlibEmulation)
  117. #pragma alloc_text(INIT, KeI386VdmInitialize)
  118. KMUTEX VdmStringIoMutex;
  119. ULONG KeI386EFlagsAndMaskV86 = EFLAGS_USER_SANITIZE;
  120. ULONG KeI386EFlagsOrMaskV86 = EFLAGS_INTERRUPT_MASK;
  121. ULONG KeI386VirtualIntExtensions = 0;
  122. BOOLEAN
  123. Ki386GetSelectorParameters(
  124. IN USHORT Selector,
  125. OUT PULONG pFlags,
  126. OUT PULONG pBase,
  127. OUT PULONG pLimit
  128. )
  129. /*++
  130. Routine Description:
  131. This routine gets information about a selector in the ldt, and
  132. returns it to the caller.
  133. Arguments:
  134. IN USHORT Selector -- selector number for selector to return info for
  135. OUT PULONG Flags -- flags indicating the type of the selector.
  136. OUT PULONG Base -- base linear address of the selector
  137. OUT PULONG Limit -- limit of the selector.
  138. Return Value:
  139. return-value - True if the selector is in the LDT, and present.
  140. False otherwise.
  141. Note:
  142. This routine should probably be somewhere else. There are a number
  143. of issues to clear up with respect to selectors and the kernel, and
  144. after they have been cleared up, this code will be moved to its
  145. correct place
  146. --*/
  147. {
  148. PLDT_ENTRY Ldt;
  149. ULONG LdtLimit;
  150. PKPROCESS Process;
  151. BOOLEAN ReturnValue = TRUE;
  152. LDT_ENTRY LdtEntry={0};
  153. ULONG Flags;
  154. ULONG Base;
  155. ULONG Limit;
  156. KIRQL OldIrql;
  157. *pFlags = 0;
  158. Flags = 0;
  159. if ((Selector & (SELECTOR_TABLE_INDEX | DPL_USER))
  160. != (SELECTOR_TABLE_INDEX | DPL_USER)) {
  161. return FALSE;
  162. }
  163. Process = KeGetCurrentThread()->ApcState.Process;
  164. Selector &= ~(SELECTOR_TABLE_INDEX | DPL_USER);
  165. //
  166. // Protect against LDT changes which occur in cross processor DPC's
  167. //
  168. KeRaiseIrql (DISPATCH_LEVEL, &OldIrql);
  169. Ldt = (PLDT_ENTRY)((Process->LdtDescriptor.BaseLow) |
  170. (Process->LdtDescriptor.HighWord.Bytes.BaseMid << 16) |
  171. (Process->LdtDescriptor.HighWord.Bytes.BaseHi << 24));
  172. LdtLimit = ((Process->LdtDescriptor.LimitLow) |
  173. (Process->LdtDescriptor.HighWord.Bits.LimitHi << 16));
  174. if (((ULONG)Selector >= LdtLimit) || (!Ldt)) {
  175. ReturnValue = FALSE;
  176. } else {
  177. LdtEntry = Ldt[Selector/sizeof(LDT_ENTRY)];
  178. ReturnValue = TRUE;
  179. }
  180. //
  181. // Restore IRQL
  182. //
  183. KeLowerIrql (OldIrql);
  184. if (ReturnValue) {
  185. if (!LdtEntry.HighWord.Bits.Pres) {
  186. Flags = SEL_TYPE_NP;
  187. ReturnValue = FALSE;
  188. } else {
  189. Base = (LdtEntry.BaseLow |
  190. (LdtEntry.HighWord.Bytes.BaseMid << 16) |
  191. (LdtEntry.HighWord.Bytes.BaseHi << 24));
  192. Limit = (LdtEntry.LimitLow |
  193. (LdtEntry.HighWord.Bits.LimitHi << 16));
  194. if ((LdtEntry.HighWord.Bits.Type & 0x18) == 0x18) {
  195. Flags |= SEL_TYPE_EXECUTE;
  196. if (LdtEntry.HighWord.Bits.Type & 0x02) {
  197. Flags |= SEL_TYPE_READ;
  198. }
  199. } else {
  200. Flags |= SEL_TYPE_READ;
  201. if (LdtEntry.HighWord.Bits.Type & 0x02) {
  202. Flags |= SEL_TYPE_WRITE;
  203. }
  204. if (LdtEntry.HighWord.Bits.Type & 0x04) {
  205. Flags |= SEL_TYPE_ED;
  206. }
  207. }
  208. if (LdtEntry.HighWord.Bits.Default_Big) {
  209. Flags |= SEL_TYPE_BIG;
  210. }
  211. if (LdtEntry.HighWord.Bits.Granularity) {
  212. Flags |= SEL_TYPE_2GIG;
  213. }
  214. *pBase = Base;
  215. *pLimit = Limit;
  216. }
  217. }
  218. *pFlags = Flags;
  219. return ReturnValue;
  220. }
  221. BOOLEAN
  222. Ki386VdmDispatchIo(
  223. IN ULONG PortNumber,
  224. IN ULONG Size,
  225. IN BOOLEAN Read,
  226. IN UCHAR InstructionSize,
  227. IN PKTRAP_FRAME TrapFrame
  228. )
  229. /*++
  230. Routine Description:
  231. This routine sets up the Event info for an IO event, and causes the
  232. event to be reflected to the Monitor.
  233. It is assumed that interrupts are enabled upon entry, and Irql is
  234. at APC level.
  235. Arguments:
  236. PortNumber -- Supplies the port number the IO was done to
  237. Size -- Supplies the size of the IO operation.
  238. Read -- Indicates whether the IO operation was a read or a write.
  239. InstructionSize -- Supplies the size of the IO instruction in bytes.
  240. Return Value:
  241. True if the io instruction will be reflected to User mode.
  242. --*/
  243. {
  244. PVDM_TIB VdmTib;
  245. EXCEPTION_RECORD ExceptionRecord;
  246. VDM_IO_HANDLER VdmIoHandler;
  247. ULONG Result;
  248. BOOLEAN Success = FALSE;
  249. ULONG Context;
  250. PVDM_PROCESS_OBJECTS pVdmObjects;
  251. //
  252. // First check if this port needs special handling
  253. //
  254. if (Size == 1) {
  255. pVdmObjects = PsGetCurrentProcess()->VdmObjects;
  256. if (pVdmObjects &&
  257. (pVdmObjects->AdlibAction == ADLIB_DIRECT_IO ||
  258. pVdmObjects->AdlibAction == ADLIB_KERNEL_EMULATION)) {
  259. if ((PortNumber >= pVdmObjects->AdlibPhysPortStart &&
  260. PortNumber <= pVdmObjects->AdlibPhysPortEnd) ||
  261. (PortNumber >= pVdmObjects->AdlibVirtPortStart &&
  262. PortNumber <= pVdmObjects->AdlibVirtPortEnd)) {
  263. Ki386AdlibEmulation(PortNumber,
  264. Read,
  265. TrapFrame);
  266. TrapFrame->Eip += InstructionSize;
  267. return TRUE;
  268. }
  269. }
  270. }
  271. Success = Ps386GetVdmIoHandler(
  272. PsGetCurrentProcess(),
  273. PortNumber & ~0x3,
  274. &VdmIoHandler,
  275. &Context
  276. );
  277. if (Success) {
  278. Result = TrapFrame->Eax;
  279. // if port is not aligned, perform unaligned IO
  280. // else do the io the easy way
  281. if (PortNumber % Size) {
  282. Success = VdmDispatchUnalignedIoToHandler(
  283. &VdmIoHandler,
  284. Context,
  285. PortNumber,
  286. Size,
  287. Read,
  288. &Result
  289. );
  290. } else {
  291. Success = VdmDispatchIoToHandler(
  292. &VdmIoHandler,
  293. Context,
  294. PortNumber,
  295. Size,
  296. Read,
  297. &Result
  298. );
  299. }
  300. } else {
  301. Result = 0; // satisfy no_opt compiler
  302. }
  303. if (Success) {
  304. if (Read) {
  305. switch (Size) {
  306. case 4:
  307. TrapFrame->Eax = Result;
  308. break;
  309. case 2:
  310. *(PUSHORT)(&TrapFrame->Eax) = (USHORT)Result;
  311. break;
  312. case 1:
  313. *(PUCHAR)(&TrapFrame->Eax) = (UCHAR)Result;
  314. break;
  315. }
  316. }
  317. TrapFrame->Eip += (ULONG) InstructionSize;
  318. return TRUE;
  319. } else {
  320. if (!NT_SUCCESS (VdmpGetVdmTib(&VdmTib))) {
  321. ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
  322. ExceptionRecord.ExceptionFlags = 0;
  323. ExceptionRecord.NumberParameters = 0;
  324. ExRaiseException(&ExceptionRecord);
  325. return FALSE;
  326. }
  327. try {
  328. VdmTib->EventInfo.InstructionSize = (ULONG) InstructionSize;
  329. VdmTib->EventInfo.Event = VdmIO;
  330. VdmTib->EventInfo.IoInfo.PortNumber = (USHORT)PortNumber;
  331. VdmTib->EventInfo.IoInfo.Size = (USHORT)Size;
  332. VdmTib->EventInfo.IoInfo.Read = Read;
  333. } except(EXCEPTION_EXECUTE_HANDLER) {
  334. ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
  335. ExceptionRecord.ExceptionFlags = 0;
  336. ExceptionRecord.NumberParameters = 0;
  337. ExRaiseException(&ExceptionRecord);
  338. return FALSE;
  339. }
  340. }
  341. VdmEndExecution(TrapFrame, VdmTib);
  342. return TRUE;
  343. }
  344. BOOLEAN
  345. Ki386VdmDispatchStringIo(
  346. IN ULONG PortNumber,
  347. IN ULONG Size,
  348. IN BOOLEAN Rep,
  349. IN BOOLEAN Read,
  350. IN ULONG Count,
  351. IN ULONG Address,
  352. IN UCHAR InstructionSize,
  353. IN PKTRAP_FRAME TrapFrame
  354. )
  355. /*++
  356. Routine Description:
  357. This routine sets up the Event info for a string IO event, and causes the
  358. event to be reflected to the Monitor.
  359. It is assumed that interrupts are enabled upon entry, and Irql is
  360. at APC level.
  361. Arguments:
  362. PortNumber -- Supplies the port number the IO was done to
  363. Size -- Supplies the size of the IO operation.
  364. Read -- Indicates whether the IO operation was a read or a write.
  365. Count -- indicates the number of IO operations of Size size
  366. Address -- Indicates address for string io
  367. InstructionSize -- Supplies the size of the IO instruction in bytes.
  368. Return Value:
  369. True if the io instruction will be reflected to User mode.
  370. --*/
  371. {
  372. PVDM_TIB VdmTib;
  373. EXCEPTION_RECORD ExceptionRecord;
  374. BOOLEAN Success = FALSE;
  375. VDM_IO_HANDLER VdmIoHandler;
  376. ULONG Context;
  377. Success = Ps386GetVdmIoHandler(
  378. PsGetCurrentProcess(),
  379. PortNumber & ~0x3,
  380. &VdmIoHandler,
  381. &Context
  382. );
  383. if (Success) {
  384. Success = VdmDispatchStringIoToHandler(
  385. &VdmIoHandler,
  386. Context,
  387. PortNumber,
  388. Size,
  389. Count,
  390. Read,
  391. Address
  392. );
  393. }
  394. if (Success) {
  395. PUSHORT pIndexRegister;
  396. USHORT Index;
  397. // WARNING no 32 bit address support
  398. pIndexRegister = Read ? (PUSHORT)&TrapFrame->Edi
  399. : (PUSHORT)&TrapFrame->Esi;
  400. if (TrapFrame->EFlags & EFLAGS_DF_MASK) {
  401. Index = *pIndexRegister - (USHORT)(Count * Size);
  402. }
  403. else {
  404. Index = *pIndexRegister + (USHORT)(Count * Size);
  405. }
  406. *pIndexRegister = Index;
  407. if (Rep) {
  408. TrapFrame->Ecx = 0;
  409. }
  410. TrapFrame->Eip += (ULONG) InstructionSize;
  411. return TRUE;
  412. }
  413. if (!NT_SUCCESS (VdmpGetVdmTib(&VdmTib))) {
  414. ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
  415. ExceptionRecord.ExceptionFlags = 0;
  416. ExceptionRecord.NumberParameters = 0;
  417. ExRaiseException(&ExceptionRecord);
  418. return FALSE;
  419. }
  420. try {
  421. VdmTib->EventInfo.InstructionSize = (ULONG) InstructionSize;
  422. VdmTib->EventInfo.Event = VdmStringIO;
  423. VdmTib->EventInfo.StringIoInfo.PortNumber = (USHORT)PortNumber;
  424. VdmTib->EventInfo.StringIoInfo.Size = (USHORT)Size;
  425. VdmTib->EventInfo.StringIoInfo.Rep = Rep;
  426. VdmTib->EventInfo.StringIoInfo.Read = Read;
  427. VdmTib->EventInfo.StringIoInfo.Count = Count;
  428. VdmTib->EventInfo.StringIoInfo.Address = Address;
  429. } except(EXCEPTION_EXECUTE_HANDLER) {
  430. ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
  431. ExceptionRecord.ExceptionFlags = 0;
  432. ExceptionRecord.NumberParameters = 0;
  433. ExRaiseException(&ExceptionRecord);
  434. return FALSE;
  435. }
  436. VdmEndExecution(TrapFrame, VdmTib);
  437. return TRUE;
  438. }
  439. BOOLEAN
  440. VdmDispatchIoToHandler(
  441. IN PVDM_IO_HANDLER VdmIoHandler,
  442. IN ULONG Context,
  443. IN ULONG PortNumber,
  444. IN ULONG Size,
  445. IN BOOLEAN Read,
  446. IN OUT PULONG Data
  447. )
  448. /*++
  449. Routine Description:
  450. This routine calls the handler for the IO. If there is not a handler
  451. of the proper size, it will call this function for 2 io's to the next
  452. smaller size. If the size was a byte, and there was no handler, FALSE
  453. is returned.
  454. Arguments:
  455. VdmIoHandler -- Supplies a pointer to the handler table
  456. Context -- Supplies 32 bits of data set when the port was trapped
  457. PortNumber -- Supplies the port number the IO was done to
  458. Size -- Supplies the size of the IO operation.
  459. Read -- Indicates whether the IO operation was a read or a write.
  460. Result -- Supplies a pointer to the location to put the result
  461. Return Value:
  462. True if one or more handlers were called to take care of the IO.
  463. False if no handler was called to take care of the IO.
  464. --*/
  465. {
  466. NTSTATUS Status;
  467. BOOLEAN Success1, Success2;
  468. USHORT FnIndex;
  469. UCHAR AccessType;
  470. // Insure that Io is aligned
  471. ASSERT((!(PortNumber % Size)));
  472. if (Read) {
  473. FnIndex = 0;
  474. AccessType = EMULATOR_READ_ACCESS;
  475. } else {
  476. FnIndex = 1;
  477. AccessType = EMULATOR_WRITE_ACCESS;
  478. }
  479. switch (Size) {
  480. case 1:
  481. if (VdmIoHandler->IoFunctions[FnIndex].UcharIo[PortNumber % 4]) {
  482. Status = (*(VdmIoHandler->IoFunctions[FnIndex].UcharIo[PortNumber % 4]))(
  483. Context,
  484. PortNumber,
  485. AccessType,
  486. (PUCHAR)Data
  487. );
  488. if (NT_SUCCESS(Status)) {
  489. return TRUE;
  490. }
  491. }
  492. // No handler for this port
  493. return FALSE;
  494. case 2:
  495. if (VdmIoHandler->IoFunctions[FnIndex].UshortIo[PortNumber % 2]) {
  496. Status = (*(VdmIoHandler->IoFunctions[FnIndex].UshortIo[PortNumber % 2]))(
  497. Context,
  498. PortNumber,
  499. AccessType,
  500. (PUSHORT)Data
  501. );
  502. if (NT_SUCCESS(Status)) {
  503. return TRUE;
  504. }
  505. } else {
  506. // Dispatch to the two uchar handlers for this ushort port
  507. Success1 = VdmDispatchIoToHandler(
  508. VdmIoHandler,
  509. Context,
  510. PortNumber,
  511. Size /2,
  512. Read,
  513. Data
  514. );
  515. Success2 = VdmDispatchIoToHandler(
  516. VdmIoHandler,
  517. Context,
  518. PortNumber + 1,
  519. Size / 2,
  520. Read,
  521. (PULONG)((PUCHAR)Data + 1)
  522. );
  523. return (Success1 || Success2);
  524. }
  525. return FALSE;
  526. case 4:
  527. if (VdmIoHandler->IoFunctions[FnIndex].UlongIo) {
  528. Status = (*(VdmIoHandler->IoFunctions[FnIndex].UlongIo))(
  529. Context,
  530. PortNumber,
  531. AccessType,
  532. Data
  533. );
  534. if (NT_SUCCESS(Status)) {
  535. return TRUE;
  536. }
  537. } else {
  538. // Dispatch to the two ushort handlers for this port
  539. Success1 = VdmDispatchIoToHandler(
  540. VdmIoHandler,
  541. Context,
  542. PortNumber,
  543. Size /2,
  544. Read,
  545. Data);
  546. Success2 = VdmDispatchIoToHandler(
  547. VdmIoHandler,
  548. Context,
  549. PortNumber + 2,
  550. Size / 2,
  551. Read,
  552. (PULONG)((PUSHORT)Data + 1)
  553. );
  554. return (Success1 || Success2);
  555. }
  556. return FALSE;
  557. }
  558. return FALSE;
  559. }
  560. BOOLEAN
  561. VdmDispatchUnalignedIoToHandler(
  562. IN PVDM_IO_HANDLER VdmIoHandler,
  563. IN ULONG Context,
  564. IN ULONG PortNumber,
  565. IN ULONG Size,
  566. IN BOOLEAN Read,
  567. IN OUT PULONG Data
  568. )
  569. /*++
  570. Routine Description:
  571. This routine converts the unaligned IO to the necessary number of aligned
  572. IOs to smaller ports.
  573. Arguments:
  574. VdmIoHandler -- Supplies a pointer to the handler table
  575. Context -- Supplies 32 bits of data set when the port was trapped
  576. PortNumber -- Supplies the port number the IO was done to
  577. Size -- Supplies the size of the IO operation.
  578. Read -- Indicates whether the IO operation was a read or a write.
  579. Result -- Supplies a pointer to the location to put the result
  580. Return Value:
  581. True if one or more handlers were called to take care of the IO.
  582. False if no handler was called to take care of the IO.
  583. --*/
  584. {
  585. ULONG Offset;
  586. BOOLEAN Success;
  587. ASSERT((Size > 1));
  588. ASSERT((PortNumber % Size));
  589. Offset = 0;
  590. //
  591. // The possible unaligned io situations are as follows.
  592. //
  593. // 1. Uchar aligned Ulong io
  594. // We have to dispatch a uchar io, a ushort io, and a uchar io
  595. //
  596. // 2. Ushort aligned Ulong Io
  597. // We have to dispatch a ushort io, and a ushort io
  598. //
  599. // 3. Uchar aligned Ushort Io
  600. // We have to dispatch a uchar io and a uchar io
  601. //
  602. // if the port is uchar aligned
  603. if ((PortNumber % Size) & 1) {
  604. Success = VdmDispatchIoToHandler(
  605. VdmIoHandler,
  606. Context,
  607. PortNumber,
  608. 1,
  609. Read,
  610. Data
  611. );
  612. Offset += 1;
  613. // else it is ushort aligned (and therefore must be a ulong port)
  614. } else {
  615. Success = VdmDispatchIoToHandler(
  616. VdmIoHandler,
  617. Context,
  618. PortNumber,
  619. 2,
  620. Read,
  621. Data
  622. );
  623. Offset += 2;
  624. }
  625. // if it is a ulong port, we know we have a ushort IO to dispatch
  626. if (Size == 4) {
  627. Success |= VdmDispatchIoToHandler(
  628. VdmIoHandler,
  629. Context,
  630. PortNumber + Offset,
  631. 2,
  632. Read,
  633. (PULONG)((PUCHAR)Data + Offset)
  634. );
  635. Offset += 2;
  636. }
  637. // If we haven't dispatched the entire port, dispatch the final uchar
  638. if (Offset != 4) {
  639. Success |= VdmDispatchIoToHandler(
  640. VdmIoHandler,
  641. Context,
  642. PortNumber + Offset,
  643. 1,
  644. Read,
  645. (PULONG)((PUCHAR)Data + Offset)
  646. );
  647. }
  648. return Success;
  649. }
  650. BOOLEAN
  651. VdmDispatchStringIoToHandler(
  652. IN PVDM_IO_HANDLER VdmIoHandler,
  653. IN ULONG Context,
  654. IN ULONG PortNumber,
  655. IN ULONG Size,
  656. IN ULONG Count,
  657. IN BOOLEAN Read,
  658. IN ULONG Data
  659. )
  660. /*++
  661. Routine Description:
  662. This routine calls the handler for the IO. If there is not a handler
  663. of the proper size, or the io is not aligned, it will simulate the io
  664. to the normal io handlers.
  665. Arguments:
  666. VdmIoHandler -- Supplies a pointer to the handler table
  667. Context -- Supplies 32 bits of data set when the port was trapped
  668. PortNumber -- Supplies the port number the IO was done to
  669. Size -- Supplies the size of the IO operation.
  670. Count -- Supplies the number of IO operations.
  671. Read -- Indicates whether the IO operation was a read or a write.
  672. Data -- Supplies a segmented address at which to put the result.
  673. Return Value:
  674. True if one or more handlers were called to take care of the IO.
  675. False if no handler was called to take care of the IO.
  676. --*/
  677. {
  678. BOOLEAN Success = FALSE;
  679. USHORT FnIndex;
  680. NTSTATUS Status;
  681. if (Read) {
  682. FnIndex = 0;
  683. } else {
  684. FnIndex = 1;
  685. }
  686. Status = KeWaitForSingleObject(
  687. &VdmStringIoMutex,
  688. Executive,
  689. KernelMode,
  690. FALSE,
  691. NULL
  692. );
  693. if (!NT_SUCCESS(Status)) {
  694. return FALSE;
  695. }
  696. try {
  697. switch (Size) {
  698. case 1:
  699. Success = VdmCallStringIoHandler(
  700. VdmIoHandler,
  701. (PVOID)(ULONG_PTR)VdmIoHandler->IoFunctions[FnIndex].UcharStringIo[PortNumber % 4],
  702. Context,
  703. PortNumber,
  704. Size,
  705. Count,
  706. Read,
  707. Data
  708. );
  709. break;
  710. case 2:
  711. Success = VdmCallStringIoHandler(
  712. VdmIoHandler,
  713. (PVOID)(ULONG_PTR)VdmIoHandler->IoFunctions[FnIndex].UshortStringIo[PortNumber % 2],
  714. Context,
  715. PortNumber,
  716. Size,
  717. Count,
  718. Read,
  719. Data
  720. );
  721. break;
  722. case 4:
  723. Success = VdmCallStringIoHandler(
  724. VdmIoHandler,
  725. (PVOID)(ULONG_PTR)VdmIoHandler->IoFunctions[FnIndex].UlongStringIo,
  726. Context,
  727. PortNumber,
  728. Size,
  729. Count,
  730. Read,
  731. Data
  732. );
  733. break;
  734. }
  735. } except(EXCEPTION_EXECUTE_HANDLER) {
  736. // Cause kernel exit, rather than Io reflection
  737. Success = TRUE;
  738. }
  739. KeReleaseMutex(&VdmStringIoMutex, FALSE);
  740. return Success;
  741. }
  742. #define STRINGIO_BUFFER_SIZE 1024
  743. UCHAR VdmStringIoBuffer[STRINGIO_BUFFER_SIZE];
  744. BOOLEAN
  745. VdmCallStringIoHandler(
  746. IN PVDM_IO_HANDLER VdmIoHandler,
  747. IN PVOID StringIoRoutine,
  748. IN ULONG Context,
  749. IN ULONG PortNumber,
  750. IN ULONG Size,
  751. IN ULONG Count,
  752. IN BOOLEAN Read,
  753. IN ULONG Data
  754. )
  755. /*++
  756. Routine Description:
  757. This routine actually performs the call to string io routine. It takes
  758. care of buffering the user data in kernel space so that the device driver
  759. does not have to. If there is not a string io function, or the io is
  760. misaligned, it will be simulated as a series of normal io operations
  761. Arguments:
  762. StringIoRoutine -- Supplies a pointer to the string Io routine
  763. Context -- Supplies 32 bits of data set when the port was trapped
  764. PortNumber -- Supplies the number of the port to perform Io to
  765. Size -- Supplies the size of the io operations
  766. Count -- Supplies the number of Io operations in the string.
  767. Read -- Indicates a read operation
  768. Data -- Supplies a pointer to the user buffer to perform the io on.
  769. Returns
  770. TRUE if a handler was called
  771. FALSE if not.
  772. --*/
  773. {
  774. ULONG TotalBytes,BytesDone,BytesToDo,LoopCount,NumberIo;
  775. PUCHAR CurrentDataPtr;
  776. UCHAR AccessType;
  777. EXCEPTION_RECORD ExceptionRecord;
  778. NTSTATUS Status;
  779. BOOLEAN Success;
  780. Success = VdmConvertToLinearAddress(
  781. Data,
  782. &CurrentDataPtr
  783. );
  784. if (!Success) {
  785. ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
  786. ExceptionRecord.ExceptionFlags = 0;
  787. ExceptionRecord.NumberParameters = 0;
  788. ExRaiseException(&ExceptionRecord);
  789. // Cause kernel exit, rather than Io reflection
  790. return TRUE;
  791. }
  792. TotalBytes = Count * Size;
  793. BytesDone = 0;
  794. if (PortNumber % Size) {
  795. StringIoRoutine = NULL;
  796. }
  797. if (Read) {
  798. AccessType = EMULATOR_READ_ACCESS;
  799. } else {
  800. AccessType = EMULATOR_WRITE_ACCESS;
  801. }
  802. // Set up try out here to avoid overhead in loop
  803. try {
  804. while (BytesDone < TotalBytes) {
  805. if ((BytesDone + STRINGIO_BUFFER_SIZE) > TotalBytes) {
  806. BytesToDo = TotalBytes - BytesDone;
  807. } else {
  808. BytesToDo = STRINGIO_BUFFER_SIZE;
  809. }
  810. ASSERT((!(BytesToDo % Size)));
  811. if (!Read) {
  812. RtlCopyMemory(VdmStringIoBuffer, CurrentDataPtr, BytesToDo);
  813. }
  814. NumberIo = BytesToDo / Size;
  815. if (StringIoRoutine) {
  816. // in order to avoid having 3 separate calls, one for each size
  817. // we simply cast the parameters appropriately for the
  818. // byte routine.
  819. Status = (*((PDRIVER_IO_PORT_UCHAR_STRING)(ULONG_PTR)StringIoRoutine))(
  820. Context,
  821. PortNumber,
  822. AccessType,
  823. VdmStringIoBuffer,
  824. NumberIo
  825. );
  826. if (NT_SUCCESS(Status)) {
  827. Success |= TRUE;
  828. }
  829. } else {
  830. if (PortNumber % Size) {
  831. for (LoopCount = 0; LoopCount < NumberIo; LoopCount++ ) {
  832. Success |= VdmDispatchUnalignedIoToHandler(
  833. VdmIoHandler,
  834. Context,
  835. PortNumber,
  836. Size,
  837. Read,
  838. (PULONG)(VdmStringIoBuffer + LoopCount * Size)
  839. );
  840. }
  841. } else {
  842. for (LoopCount = 0; LoopCount < NumberIo; LoopCount++ ) {
  843. Success |= VdmDispatchIoToHandler(
  844. VdmIoHandler,
  845. Context,
  846. PortNumber,
  847. Size,
  848. Read,
  849. (PULONG)(VdmStringIoBuffer + LoopCount * Size)
  850. );
  851. }
  852. }
  853. }
  854. if (Read) {
  855. RtlCopyMemory(CurrentDataPtr, VdmStringIoBuffer, BytesToDo);
  856. }
  857. BytesDone += BytesToDo;
  858. CurrentDataPtr += BytesToDo;
  859. }
  860. } except(EXCEPTION_EXECUTE_HANDLER) {
  861. ExceptionRecord.ExceptionCode = GetExceptionCode();
  862. ExceptionRecord.ExceptionFlags = 0;
  863. ExceptionRecord.NumberParameters = 0;
  864. ExRaiseException(&ExceptionRecord);
  865. // Cause kernel exit, rather than Io reflection
  866. Success = TRUE;
  867. }
  868. return Success;
  869. }
  870. BOOLEAN
  871. VdmConvertToLinearAddress(
  872. IN ULONG SegmentedAddress,
  873. OUT PVOID *LinearAddress
  874. )
  875. /*++
  876. Routine Description:
  877. This routine converts the specified segmented address into a linear
  878. address, based on processor mode in user mode.
  879. Arguments:
  880. SegmentedAddress -- Supplies the segmented address to convert.
  881. LinearAddress -- Supplies a pointer to the destination for the
  882. coresponding linear address
  883. Return Value:
  884. True if the address was converted.
  885. False otherwise
  886. Note:
  887. A linear address of 0 is a valid return
  888. --*/
  889. {
  890. PKTHREAD Thread;
  891. PKTRAP_FRAME TrapFrame;
  892. BOOLEAN Success;
  893. ULONG Base, Limit, Flags;
  894. Thread = KeGetCurrentThread();
  895. TrapFrame = VdmGetTrapFrame(Thread);
  896. if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
  897. *LinearAddress = (PVOID)(((SegmentedAddress & 0xFFFF0000) >> 12) +
  898. (SegmentedAddress & 0xFFFF));
  899. Success = TRUE;
  900. } else {
  901. Success = Ki386GetSelectorParameters(
  902. (USHORT)((SegmentedAddress & 0xFFFF0000) >> 16),
  903. &Flags,
  904. &Base,
  905. &Limit
  906. );
  907. if (Success) {
  908. *LinearAddress = (PVOID)(Base + (SegmentedAddress & 0xFFFF));
  909. }
  910. }
  911. return Success;
  912. }
  913. VOID
  914. KeI386VdmInitialize(
  915. VOID
  916. )
  917. /*++
  918. Routine Description:
  919. This routine initializes the vdm stuff
  920. Arguments:
  921. None
  922. Return Value:
  923. None
  924. --*/
  925. {
  926. NTSTATUS Status;
  927. OBJECT_ATTRIBUTES ObjectAttributes;
  928. HANDLE RegistryHandle = NULL;
  929. UNICODE_STRING WorkString;
  930. UCHAR KeyInformation[sizeof(KEY_VALUE_BASIC_INFORMATION) + 30];
  931. ULONG ResultLength;
  932. KeInitializeMutex( &VdmStringIoMutex, MUTEX_LEVEL_VDM_IO );
  933. //
  934. // Set up and open KeyPath to wow key
  935. //
  936. RtlInitUnicodeString(
  937. &WorkString,
  938. L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Wow"
  939. );
  940. InitializeObjectAttributes(
  941. &ObjectAttributes,
  942. &WorkString,
  943. OBJ_CASE_INSENSITIVE,
  944. (HANDLE)NULL,
  945. NULL
  946. );
  947. Status = ZwOpenKey(
  948. &RegistryHandle,
  949. KEY_READ,
  950. &ObjectAttributes
  951. );
  952. //
  953. // If there is no Wow key, don't allow Vdms to run
  954. //
  955. if (!NT_SUCCESS(Status)) {
  956. return;
  957. }
  958. //
  959. // Set up for using virtual interrupt extensions if they are available
  960. //
  961. //
  962. // Get the Pentium Feature disable value.
  963. // If this value is present, don't enable vme stuff.
  964. //
  965. RtlInitUnicodeString(
  966. &WorkString,
  967. L"DisableVme"
  968. );
  969. Status = ZwQueryValueKey(
  970. RegistryHandle,
  971. &WorkString,
  972. KeyValueBasicInformation,
  973. &KeyInformation[0],
  974. sizeof(KEY_VALUE_BASIC_INFORMATION) + 30,
  975. &ResultLength
  976. );
  977. if (!NT_SUCCESS(Status)) {
  978. //
  979. // If we have the extensions, set the appropriate bits
  980. // in cr4
  981. //
  982. if (KeFeatureBits & KF_V86_VIS) {
  983. KeIpiGenericCall(
  984. Ki386VdmEnablePentiumExtentions,
  985. TRUE
  986. );
  987. KeI386VirtualIntExtensions = V86_VIRTUAL_INT_EXTENSIONS;
  988. }
  989. }
  990. ZwClose(RegistryHandle);
  991. }
  992. BOOLEAN
  993. KeVdmInsertQueueApc (
  994. IN PKAPC Apc,
  995. IN PKTHREAD Thread,
  996. IN KPROCESSOR_MODE ApcMode,
  997. IN PKKERNEL_ROUTINE KernelRoutine,
  998. IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
  999. IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL,
  1000. IN PVOID NormalContext OPTIONAL,
  1001. IN KPRIORITY Increment
  1002. )
  1003. /*++
  1004. Routine Description:
  1005. This function initializes and queues a vdm type of APC to the specified
  1006. target thread.
  1007. A Vdm type of APC:
  1008. - OriginalApcEnvironment
  1009. - will only be queued to one thread at a time
  1010. - if UserMode Fires on the next system exit. A UserMode apc should
  1011. not be queued if the current vdm context is not application mode.
  1012. N.B. The delay interrupt lock must be held when this routine is called
  1013. to ensure that no other processor attempts to queue or requeue the
  1014. same APC.
  1015. Arguments:
  1016. Apc - Supplies a pointer to a control object of type APC.
  1017. Thread - Supplies a pointer to a dispatcher object of type thread.
  1018. ApcMode - Supplies the processor mode user\kernel of the Apc
  1019. KernelRoutine - Supplies a pointer to a function that is to be
  1020. executed at IRQL APC_LEVEL in kernel mode.
  1021. RundownRoutine - Supplies an optional pointer to a function that is to be
  1022. called if the APC is in a thread's APC queue when the thread terminates.
  1023. NormalRoutine - Supplies an optional pointer to a function that is
  1024. to be executed at IRQL 0 in the specified processor mode. If this
  1025. parameter is not specified, then the ProcessorMode and NormalContext
  1026. parameters are ignored.
  1027. NormalContext - Supplies a pointer to an arbitrary data structure which is
  1028. to be passed to the function specified by the NormalRoutine parameter.
  1029. Increment - Supplies the priority increment that is to be applied if
  1030. queuing the APC causes a thread wait to be satisfied.
  1031. Return Value:
  1032. If APC queuing is disabled, then a value of FALSE is returned.
  1033. Otherwise a value of TRUE is returned.
  1034. --*/
  1035. {
  1036. PKAPC_STATE ApcState;
  1037. PKTHREAD ApcThread;
  1038. KLOCK_QUEUE_HANDLE LockHandle;
  1039. BOOLEAN Inserted;
  1040. //
  1041. // If the apc object not initialized, then initialize it and acquire
  1042. // the target thread APC queue lock.
  1043. //
  1044. if (Apc->Type != ApcObject) {
  1045. Apc->Type = ApcObject;
  1046. Apc->Size = sizeof(KAPC);
  1047. Apc->ApcStateIndex = OriginalApcEnvironment;
  1048. Apc->Inserted = FALSE;
  1049. } else {
  1050. //
  1051. // Acquire the APC thread APC queue lock and raise IRQL to SYNCH_LEVEL.
  1052. //
  1053. // If the APC is inserted in the corresponding APC queue, and the
  1054. // APC thread is not the same thread as the target thread, then
  1055. // the APC is removed from its current queue, the APC pending state
  1056. // is updated, the APC thread APC queue lock is released, and the
  1057. // target thread APC queue lock is acquired. Otherwise, the APC
  1058. // thread and the target thread are same thread and the APC is already
  1059. // queued to the correct thread.
  1060. //
  1061. // If the APC is not inserted in an APC queue, then release the
  1062. // APC thread APC queue lock and acquire the target thread APC queue
  1063. // lock.
  1064. //
  1065. ApcThread = Apc->Thread;
  1066. if (ApcThread) {
  1067. KeAcquireInStackQueuedSpinLockRaiseToSynch(&ApcThread->ApcQueueLock,
  1068. &LockHandle);
  1069. KiLockDispatcherDatabaseAtSynchLevel();
  1070. if (Apc->Inserted) {
  1071. if (ApcThread == Apc->Thread && Apc->Thread != Thread) {
  1072. Apc->Inserted = FALSE;
  1073. ApcState = Apc->Thread->ApcStatePointer[Apc->ApcStateIndex];
  1074. if (RemoveEntryList(&Apc->ApcListEntry) != FALSE) {
  1075. if (Apc->ApcMode == KernelMode) {
  1076. ApcState->KernelApcPending = FALSE;
  1077. } else {
  1078. ApcState->UserApcPending = FALSE;
  1079. }
  1080. }
  1081. } else {
  1082. KiUnlockDispatcherDatabaseFromSynchLevel();
  1083. KeReleaseInStackQueuedSpinLock(&LockHandle);
  1084. return TRUE;
  1085. }
  1086. }
  1087. KiUnlockDispatcherDatabaseFromSynchLevel();
  1088. KeReleaseInStackQueuedSpinLock(&LockHandle);
  1089. }
  1090. }
  1091. Apc->ApcMode = ApcMode;
  1092. Apc->Thread = Thread;
  1093. Apc->KernelRoutine = KernelRoutine;
  1094. Apc->RundownRoutine = RundownRoutine;
  1095. Apc->NormalRoutine = NormalRoutine;
  1096. Apc->SystemArgument1 = NULL;
  1097. Apc->SystemArgument2 = NULL;
  1098. Apc->NormalContext = NormalContext;
  1099. //
  1100. // Raise IRQL to SYNCH_LEVEL and acquire the thread APC queue lock.
  1101. //
  1102. // If APC queuing is enabled and the APC is not already queued, then
  1103. // insert the APC in the APC queue.
  1104. //
  1105. KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
  1106. if ((Thread->ApcQueueable == TRUE) && (Apc->Inserted == FALSE)) {
  1107. Apc->Inserted = TRUE;
  1108. KiInsertQueueApc(Apc, Increment);
  1109. //
  1110. // If the APC mode is user, then lock the dispatcher database, boost
  1111. // the target thread priorty, and unlock the dispatcher database.
  1112. //
  1113. // N.B. The unlock of the dispatcher database specifying SYNCH_LEVEL
  1114. // is to make sure a dispatch interrupt is generated if necessary.
  1115. //
  1116. if (ApcMode == UserMode) {
  1117. KiLockDispatcherDatabaseAtSynchLevel();
  1118. KiBoostPriorityThread(Thread, Increment);
  1119. Thread->ApcState.UserApcPending = TRUE;
  1120. KiUnlockDispatcherDatabaseFromSynchLevel();
  1121. }
  1122. Inserted = TRUE;
  1123. } else {
  1124. Inserted = FALSE;
  1125. }
  1126. //
  1127. // Unlock the thread APC queue lock, exit the scheduler, and return
  1128. // whether the APC object was inserted.
  1129. //
  1130. KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
  1131. KiExitDispatcher(LockHandle.OldIrql);
  1132. return Inserted;
  1133. }
  1134. #define AD_MASK 0x04 // adlib register used to control opl2
  1135. VOID
  1136. Ki386AdlibEmulation(
  1137. IN ULONG PortNumber,
  1138. IN BOOLEAN Read,
  1139. IN PKTRAP_FRAME TrapFrame
  1140. )
  1141. /*++
  1142. Routine Description:
  1143. This routine performs kernel mode adlib emulation.
  1144. Note, here we only do SB2.0 adlib emulation. That means the only IO ports
  1145. that we emulatated are 0x388, 0x389 and 0x2x8 and 0x2x9.
  1146. Arguments:
  1147. PortNumber -- Supplies the port number the IO was done to
  1148. Size -- Supplies the size of the IO operation.
  1149. Read -- Indicates whether the IO operation was a read or a write.
  1150. InstructionSize -- Supplies the size of the IO instruction in bytes.
  1151. Return Value:
  1152. None.
  1153. --*/
  1154. {
  1155. PVDM_PROCESS_OBJECTS pVdmObjects = PsGetCurrentProcess()->VdmObjects;
  1156. PUCHAR pData = (PUCHAR)&TrapFrame->Eax;
  1157. if (Read) {
  1158. //
  1159. // Must be read status
  1160. //
  1161. *pData = (UCHAR)pVdmObjects->AdlibStatus;
  1162. } else {
  1163. //
  1164. // Could be write adlib index register or write actual data
  1165. //
  1166. if ((PortNumber & 0xf) == 0x8) {
  1167. //
  1168. // It's adlib register select
  1169. //
  1170. pVdmObjects->AdlibIndexRegister = (USHORT)*pData;
  1171. } else {
  1172. //
  1173. // It's adlib data write. We don't actually write any data out.
  1174. // But we will emulate the status change.
  1175. //
  1176. UCHAR data = *pData;
  1177. if ((pVdmObjects->AdlibIndexRegister >= 0xB0 &&
  1178. pVdmObjects->AdlibIndexRegister <= 0xBD) ||
  1179. pVdmObjects->AdlibIndexRegister == AD_MASK) {
  1180. if (pVdmObjects->AdlibIndexRegister == AD_MASK) {
  1181. // Look for RST and starting timers
  1182. if (data & 0x80) {
  1183. pVdmObjects->AdlibStatus = 0x00; // reset both timers
  1184. }
  1185. }
  1186. //
  1187. // We ignore starting of timers if their interrupt
  1188. // flag is set because the timer status will have to
  1189. // be set again to make the status for this timer change
  1190. //
  1191. if ((data & 1) && !(pVdmObjects->AdlibStatus & 0x40)) {
  1192. //
  1193. // simulate immediate expiry of timer1
  1194. //
  1195. pVdmObjects->AdlibStatus |= 0xC0;
  1196. }
  1197. if ((data & 2) && !(pVdmObjects->AdlibStatus & 0x20)) {
  1198. //
  1199. // simulate immediate expiry of timer2
  1200. //
  1201. pVdmObjects->AdlibStatus |= 0xA0;
  1202. }
  1203. }
  1204. }
  1205. }
  1206. }
  1207. //
  1208. // END of ACTIVE CODE
  1209. //
  1210. #if VDM_IO_TEST
  1211. NTSTATUS
  1212. TestIoByteRoutine(
  1213. IN ULONG Port,
  1214. IN UCHAR AccessMode,
  1215. IN OUT PUCHAR Data
  1216. )
  1217. {
  1218. if (AccessMode & EMULATOR_READ_ACCESS) {
  1219. *Data = Port - 400;
  1220. }
  1221. return STATUS_SUCCESS;
  1222. }
  1223. NTSTATUS
  1224. TestIoWordReadRoutine(
  1225. IN ULONG Port,
  1226. IN UCHAR AccessMode,
  1227. IN OUT PUSHORT Data
  1228. )
  1229. {
  1230. if (AccessMode & EMULATOR_READ_ACCESS) {
  1231. *Data = Port - 200;
  1232. }
  1233. return STATUS_SUCCESS;
  1234. }
  1235. NTSTATUS
  1236. TestIoWordWriteRoutine(
  1237. IN ULONG Port,
  1238. IN UCHAR AccessMode,
  1239. IN OUT PUSHORT Data
  1240. )
  1241. {
  1242. DbgPrint("Word Write routine port # %lx, %x\n",Port,*Data);
  1243. return STATUS_SUCCESS;
  1244. }
  1245. NTSTATUS
  1246. TestIoDwordRoutine(
  1247. IN ULONG Port,
  1248. IN USHORT AccessMode,
  1249. IN OUT PULONG Data
  1250. )
  1251. {
  1252. if (AccessMode & EMULATOR_READ_ACCESS) {
  1253. *Data = Port;
  1254. }
  1255. return STATUS_SUCCESS;
  1256. }
  1257. NTSTATUS
  1258. TestIoStringRoutine(
  1259. IN ULONG Port,
  1260. IN USHORT AccessMode,
  1261. IN OUT PSHORT Data,
  1262. IN ULONG Count
  1263. )
  1264. {
  1265. ULONG i;
  1266. if (AccessMode & EMULATOR_READ_ACCESS) {
  1267. for (i = 0;i < Count ;i++ ) {
  1268. Data[i] = i;
  1269. }
  1270. } else {
  1271. DbgPrint("String Port Called for write port #%lx,",Port);
  1272. for (i = 0;i < Count ;i++ ) {
  1273. DbgPrint("%x\n",Data[i]);
  1274. }
  1275. }
  1276. return STATUS_SUCCESS;
  1277. }
  1278. PROCESS_IO_PORT_HANDLER_INFORMATION IoPortHandler;
  1279. EMULATOR_ACCESS_ENTRY Entry[4];
  1280. BOOLEAN Connect = TRUE, Disconnect = FALSE;
  1281. VOID
  1282. TestIoHandlerStuff(
  1283. VOID
  1284. )
  1285. {
  1286. NTSTATUS Status;
  1287. IoPortHandler.Install = TRUE;
  1288. IoPortHandler.NumEntries = 5L;
  1289. IoPortHandler.EmulatorAccessEntries = Entry;
  1290. Entry[0].BasePort = 0x400;
  1291. Entry[0].NumConsecutivePorts = 0x30;
  1292. Entry[0].AccessType = Uchar;
  1293. Entry[0].AccessMode = EMULATOR_READ_ACCESS | EMULATOR_WRITE_ACCESS;
  1294. Entry[0].StringSupport = FALSE;
  1295. Entry[0].Routine = TestIoByteRoutine;
  1296. Entry[1].BasePort = 0x400;
  1297. Entry[1].NumConsecutivePorts = 0x18;
  1298. Entry[1].AccessType = Ushort;
  1299. Entry[1].AccessMode = EMULATOR_READ_ACCESS | EMULATOR_WRITE_ACCESS;
  1300. Entry[1].StringSupport = FALSE;
  1301. Entry[1].Routine = TestIoWordReadRoutine;
  1302. Entry[2].BasePort = 0x400;
  1303. Entry[2].NumConsecutivePorts = 0xc;
  1304. Entry[2].AccessType = Ulong;
  1305. Entry[2].AccessMode = EMULATOR_READ_ACCESS | EMULATOR_WRITE_ACCESS;
  1306. Entry[2].StringSupport = FALSE;
  1307. Entry[2].Routine = TestIoDwordRoutine;
  1308. Entry[3].BasePort = 0x400;
  1309. Entry[3].NumConsecutivePorts = 0x18;
  1310. Entry[3].AccessType = Ushort;
  1311. Entry[3].AccessMode = EMULATOR_READ_ACCESS | EMULATOR_WRITE_ACCESS;
  1312. Entry[3].StringSupport = TRUE;
  1313. Entry[3].Routine = TestIoStringRoutine;
  1314. if (Connect) {
  1315. Status = ZwSetInformationProcess(
  1316. NtCurrentProcess(),
  1317. ProcessIoPortHandlers,
  1318. &IoPortHandler,
  1319. sizeof(PROCESS_IO_PORT_HANDLER_INFORMATION)
  1320. ) ;
  1321. if (!NT_SUCCESS(Status)) {
  1322. DbgBreakPoint();
  1323. }
  1324. Connect = FALSE;
  1325. }
  1326. IoPortHandler.Install = FALSE;
  1327. if (Disconnect) {
  1328. Status = ZwSetInformationProcess(
  1329. NtCurrentProcess(),
  1330. ProcessIoPortHandlers,
  1331. &IoPortHandler,
  1332. sizeof(PROCESS_IO_PORT_HANDLER_INFORMATION)
  1333. );
  1334. if (!NT_SUCCESS(Status)) {
  1335. DbgBreakPoint();
  1336. }
  1337. Disconnect = FALSE;
  1338. }
  1339. }
  1340. #endif