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.

1311 lines
31 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. dpmiint.c
  5. Abstract:
  6. This file contains the interrupt support for DPMI. Most of this is
  7. for supporting the 486 emulator on risc platforms, but some code
  8. is shared with x86.
  9. Author:
  10. Neil Sandlin (neilsa) 1-Jun-1995
  11. Revision History:
  12. Comments:
  13. DPMI stack switching is accomplished by keeping a "locked pm stack"
  14. count, and when the count is zero, a stack switch occurs. This keeps
  15. track of the situation with recursive interrupts where the client
  16. may switch to its own stack. So, a stack switch to our locked stack
  17. occurs on the first level interrupt, and on subsequent nested interrupts,
  18. only the count is maintained. This is identical to how win31 managed
  19. the stack.
  20. If a client specifies that it is a 32-bit dpmi client, this only affects
  21. the "width" of a stack frame. A 16-bit client gets 16-bit frames, and
  22. a 32 bit client gets 32-bit frames. It is still necessary to check
  23. the size of the stack segment to determine if SP or ESP should be used.
  24. --*/
  25. #include "precomp.h"
  26. #pragma hdrstop
  27. #include <softpc.h>
  28. #include <dpmiint.h>
  29. BOOL
  30. SetProtectedModeInterrupt(
  31. USHORT IntNumber,
  32. USHORT Sel,
  33. ULONG Offset,
  34. USHORT Flags
  35. )
  36. /*++
  37. Routine Description:
  38. This function services the SetProtectedmodeInterrupt bop. It retrieves
  39. the handler information from the Dos application stack, and puts it into
  40. the VdmTib, for use by instruction emulation.
  41. --*/
  42. {
  43. DECLARE_LocalVdmContext;
  44. PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
  45. if (IntNumber >= 256) {
  46. return FALSE;
  47. }
  48. if ((IntNumber >= 8 && IntNumber <= 0xf) ||
  49. (IntNumber >= 0x70 && IntNumber <= 0x7f)) {
  50. //
  51. // Hardware Interrupt
  52. //
  53. Flags |= VDM_INT_INT_GATE;
  54. } else {
  55. //
  56. // Software Interrupt
  57. //
  58. Flags |= VDM_INT_TRAP_GATE;
  59. }
  60. if (Sel != PMReflectorSeg) {
  61. //
  62. // The caller is setting the PM interrupt vector to be something other
  63. // than the dpmi default "end-of-the-chain" PM handler. Now we check
  64. // to see if the interrupt needs to be sent up to PM when it is encountered
  65. // in v86 mode.
  66. //
  67. if ((IntNumber == 0x1b) || //^Break?
  68. (IntNumber == 0x1c) || //Timer Tick?
  69. (IntNumber == 0x23) || //Ctrl-C?
  70. (IntNumber == 0x24) || //Critical Error Handler?
  71. (IntNumber == 0x02) || //Math co-processor exception used by math library routines!
  72. ((IntNumber >= 0x08) && (IntNumber <= 0xf)) || //Hardware?
  73. ((IntNumber >= 0x70) && (IntNumber <= 0x77))) {
  74. // Flag this so that the v86 reflector code will send it to PM
  75. Flags |= VDM_INT_HOOKED;
  76. // Mark it down low so NTIO.SYS can do the right thing
  77. if ( (IntNumber == 0x1c) || (IntNumber == 8) ) {
  78. *(ULONG *)(IntelBase+FIXED_NTVDMSTATE_LINEAR) |= VDM_INTS_HOOKED_IN_PM;
  79. }
  80. }
  81. }
  82. Handlers[IntNumber].Flags = Flags;
  83. Handlers[IntNumber].CsSelector = Sel;
  84. Handlers[IntNumber].Eip = Offset;
  85. DBGTRACE((USHORT)(VDMTR_TYPE_DPMI_SI | IntNumber), Sel, Offset);
  86. #ifdef _X86_
  87. if (IntNumber == 0x21)
  88. {
  89. VDMSET_INT21_HANDLER_DATA ServiceData;
  90. NTSTATUS Status;
  91. ServiceData.Selector = Handlers[IntNumber].CsSelector;
  92. ServiceData.Offset = Handlers[IntNumber].Eip;
  93. ServiceData.Gate32 = Handlers[IntNumber].Flags & VDM_INT_32;
  94. Status = NtVdmControl(VdmSetInt21Handler, &ServiceData);
  95. #if DBG
  96. if (!NT_SUCCESS(Status)) {
  97. OutputDebugString("DPMI32: Error Setting Int21handler\n");
  98. }
  99. #endif
  100. }
  101. #endif //_X86_
  102. return TRUE;
  103. }
  104. VOID
  105. DpmiInitIDT(
  106. VOID
  107. )
  108. /*++
  109. Routine Description:
  110. This function initializes the state of the IDT. It takes as input the
  111. IDT set up by DOSX, updates the IDT's access bytes, and sets the DPMI32
  112. interrupt handlers by calling SetProtectedModeInterrupt.
  113. --*/
  114. {
  115. DECLARE_LocalVdmContext;
  116. USHORT IntNumber;
  117. USHORT Flags = getBX();
  118. Idt = (PVOID)VdmMapFlat(getAX(), 0, getMODE());
  119. for (IntNumber = 0; IntNumber<256; IntNumber++) {
  120. SetProtectedModeInterrupt(IntNumber,
  121. Idt[IntNumber].Selector,
  122. (((ULONG)Idt[IntNumber].OffsetHi)<<16) +
  123. Idt[IntNumber].OffsetLow,
  124. Flags);
  125. }
  126. }
  127. BOOL
  128. SetFaultHandler(
  129. USHORT IntNumber,
  130. USHORT Sel,
  131. ULONG Offset
  132. )
  133. /*++
  134. Routine Description:
  135. This function services the SetFaultHandler bop. It retrieves
  136. the handler information from the Dos application stack, and puts it into
  137. the VdmTib, for use by instruction emulation.
  138. --*/
  139. {
  140. DECLARE_LocalVdmContext;
  141. PVDM_FAULTHANDLER Handlers = DpmiFaultHandlers;
  142. if (IntNumber >= 32) {
  143. return FALSE;
  144. }
  145. Handlers[IntNumber].Flags = VDM_INT_INT_GATE;
  146. Handlers[IntNumber].CsSelector = Sel;
  147. Handlers[IntNumber].Eip = Offset;
  148. Handlers[IntNumber].SsSelector = 0; //BUGBUG These are obselete
  149. Handlers[IntNumber].Esp = 0; //BUGBUG These are obselete
  150. DBGTRACE((USHORT)(VDMTR_TYPE_DPMI_SF | IntNumber),
  151. Handlers[IntNumber].CsSelector,
  152. Handlers[IntNumber].Eip);
  153. return TRUE;
  154. }
  155. VOID
  156. DpmiInitExceptionHandlers(
  157. VOID
  158. )
  159. {
  160. DECLARE_LocalVdmContext;
  161. USHORT OffsetIncr = getAX();
  162. USHORT IntCount = getBX();
  163. USHORT Selector = getCX();
  164. ULONG Offset = (ULONG) getDX();
  165. USHORT IntNumber;
  166. for (IntNumber = 0; IntNumber < 32; IntNumber++) {
  167. SetFaultHandler(IntNumber, Selector, Offset);
  168. Offset += OffsetIncr;
  169. }
  170. }
  171. VOID
  172. DpmiUnhandledExceptionHandler(
  173. VOID
  174. )
  175. /*++
  176. Routine Description:
  177. This function gets control when a PM fault occurs that isn't handled
  178. by an installed handler. The body of this function emulates Win31
  179. DPMI behavior, where a fault that is reflected to the end of the
  180. PM fault handler chain is then reflected to the PM *interrupt*
  181. chain.
  182. Arguments:
  183. client SS:(E)SP points to dpmi fault stack frame
  184. --*/
  185. {
  186. DECLARE_LocalVdmContext;
  187. PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
  188. USHORT SegSs, SegCs;
  189. UCHAR XNumber;
  190. PCHAR VdmStackPointer;
  191. PCHAR VdmCodePointer;
  192. USHORT FaultingCS;
  193. ULONG FaultingEip;
  194. SegSs = getSS();
  195. VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
  196. if (SEGMENT_IS_BIG(SegSs)) {
  197. VdmStackPointer += getESP();
  198. } else {
  199. VdmStackPointer += getSP();
  200. }
  201. SegCs = getCS();
  202. VdmCodePointer = Sim32GetVDMPointer(SegCs<<16, 1, TRUE);
  203. if (SEGMENT_IS_BIG(SegCs)) {
  204. VdmCodePointer += getEIP();
  205. } else {
  206. VdmCodePointer += getIP();
  207. }
  208. XNumber = *(VdmCodePointer);
  209. if ((XNumber > 7) || (XNumber == 6)) {
  210. DpmiFatalExceptionHandler(XNumber, VdmStackPointer);
  211. return;
  212. }
  213. if (Frame32) {
  214. PCHAR VdmNewStackPointer;
  215. ULONG FrameSS, FrameSP, FrameCS, FrameIP, FrameFlags;
  216. //
  217. // Build an iret frame on the faulting stack
  218. //
  219. FrameSS = *(PDWORD16) (VdmStackPointer+28);
  220. FrameSP = *(PDWORD16) (VdmStackPointer+24) - 12;
  221. *(PDWORD16) (VdmStackPointer+24) = FrameSP;
  222. VdmNewStackPointer = Sim32GetVDMPointer((ULONG)(FrameSS << 16), 1, TRUE);
  223. VdmNewStackPointer += FrameSP;
  224. FrameIP = *(PDWORD16) (VdmStackPointer+12);
  225. *(PDWORD16) (VdmStackPointer+12) = Handlers[XNumber].Eip;
  226. *(PDWORD16) (VdmNewStackPointer) = FrameIP;
  227. FrameCS = *(PDWORD16) (VdmStackPointer+16);
  228. *(PDWORD16) (VdmStackPointer+16) = (ULONG) Handlers[XNumber].CsSelector;
  229. *(PDWORD16) (VdmNewStackPointer+4) = FrameCS;
  230. FrameFlags = *(PDWORD16) (VdmStackPointer+20);
  231. *(PDWORD16) (VdmNewStackPointer+4) = FrameFlags;
  232. FrameFlags &= ~(EFLAGS_IF_MASK | EFLAGS_TF_MASK);
  233. *(PDWORD16) (VdmStackPointer+20) = FrameFlags;
  234. //
  235. // Simulate a dpmi fault handler retf
  236. //
  237. setCS((USHORT)*(PDWORD16)(VdmStackPointer+4));
  238. setEIP(*(PDWORD16)(VdmStackPointer));
  239. setESP(getESP() + 8);
  240. } else {
  241. USHORT FrameSS, FrameSP, FrameCS, FrameIP, FrameFlags;
  242. FrameSS = *(PWORD16) (VdmStackPointer+14);
  243. FrameCS = *(PWORD16) (VdmStackPointer+8);
  244. FrameFlags = *(PWORD16) (VdmStackPointer+10);
  245. if (!SEGMENT_IS_BIG(FrameSS) && !SEGMENT_IS_BIG(FrameCS)) {
  246. PCHAR VdmNewStackPointer;
  247. //
  248. // Build an iret frame on the faulting stack
  249. //
  250. FrameSP = *(PWORD16) (VdmStackPointer+12) - 6;
  251. *(PWORD16) (VdmStackPointer+12) = FrameSP;
  252. VdmNewStackPointer = Sim32GetVDMPointer((ULONG)(FrameSS << 16)+FrameSP, 1, TRUE);
  253. FrameIP = *(PWORD16) (VdmStackPointer+6);
  254. *(PWORD16) (VdmStackPointer+6) = (WORD) Handlers[XNumber].Eip;
  255. *(PWORD16) (VdmNewStackPointer) = FrameIP;
  256. *(PWORD16) (VdmStackPointer+8) = Handlers[XNumber].CsSelector;
  257. *(PWORD16) (VdmNewStackPointer+2) = FrameCS;
  258. *(PWORD16) (VdmNewStackPointer+4) = FrameFlags;
  259. FrameFlags &= ~(EFLAGS_IF_MASK | EFLAGS_TF_MASK);
  260. *(PWORD16) (VdmStackPointer+10) = FrameFlags;
  261. //
  262. // Simulate a dpmi fault handler retf
  263. //
  264. setCS(*(PWORD16)(VdmStackPointer+2));
  265. setEIP((DWORD)*(PWORD16)(VdmStackPointer));
  266. setSP(getSP() + 4);
  267. } else {
  268. //
  269. // Build an iret frame on the locked DPMI stack
  270. //
  271. FrameCS = *(PWORD16) (VdmStackPointer+2);
  272. FrameIP = *(PWORD16) (VdmStackPointer);
  273. FrameFlags &= ~EFLAGS_IF_MASK;
  274. setSP(getSP() - 2);
  275. *(PWORD16)(VdmStackPointer-2) = FrameIP;
  276. *(PWORD16)(VdmStackPointer) = FrameCS;
  277. *(PWORD16)(VdmStackPointer+2) = FrameFlags;
  278. setCS(Handlers[XNumber].CsSelector);
  279. setEIP((DWORD)LOWORD(Handlers[XNumber].Eip));
  280. setSTATUS((WORD) FrameFlags & ~EFLAGS_TF_MASK);
  281. }
  282. }
  283. }
  284. VOID
  285. DpmiFatalExceptionHandler(
  286. UCHAR XNumber,
  287. PCHAR VdmStackPointer
  288. )
  289. /*++
  290. Routine Description:
  291. This function gets control when a PM fault 6, 8-1f occurs that isn't
  292. handled by an installed handler. It pops up an error dialog for the
  293. user.
  294. Arguments:
  295. XNumber - exception number (0-1fh)
  296. VdmStackPointer - flat pointer to stack frame
  297. --*/
  298. {
  299. DECLARE_LocalVdmContext;
  300. char szBuffer[255];
  301. USHORT FaultingCS;
  302. ULONG FaultingEip;
  303. if (Frame32) {
  304. FaultingCS = (USHORT)*(PDWORD16)(VdmStackPointer+16);
  305. FaultingEip = *(PDWORD16)(VdmStackPointer+12);
  306. } else {
  307. FaultingCS = *(PWORD16)(VdmStackPointer+8);
  308. FaultingEip = (ULONG)*(PWORD16)(VdmStackPointer+6);
  309. }
  310. wsprintf(szBuffer, "X#=%.02X, CS=%.04X IP=%.08X",
  311. XNumber, FaultingCS, FaultingEip);
  312. RcErrorDialogBox(EG_BAD_FAULT, szBuffer, NULL);
  313. //
  314. // Need to try to ignore it. Since we are on a dpmi exception frame
  315. // we can just simulate a retf.
  316. //
  317. if (Frame32) {
  318. setCS((USHORT)*(PDWORD16)(VdmStackPointer+4));
  319. setEIP(*(PDWORD16)(VdmStackPointer));
  320. setESP(getESP() + 8);
  321. } else {
  322. setCS(*(PWORD16)(VdmStackPointer+2));
  323. setEIP((DWORD)*(PWORD16)(VdmStackPointer));
  324. setSP(getSP() + 4);
  325. }
  326. }
  327. VOID
  328. DpmiInitPmStackInfo(
  329. VOID
  330. )
  331. /*++
  332. Routine Description:
  333. This routine is called via BOP by DOSX to initialize values related
  334. to stack handling.
  335. Arguments:
  336. Client ES = selector of locked PM stack
  337. Return Value:
  338. None
  339. Notes:
  340. The offset of the locked pm stack is hard-coded to 0x1000, per dpmi
  341. and win31.
  342. --*/
  343. {
  344. DECLARE_LocalVdmContext;
  345. LockedPMStackSel = getES();
  346. LockedPMStackCount = 0;
  347. #ifdef _X86_
  348. ((PVDM_TIB)NtCurrentTeb()->Vdm)->DpmiInfo.Flags = CurrentAppFlags;
  349. #endif
  350. }
  351. BOOL
  352. DpmiSwIntHandler(
  353. ULONG IntNumber
  354. )
  355. /*++
  356. Routine Description:
  357. This routine is called by the emulator to dispatch a SW interrupt.
  358. Arguments:
  359. IntNumber - interrupt vector number
  360. Return Value:
  361. TRUE if the interrupt was dispatched, FALSE otherwise
  362. --*/
  363. {
  364. DECLARE_LocalVdmContext;
  365. PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
  366. PUCHAR VdmStackPointer;
  367. ULONG SaveEFLAGS;
  368. ULONG NewSP;
  369. DBGTRACE(VDMTR_TYPE_DPMI | DPMI_SW_INT, (USHORT)IntNumber, 0);
  370. //
  371. // If we're here via breakpoint, see if it belongs to NTVDM debug code.
  372. //
  373. if ((IntNumber == 3) &&
  374. (*(ULONG *)(IntelBase+FIXED_NTVDMSTATE_LINEAR) & VDM_BREAK_DEBUGGER) &&
  375. DbgBPInt()) {
  376. return TRUE;
  377. }
  378. if (!(getMSW() & MSW_PE)) {
  379. EmulateV86Int((UCHAR)IntNumber);
  380. } else {
  381. PUCHAR VdmStackPointer;
  382. // Protect mode
  383. SaveEFLAGS = getEFLAGS();
  384. //BUGBUG turn off task bits
  385. SaveEFLAGS &= ~EFLAGS_NT_MASK;
  386. setEFLAGS(SaveEFLAGS & ~EFLAGS_TF_MASK);
  387. if (!SEGMENT_IS_PRESENT(Handlers[IntNumber].CsSelector)) {
  388. return FALSE;
  389. }
  390. if (!BuildStackFrame(3, &VdmStackPointer, &NewSP)) {
  391. return FALSE;
  392. }
  393. if (Frame32) {
  394. *(PDWORD16)(VdmStackPointer-4) = SaveEFLAGS;
  395. *(PDWORD16)(VdmStackPointer-8) = getCS();
  396. *(PDWORD16)(VdmStackPointer-12) = getEIP();
  397. setEIP(Handlers[IntNumber].Eip);
  398. setESP(NewSP);
  399. } else {
  400. *(PWORD16)(VdmStackPointer-2) = (WORD) SaveEFLAGS;
  401. *(PWORD16)(VdmStackPointer-4) = (WORD) getCS();
  402. *(PWORD16)(VdmStackPointer-6) = (WORD) getEIP();
  403. setEIP((DWORD)LOWORD(Handlers[IntNumber].Eip));
  404. setSP((WORD)NewSP);
  405. }
  406. setCS(Handlers[IntNumber].CsSelector);
  407. #if DBG
  408. if (Handlers[IntNumber].CsSelector != getCS()) {
  409. char szFormat[] = "NTVDM Dpmi Error! Can't set CS to %.4X\n";
  410. char szMsg[sizeof(szFormat)+30];
  411. wsprintf(szMsg, szFormat, Handlers[IntNumber].CsSelector);
  412. OutputDebugString(szMsg);
  413. }
  414. #endif
  415. }
  416. DBGTRACE(VDMTR_TYPE_DPMI | DPMI_DISPATCH_INT, (USHORT)IntNumber, 0);
  417. return TRUE;
  418. }
  419. BOOL
  420. DpmiHwIntHandler(
  421. ULONG IntNumber
  422. )
  423. /*++
  424. Routine Description:
  425. This routine is called by the emulator to dispatch a HW interrupt.
  426. Arguments:
  427. IntNumber - interrupt vector number
  428. Return Value:
  429. TRUE if the interrupt was dispatched, FALSE otherwise
  430. --*/
  431. {
  432. DECLARE_LocalVdmContext;
  433. PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
  434. PUCHAR VdmStackPointer;
  435. ULONG SaveEFLAGS;
  436. ULONG NewSP;
  437. DBGTRACE(VDMTR_TYPE_DPMI | DPMI_HW_INT, (USHORT)IntNumber, 0);
  438. if (!(getMSW() & MSW_PE)) {
  439. EmulateV86Int((UCHAR)IntNumber);
  440. } else {
  441. PUCHAR VdmStackPointer;
  442. SaveEFLAGS = getEFLAGS();
  443. //BUGBUG turn off task bits
  444. SaveEFLAGS &= ~0x4000;
  445. setEFLAGS(SaveEFLAGS & ~(EFLAGS_IF_MASK | EFLAGS_TF_MASK));
  446. BeginUseLockedPMStack();
  447. if (!BuildStackFrame(6, &VdmStackPointer, &NewSP)) {
  448. EndUseLockedPMStack();
  449. return FALSE;
  450. }
  451. if (Frame32) {
  452. *(PDWORD16)(VdmStackPointer-4) = SaveEFLAGS;
  453. *(PDWORD16)(VdmStackPointer-8) = getCS();
  454. *(PDWORD16)(VdmStackPointer-12) = getEIP();
  455. *(PDWORD16)(VdmStackPointer-16) = getEFLAGS();
  456. *(PDWORD16)(VdmStackPointer-20) = (DWORD)HIWORD(DosxIntHandlerIretd);
  457. *(PDWORD16)(VdmStackPointer-24) = (DWORD)LOWORD(DosxIntHandlerIretd);
  458. setEIP(Handlers[IntNumber].Eip);
  459. setESP(NewSP);
  460. } else {
  461. *(PWORD16)(VdmStackPointer-2) = (WORD)SaveEFLAGS;
  462. *(PWORD16)(VdmStackPointer-4) = (WORD)getCS();
  463. *(PWORD16)(VdmStackPointer-6) = (WORD)getIP();
  464. *(PWORD16)(VdmStackPointer-8) = (WORD)getEFLAGS();
  465. *(PWORD16)(VdmStackPointer-10) = HIWORD(DosxIntHandlerIret);
  466. *(PWORD16)(VdmStackPointer-12) = LOWORD(DosxIntHandlerIret);
  467. setEIP((DWORD)LOWORD(Handlers[IntNumber].Eip));
  468. setSP((WORD)NewSP);
  469. }
  470. setCS(Handlers[IntNumber].CsSelector);
  471. }
  472. DBGTRACE(VDMTR_TYPE_DPMI | DPMI_DISPATCH_INT, (USHORT)IntNumber, 0);
  473. return TRUE;
  474. }
  475. VOID
  476. DpmiIntHandlerIret16(
  477. VOID
  478. )
  479. /*++
  480. Routine Description:
  481. This routine is an IRET hook called via a BOP in dosx. It is called
  482. at the end of a 16-bit HW or SW interrupt. The main reason we want
  483. to come in here is to maintain the DPMI stack, and know when to restore
  484. the original values when we pop back out to level zero.
  485. --*/
  486. {
  487. DECLARE_LocalVdmContext;
  488. PUCHAR VdmStackPointer;
  489. ULONG NewSP;
  490. USHORT SegSs;
  491. BOOL bSsBig;
  492. SegSs = getSS();
  493. VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
  494. if (bSsBig = SEGMENT_IS_BIG(SegSs)) {
  495. VdmStackPointer += getESP();
  496. } else {
  497. VdmStackPointer += getSP();
  498. }
  499. //
  500. // Fast iret (without executing final 16-bit iret)
  501. //
  502. #ifdef _X86_
  503. setCS(*(PWORD16)(VdmStackPointer+2));
  504. setEFLAGS((getEFLAGS()&0xffff0000) | *(PWORD16)(VdmStackPointer+4));
  505. //
  506. // if EndUseLockedPMStack fails, then we need to restore EIP and pop
  507. // the stack frame
  508. //
  509. if (!EndUseLockedPMStack()) {
  510. setEIP((DWORD)*(PWORD16)(VdmStackPointer));
  511. //
  512. // Pop iret frame off the stack
  513. //
  514. if (bSsBig) {
  515. setESP(getESP()+6);
  516. } else {
  517. setSP(getSP()+6);
  518. }
  519. }
  520. //
  521. // Slow iret (with executing final 16-bit iret)
  522. //
  523. #else
  524. if (EndUseLockedPMStack()) {
  525. ULONG NewEIP, NewEFLAGS, NewCS;
  526. NewEIP = getEIP();
  527. NewCS = (ULONG) *(PWORD16)(VdmStackPointer+2);
  528. NewEFLAGS = (getEFLAGS()&0xffff0000) | *(PWORD16)(VdmStackPointer+4);
  529. //
  530. // Since EndUseLockedPMStack() has restored all of EIP, and we may be
  531. // returning to a 32-bit code segment, build a 32-bit iret frame
  532. // even if this is a 16-bit client. That way, EIP will be restored
  533. // correctly.
  534. // Pass 6 to BuildStackFrame since 6 words = 3 dwords
  535. //
  536. if (!BuildStackFrame(6, &VdmStackPointer, &NewSP)) {
  537. #if DBG
  538. OutputDebugString("NTVDM: Dpmi encountered a stack fault!\n");
  539. #endif
  540. DpmiFaultHandler(STACK_FAULT, 0);
  541. return;
  542. }
  543. //
  544. // SS has changed, so we need to check LDT again
  545. //
  546. if (SEGMENT_IS_BIG(getSS())) {
  547. setESP(NewSP);
  548. } else {
  549. setSP((WORD)NewSP);
  550. }
  551. *(PDWORD16)(VdmStackPointer-4) = NewEFLAGS;
  552. *(PDWORD16)(VdmStackPointer-8) = NewCS;
  553. *(PDWORD16)(VdmStackPointer-12) = NewEIP;
  554. setCS(HIWORD(DosxIretd));
  555. setEIP((ULONG)LOWORD(DosxIretd));
  556. } else {
  557. // still on locked stack, just do a real iret (16-bit frame)
  558. setCS(HIWORD(DosxIret));
  559. setEIP((ULONG)LOWORD(DosxIret));
  560. }
  561. #endif // _X86_
  562. }
  563. VOID
  564. DpmiIntHandlerIret32(
  565. VOID
  566. )
  567. /*++
  568. Routine Description:
  569. This routine is an IRET hook called via a BOP in dosx. It is called
  570. at the end of a 32-bit HW or SW interrupt. The main reason we want
  571. to come in here is to maintain the DPMI stack, and know when to restore
  572. the original values when we pop back out to level zero.
  573. --*/
  574. {
  575. DECLARE_LocalVdmContext;
  576. PUCHAR VdmStackPointer;
  577. ULONG NewSP;
  578. USHORT SegSs;
  579. BOOL bSsBig;
  580. SegSs = getSS();
  581. VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
  582. if (bSsBig = SEGMENT_IS_BIG(SegSs)) {
  583. VdmStackPointer += getESP();
  584. } else {
  585. VdmStackPointer += getSP();
  586. }
  587. #ifdef _X86_
  588. setCS(*(PDWORD16)(VdmStackPointer+4));
  589. setEFLAGS(*(PDWORD16)(VdmStackPointer+8));
  590. //
  591. // if EndUseLockedPMStack succeeds, then we don't need to restore EIP
  592. //
  593. if (!EndUseLockedPMStack()) {
  594. setEIP(*(PDWORD16)(VdmStackPointer));
  595. //
  596. // Pop iret frame off the stack
  597. //
  598. if (bSsBig) {
  599. setESP(getESP()+12);
  600. } else {
  601. setSP(getSP()+12);
  602. }
  603. }
  604. #else
  605. if (EndUseLockedPMStack()) {
  606. ULONG NewEIP, NewCS, NewEFLAGS;
  607. NewEIP = getEIP();
  608. NewCS = *(PDWORD16)(VdmStackPointer+4);
  609. NewEFLAGS = *(PDWORD16)(VdmStackPointer+8);
  610. if (!BuildStackFrame(3, &VdmStackPointer, &NewSP)) {
  611. #if DBG
  612. OutputDebugString("NTVDM: Dpmi encountered a stack fault!\n");
  613. #endif
  614. DpmiFaultHandler(STACK_FAULT, 0);
  615. return;
  616. }
  617. //
  618. // SS has changed, so we need to check LDT again
  619. //
  620. if (SEGMENT_IS_BIG(getSS())) {
  621. setESP(NewSP);
  622. } else {
  623. setSP((WORD)NewSP);
  624. }
  625. *(PDWORD16)(VdmStackPointer-4) = NewEFLAGS;
  626. *(PDWORD16)(VdmStackPointer-8) = NewCS;
  627. *(PDWORD16)(VdmStackPointer-12) = NewEIP;
  628. }
  629. setCS(HIWORD(DosxIretd));
  630. setEIP((ULONG)LOWORD(DosxIretd));
  631. #endif // _X86_
  632. }
  633. #ifndef _X86_
  634. BOOL
  635. DpmiFaultHandler(
  636. ULONG IntNumber,
  637. ULONG ErrorCode
  638. )
  639. /*++
  640. Routine Description:
  641. This routine is called by the emulator when an exception occurs.
  642. Arguments:
  643. IntNumber - exception number (0-1f)
  644. ErrorCode - exception error code to be placed on the stack
  645. Return Value:
  646. TRUE if the interrupt was dispatched, FALSE otherwise
  647. --*/
  648. {
  649. DECLARE_LocalVdmContext;
  650. PVDM_FAULTHANDLER Handlers = DpmiFaultHandlers;
  651. PUCHAR VdmStackPointer;
  652. ULONG SaveSS, SaveESP, SaveEFLAGS, SaveCS, SaveEIP;
  653. ULONG StackOffset;
  654. ULONG NewSP;
  655. DBGTRACE(VDMTR_TYPE_DPMI | DPMI_FAULT, (USHORT)IntNumber, ErrorCode);
  656. if ((IntNumber == 1) &&
  657. (*(ULONG *)(IntelBase+FIXED_NTVDMSTATE_LINEAR) & VDM_BREAK_DEBUGGER) &&
  658. DbgTraceInt()) {
  659. return TRUE;
  660. }
  661. if (DbgFault(IntNumber)) { // try the debugger
  662. //
  663. // exception handled via user input
  664. //
  665. return TRUE;
  666. }
  667. if (!(getMSW() & MSW_PE)) {
  668. EmulateV86Int((UCHAR)IntNumber);
  669. return TRUE;
  670. }
  671. SaveSS = getSS();
  672. SaveESP = getESP();
  673. SaveEFLAGS = getEFLAGS();
  674. SaveEIP = getEIP();
  675. SaveCS = getCS();
  676. setEFLAGS(SaveEFLAGS & ~(EFLAGS_IF_MASK | EFLAGS_TF_MASK));
  677. if ((IntNumber == 13) || (IntNumber == 6)) {
  678. if (DpmiEmulateInstruction()) {
  679. return TRUE;
  680. }
  681. }
  682. if (!SEGMENT_IS_PRESENT(Handlers[IntNumber].CsSelector)) {
  683. return FALSE;
  684. }
  685. //
  686. // switch stacks
  687. //
  688. BeginUseLockedPMStack();
  689. //
  690. // Win31 has an undocumented feature of creating a 32byte area on the
  691. // stack. Krnl386 sticks stuff in there, so we emulate the behavior here.
  692. //
  693. setESP(getESP()-0x20);
  694. //
  695. // allocate space on new stack
  696. //
  697. if (!BuildStackFrame(8, &VdmStackPointer, &NewSP)) {
  698. //BUGBUG Check for double fault
  699. EndUseLockedPMStack();
  700. return FALSE;
  701. }
  702. if (Frame32) {
  703. *(PDWORD16)(VdmStackPointer-4) = SaveSS;
  704. *(PDWORD16)(VdmStackPointer-8) = SaveESP;
  705. *(PDWORD16)(VdmStackPointer-12) = SaveEFLAGS;
  706. *(PDWORD16)(VdmStackPointer-16) = SaveCS;
  707. *(PDWORD16)(VdmStackPointer-20) = SaveEIP;
  708. *(PDWORD16)(VdmStackPointer-24) = ErrorCode;
  709. *(PDWORD16)(VdmStackPointer-28) = (ULONG) HIWORD(DosxFaultHandlerIretd);
  710. *(PDWORD16)(VdmStackPointer-32) = (ULONG) LOWORD(DosxFaultHandlerIretd);
  711. setEIP(Handlers[IntNumber].Eip);
  712. setESP(NewSP);
  713. } else {
  714. *(PWORD16)(VdmStackPointer-2) = (WORD) SaveSS;
  715. *(PWORD16)(VdmStackPointer-4) = (WORD) SaveESP;
  716. *(PWORD16)(VdmStackPointer-6) = (WORD) SaveEFLAGS;
  717. *(PWORD16)(VdmStackPointer-8) = (WORD) SaveCS;
  718. *(PWORD16)(VdmStackPointer-10) = (WORD) SaveEIP;
  719. *(PWORD16)(VdmStackPointer-12) = (WORD) ErrorCode;
  720. *(PDWORD16)(VdmStackPointer-16) = DosxFaultHandlerIret;
  721. setEIP(LOWORD(Handlers[IntNumber].Eip));
  722. setSP((WORD)NewSP);
  723. }
  724. setCS(Handlers[IntNumber].CsSelector);
  725. #if DBG
  726. if (Handlers[IntNumber].CsSelector != getCS()) {
  727. char szFormat[] = "NTVDM Dpmi Error! Can't set CS to %.4X\n";
  728. char szMsg[sizeof(szFormat)+30];
  729. wsprintf(szMsg, szFormat, Handlers[IntNumber].CsSelector);
  730. OutputDebugString(szMsg);
  731. }
  732. #endif
  733. DBGTRACE(VDMTR_TYPE_DPMI | DPMI_DISPATCH_FAULT, (USHORT)IntNumber, 0);
  734. return TRUE;
  735. }
  736. #endif // _X86_
  737. VOID
  738. DpmiFaultHandlerIret16(
  739. VOID
  740. )
  741. /*++
  742. Routine Description:
  743. This routine is an IRET hook called via a BOP in dosx. It is called
  744. at the end of the execution of a 16-bit fault handler.
  745. --*/
  746. {
  747. DECLARE_LocalVdmContext;
  748. PUCHAR VdmStackPointer;
  749. USHORT SegSs;
  750. SegSs = getSS();
  751. VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
  752. if (SEGMENT_IS_BIG(SegSs)) {
  753. VdmStackPointer += getESP();
  754. } else {
  755. VdmStackPointer += getSP();
  756. }
  757. EndUseLockedPMStack();
  758. setEIP((DWORD)*(PWORD16)(VdmStackPointer+2));
  759. setCS(*(PWORD16)(VdmStackPointer+4));
  760. setSTATUS(*(PWORD16)(VdmStackPointer+6));
  761. setSP(*(PWORD16)(VdmStackPointer+8));
  762. setSS(*(PWORD16)(VdmStackPointer+10));
  763. }
  764. VOID
  765. DpmiFaultHandlerIret32(
  766. VOID
  767. )
  768. /*++
  769. Routine Description:
  770. This routine is an IRET hook called via a BOP in dosx. It is called
  771. at the end of the execution of a 32-bit fault handler.
  772. --*/
  773. {
  774. DECLARE_LocalVdmContext;
  775. PUCHAR VdmStackPointer;
  776. USHORT SegSs;
  777. SegSs = getSS();
  778. VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
  779. if (SEGMENT_IS_BIG(SegSs)) {
  780. VdmStackPointer += getESP();
  781. } else {
  782. VdmStackPointer += getSP();
  783. }
  784. EndUseLockedPMStack();
  785. setEIP(*(PDWORD16)(VdmStackPointer+4));
  786. setCS((USHORT)*(PDWORD16)(VdmStackPointer+8));
  787. setEFLAGS(*(PDWORD16)(VdmStackPointer+12));
  788. setESP(*(PDWORD16)(VdmStackPointer+16));
  789. setSS((USHORT)*(PWORD16)(VdmStackPointer+20));
  790. }
  791. VOID
  792. DpmiHungAppIretAndExit(
  793. VOID
  794. )
  795. /*++
  796. Routine Description:
  797. This routine is called via BOP during hung app processing. The
  798. Keyboard driver calls this in the context of a hw interrupt in
  799. order to terminate the app. We need to "unwind" the current
  800. interrupt, and transfer control to code which will execute
  801. a DOS exit.
  802. --*/
  803. {
  804. DECLARE_LocalVdmContext;
  805. EndUseLockedPMStack();
  806. setCS(HIWORD(DosxHungAppExit));
  807. setIP(LOWORD(DosxHungAppExit));
  808. }
  809. BOOL
  810. DispatchPMInt(
  811. UCHAR IntNumber
  812. )
  813. /*++
  814. Routine Description:
  815. This routine is called at the end of a PM int chain. It is provided
  816. for compatibility to win31/win95. On win31/win95, VMM and VxD's have
  817. the opportunity to perform some functionality at the point where
  818. the dpmi host is about to switch the machine to v86 mode to continue
  819. the interrupt chain. Sometimes, the function is totally handled by
  820. a hook at this point.
  821. This routine provides a framework for this mechanism to allow the
  822. emulation of this behavior.
  823. Arguments:
  824. IntNumber - the interrupt# that is about to be reflected to v86 mode
  825. Return Value:
  826. TRUE if the interrupt was handled and control can return to the app
  827. FALSE otherwise, continue the reflection to v86 mode.
  828. --*/
  829. {
  830. BOOL bHandled;
  831. switch(IntNumber) {
  832. case 0x2f:
  833. bHandled = PMInt2fHandler();
  834. break;
  835. default:
  836. bHandled = FALSE;
  837. }
  838. if (bHandled) {
  839. SimulateIret(RESTORE_FLAGS);
  840. }
  841. return bHandled;
  842. }
  843. BOOL
  844. CheckEIP(
  845. ULONG Increment
  846. )
  847. /*++
  848. Routine Description:
  849. This routine does a limit check on EIP.
  850. Arguments:
  851. None
  852. Return Value:
  853. TRUE if EIP is ok, FALSE otherwise
  854. --*/
  855. {
  856. //BUGBUG NEED TO RETURN FALSE HERE IF EIP WOULD BE OFF THE END OF SEGMENT
  857. return TRUE;
  858. }
  859. #ifndef _X86_
  860. BOOL
  861. DpmiEmulateInstruction(
  862. VOID
  863. )
  864. /*++
  865. Routine Description:
  866. This routine checks to see if the instruction which caused the
  867. fault really needs to be emulated. For example, the MS C compiler (v7.00)
  868. uses instructions to manipulate the FP flags in CR0. The compiler
  869. expects them to just work as they do on win31, which also emulates them.
  870. Arguments:
  871. None
  872. Return Value:
  873. TRUE if the instruction was emulated, FALSE otherwise
  874. --*/
  875. {
  876. DECLARE_LocalVdmContext;
  877. PUCHAR pCode;
  878. UCHAR Opcode;
  879. ULONG SegCS;
  880. BOOL bReturn = FALSE;
  881. SegCS = getCS();
  882. pCode = Sim32GetVDMPointer(SegCS<<16, 1, TRUE);
  883. if (Ldt[(SegCS & ~0x7)/sizeof(LDT_ENTRY)].HighWord.Bits.Default_Big) {
  884. pCode += getEIP();
  885. } else {
  886. pCode += getIP();
  887. }
  888. Opcode = *pCode++;
  889. switch (Opcode) {
  890. case 0xf:
  891. if (!CheckEIP(1)) {
  892. break;
  893. }
  894. bReturn = DpmiOp0f(pCode);
  895. break;
  896. case 0x8e:
  897. //
  898. // This is WIN31 compatibility. If we are trying to dispatch
  899. // the client, and we get a fault loading the segment registers,
  900. // then zero them out.
  901. // BUGBUG currently only looking for FS, GS
  902. //
  903. if (!CheckEIP(2)) {
  904. break;
  905. }
  906. //
  907. // Look for code in dxutil.asm EnterProtectedMode
  908. //
  909. if ((SegCS == DosxRmCodeSelector) &&
  910. ((*pCode == 0xe0) || // mov fs, ax
  911. (*pCode == 0xe8)) // mov gs, ax
  912. ) {
  913. setEIP(getEIP()+2);
  914. bReturn = TRUE;
  915. }
  916. break;
  917. }
  918. DBGTRACE(VDMTR_TYPE_DPMI | DPMI_OP_EMULATION, Opcode, (ULONG) bReturn);
  919. return bReturn;
  920. }
  921. #define MI_GET_CRx_OPCODE 0x20
  922. #define MI_SET_CRx_OPCODE 0x22
  923. #define MI_MODMASK 0xC0
  924. #define MI_MODMOVSPEC 0xC0
  925. #define MI_REGMASK 0x38
  926. #define MI_RMMASK 0x7
  927. BOOL
  928. DpmiOp0f(
  929. PUCHAR pCode
  930. )
  931. /*++
  932. Routine Description:
  933. This routine emulates instructions that have 0x0F as the first byte.
  934. Arguments:
  935. None
  936. Return Value:
  937. TRUE if the instruction was emulated, FALSE otherwise
  938. --*/
  939. {
  940. DECLARE_LocalVdmContext;
  941. ULONG Value;
  942. switch (*pCode++) {
  943. case MI_GET_CRx_OPCODE:
  944. if (!CheckEIP(2)) {
  945. break;
  946. }
  947. if ((*pCode & MI_MODMASK) != MI_MODMOVSPEC) {
  948. break;
  949. }
  950. if (*pCode & MI_REGMASK) {
  951. Value = 0; // not CR0
  952. } else {
  953. Value = getCR0();
  954. }
  955. SetRegisterByIndex[*pCode & MI_RMMASK](Value);
  956. setEIP(getEIP()+3);
  957. return TRUE;
  958. case MI_SET_CRx_OPCODE:
  959. if (!CheckEIP(2)) {
  960. break;
  961. }
  962. if ((*pCode & MI_MODMASK) != MI_MODMOVSPEC) {
  963. break;
  964. }
  965. if (*pCode & MI_REGMASK) {
  966. break; // not CR0
  967. }
  968. setCR0(GetRegisterByIndex[*pCode & MI_RMMASK]());
  969. setEIP(getEIP()+3);
  970. return TRUE;
  971. }
  972. return FALSE;
  973. }
  974. #endif