Source code of Windows XP (NT5)
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.

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