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.

1299 lines
24 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation. All Rights Reserved.
  3. Module Name:
  4. rtexcept.c
  5. Abstract:
  6. This module contains the realtime executive exception handlers. It also
  7. has the code for setting up the 2 realtime executive IDTs: one for the
  8. realtime executive thread switcher, and one for the realtime threads
  9. themselves.
  10. Author:
  11. Joseph Ballantyne
  12. Environment:
  13. Kernel Mode
  14. Revision History:
  15. --*/
  16. // This file contains the real time executive exception handlers.
  17. // These exception handlers are used to build a separate real time IDT that is
  18. // loaded whenever we switch into the real time executive. This real time IDT is
  19. // kept loaded while any real time thread is running. We switch back to the Windows
  20. // IDT when the real time executive transfers control back to Windows.
  21. // It is very important that we do NOT allow the standard Windows exception handlers
  22. // to fire while we are running real time threads. It makes Windows get very confused
  23. // and the system will eventually crash.
  24. // The first thing we do in all of these execption handlers is to disable the
  25. // interrupt that drives the real time scheduler.
  26. // Then we break into the debugger - since currently it is an error to cause any
  27. // exception to fire while running in a real time thread.
  28. // After we get control back from the debugger, we kill the real time thread that
  29. // caused the exception.
  30. // Since I switch the IDT AFTER I load in a valid DS, I should be able to turn off
  31. // the scheduler interrupt in 1 instruction. I assume that DS is valid.
  32. // In order to be able to turn off the scheduler in 1 instruction, I will need to
  33. // map the APIC to memory where the real time executive is loaded. This will
  34. // allow me to get around the x86 instruction set limits - which do not support
  35. // indirect memory addressing.
  36. // Currently that is not being done, so it takes 3 instructions to get the interrupt
  37. // disabled instead of 1.
  38. // Note that since loading the processor IDT register is a READ from memory, if
  39. // I do not have to save the IDT first, then I can make it the VERY first thing
  40. // that happens in the real time executive SwitchRealTimeThreads interrupt handler.
  41. // That would be cool.
  42. // It would be nice if the processor automatically pushed the old IDT settings into
  43. // an internal register, and there was an instruction for poping it back out - as well
  44. // as a flag that you could read if 2 pushes occur without a pop - and blow away
  45. // one of the stored IDTs.
  46. // One very cool thing is that I figured out that we can switch the IDT as the
  47. // FIRST instruction of our real time interrupt handler - and we can restore it
  48. // as the LAST instruction of our real time interrupt handler. This is possible
  49. // as long as other people are NOT switching the IDT - so that the IDT that we
  50. // reload for Windows is correct. If other people are switching the Windows IDT,
  51. // then that will NOT work, since we will have to save the current IDT first before
  52. // we load the new one and that would involve getting a valid segment register loaded
  53. // that we can write into memory with.
  54. // However, I now have the realtime executive saving eax on the stack in
  55. // order to get working space rather than sticking it in DR0 or CR2.
  56. // That is much faster and safer, and it seems to work just fine - as
  57. // it should.
  58. // Since I CAN use the stack to save state, I may be able to save the
  59. // IDT directly on the stack at the start of the real time executive
  60. // interrupt, and then load the new one as the second instruction.
  61. // This means we will have to burn 16 cycles on every thread switch
  62. // saving and loading the IDT (assuming that we also have a separate
  63. // IDT for the real time executive itself). Which I think is probably
  64. // a good idea. 4 cycles to save the initial IDT, 6 cycles to load
  65. // the rt exec IDT, and 6 cycles to load the final IDT for the thread
  66. // being switched to.
  67. #include "common.h"
  68. #include "rtp.h"
  69. #include "x86.h"
  70. #include "rtexcept.h"
  71. #include "apic.h"
  72. #include "irq.h"
  73. #define MAX_DEFAULT_HANDLERS 20
  74. #pragma LOCKED_CODE
  75. #pragma LOCKED_DATA
  76. #ifdef MASKABLEINTERRUPT
  77. extern ULONG InjectWindowsInterrupt;
  78. extern SPTR HandleWindowsInterrupt;
  79. #endif
  80. #ifdef DEBUG
  81. ULONG HitInt3InRtThread=0;
  82. #endif
  83. #ifdef RAISE_DEBUGGER_IRQL
  84. ULONG DebuggerSavedIrql=0;
  85. #endif
  86. // This is a collection of the entry point addresses of the handlers.
  87. // It makes the IDT initialization simpler.
  88. VOID (* RtExceptionHandlers[MAX_DEFAULT_HANDLERS])(VOID)={
  89. DivideByZero, // 0x0
  90. DebugExceptions,
  91. RtExecPerfCounterNMI,
  92. RtExecBreakPoint,
  93. Overflow,
  94. BoundsCheck,
  95. InvalidOpcode,
  96. DeviceNotAvailable,
  97. DoubleFault,
  98. IntelReserved,
  99. InvalidTSS,
  100. SegmentNotPresent,
  101. StackException,
  102. GeneralProtection,
  103. PageFault,
  104. IntelReserved,
  105. FloatingPointError, // 0x10
  106. AlignmentCheck,
  107. MachineCheck,
  108. IntelReserved
  109. };
  110. // This is the real time executive IDT itself. Note that Intel suggests that it is
  111. // a good idea to make sure that the IDT does NOT cross a page boundary.
  112. // TODO: Ensure RtThreadID and RtExecID do NOT cross a page boundary.
  113. ID RtThreadID[RTTHREADLARGEIDTSIZE];
  114. ID RtExecID[RTEXECIDTSIZE];
  115. TSS RtTss={0};
  116. ULONG TssStack[512];
  117. // These hold the processor IDT register data for the rt exec and Windows. These
  118. // are used to switch the IDTs.
  119. IDT WindowsIDT;
  120. IDT RtExecIDT;
  121. IDT RtThreadIDT;
  122. IDT DebugIDT; // This is the IDT that will be restored by RT exception handlers.
  123. IDT NextIDT;
  124. // These are for debug purposes - to see if either the Windows or RT exec IDT
  125. // addresses change over time. The RT exec IDT should NEVER change. I expect
  126. // the Windows NT IDT to never change. The Win9x IDT may change since who knows
  127. // what the 3rd party VxD code does.
  128. // Note that for Windows NT if the IDT never changes we could optimize out the
  129. // saving of the current IDT and simply load the RT idt. That would allow us to
  130. // do that as the very first thing in the interrupt handler.
  131. // Doing that would also allow us to detect if SoftICE is trapping debug register
  132. // accesses. I expect that it is.
  133. #ifdef DEBUG
  134. IDT LastWindowsIDT;
  135. IDT LastRtThreadIDT;
  136. #endif
  137. VOID
  138. RtpSetupTss (
  139. VOID
  140. )
  141. {
  142. RtTss.link=0;
  143. RtTss.esp0=(ULONG)(TssStack+sizeof(TssStack)/sizeof(ULONG)-1);
  144. RtTss.ss0=RealTimeSS;
  145. RtTss.cr3=ReadCR3();
  146. RtTss.eip=(ULONG)DoubleFault;
  147. RtTss.eflags=0;
  148. RtTss.esp=(ULONG)(TssStack+sizeof(TssStack)/sizeof(ULONG)-1);
  149. RtTss.es=RealTimeDS;
  150. RtTss.cs=RtExecCS;
  151. RtTss.ss=RealTimeSS;
  152. RtTss.ds=RealTimeDS;
  153. RtTss.ldt=0;
  154. RtTss.t=0;
  155. RtTss.iomap=0x80; // Past end of TSS segment. All I/O causes exceptions.
  156. }
  157. VOID
  158. RtpBuildIdt (
  159. ID *Id,
  160. ULONG Size
  161. )
  162. {
  163. ULONG i,j;
  164. // Now build our IDT.
  165. j=0;
  166. for (i=0; i<Size; i++) {
  167. #ifdef GUARD_PAGE
  168. if (i!=DOUBLEFAULTIDTINDEX) {
  169. #endif
  170. Id[i].lowoffset=(WORD)RtExceptionHandlers[j];
  171. Id[i].selector=RtExecCS;
  172. Id[i].flags=0x8e00;
  173. Id[i].highoffset=(WORD)((DWORD)RtExceptionHandlers[j]>>16);
  174. #ifdef GUARD_PAGE
  175. }
  176. else {
  177. Id[i].lowoffset=0;
  178. Id[i].selector=RtExecTSS;
  179. Id[i].flags=0x8500;
  180. Id[i].highoffset=0;
  181. }
  182. #endif
  183. if (j<MAX_DEFAULT_HANDLERS-1) {
  184. j++;
  185. }
  186. }
  187. }
  188. VOID
  189. RtpReplaceDefaultIdtHandler (
  190. ID *Id,
  191. ULONG Size,
  192. VOID (* ExceptionHandler)(VOID),
  193. ULONG Location
  194. )
  195. {
  196. ASSERT (Location < Size);
  197. ASSERT (Location != DOUBLEFAULTIDTINDEX);
  198. Id[Location].lowoffset=(WORD)ExceptionHandler;
  199. Id[Location].selector=RtExecCS;
  200. Id[Location].flags=0x8e00;
  201. Id[Location].highoffset=(WORD)((DWORD)ExceptionHandler>>16);
  202. }
  203. VOID
  204. RtpSetupIdtSwitch (
  205. ULONG TimerVector,
  206. ULONG PerfVector,
  207. ULONG ErrorVector,
  208. ULONG SpuriousVector
  209. )
  210. {
  211. ULONG RtThreadIdtSize=RTTHREADSMALLIDTSIZE;
  212. if (TimerVector!=MASKABLEIDTINDEX ||
  213. PerfVector!=RTINTERRUPT ||
  214. ErrorVector!=APICERRORIDTINDEX ||
  215. SpuriousVector!=APICSPURIOUSIDTINDEX) {
  216. // We are not using the default vector locations for the local APIC interrupt
  217. // sources. This is because the HAL supports the local APIC and has already
  218. // set it up, so use a full IDT rather than a small one. That way we will
  219. // be able to use the same perf and timer vector locations as the ones the
  220. // HAL has set up.
  221. RtThreadIdtSize=RTTHREADLARGEIDTSIZE;
  222. }
  223. SaveIDT(WindowsIDT);
  224. RtpSetupTss();
  225. // We build the rt executive IDT without the SwitchRealTimeThreads vector
  226. // in it. That way we can catch whenever we get an interrupt that would
  227. // reenter SwitchRealTimeThreads.
  228. RtpBuildIdt(RtExecID, RTEXECIDTSIZE);
  229. #ifdef NONMI
  230. RtpReplaceDefaultIdtHandler(RtExecID, RTEXECIDTSIZE, JumpToWindowsNmiHandler, NMIIDTINDEX);
  231. #endif
  232. #ifdef DEBUG
  233. //RtpReplaceDefaultIdtHandler(RtExecID, RTEXECIDTSIZE, RtThreadBreakPoint, BREAKPOINTIDTINDEX);
  234. #endif
  235. // Now build the realtime thread IDT. This is the IDT that is loaded while
  236. // any realtime thread is running.
  237. RtpBuildIdt(RtThreadID, RtThreadIdtSize);
  238. // Replace default vectors with those we need to enable the realtime threads to
  239. // be switched properly.
  240. #ifndef NONMI
  241. RtpReplaceDefaultIdtHandler(RtThreadID, RtThreadIdtSize, RtThreadPerfCounterNMI, NMIIDTINDEX);
  242. #endif
  243. RtpReplaceDefaultIdtHandler(RtThreadID, RtThreadIdtSize, RtThreadBreakPoint, BREAKPOINTIDTINDEX);
  244. #ifdef MASKABLEINTERRUPT
  245. RtpReplaceDefaultIdtHandler(RtThreadID, RtThreadIdtSize, SwitchRealTimeThreads, PerfVector);
  246. #endif
  247. RtpReplaceDefaultIdtHandler(RtThreadID, RtThreadIdtSize, SwitchRealTimeThreads, TimerVector);
  248. RtpReplaceDefaultIdtHandler(RtThreadID, RtThreadIdtSize, RtpLocalApicErrorHandler, ErrorVector);
  249. RtpReplaceDefaultIdtHandler(RtThreadID, RtThreadIdtSize, RtpLocalApicSpuriousHandler, SpuriousVector);
  250. RtThreadIDT.base=(DWORD)RtThreadID;
  251. RtThreadIDT.limit=(WORD)(8*RtThreadIdtSize-1);
  252. RtExecIDT.base=(DWORD)RtExecID;
  253. RtExecIDT.limit=8*RTEXECIDTSIZE-1;
  254. DebugIDT=RtExecIDT;
  255. #ifdef DEBUG
  256. //RtExecIDT=WindowsIDT;
  257. LastWindowsIDT=WindowsIDT;
  258. LastRtThreadIDT=RtThreadIDT;
  259. #endif
  260. }
  261. // For now we force the debug code in the exception handlers to execute and
  262. // we force the breakpoint to hit.
  263. #ifndef DEBUG
  264. #define DEBUG 1
  265. #else
  266. #define OLDDEBUG DEBUG
  267. #endif
  268. // Here are the exception handlers.
  269. #ifdef NONMI
  270. VOID __declspec(naked) JumpToWindowsNmiHandler(VOID)
  271. {
  272. // For now I do not mask the ApicIntrInterrupt - since it should ALWAYS be
  273. // masked when we hit this anyway. Since we will only hit these after
  274. // switching IDTs.
  275. __asm push eax;
  276. DisablePerformanceCounterInterrupt();
  277. __asm mov eax, ApicTimerInterrupt
  278. __asm or dword ptr[eax], MASKED
  279. __asm mov eax, cr0
  280. __asm and eax, ~(FPUEMULATION)
  281. __asm mov cr0, eax
  282. __asm pop eax;
  283. LoadIDT(WindowsIDT);
  284. // Now we jump to the NMI handler in the Windows IDT.
  285. __asm {
  286. // Save space for address of windows NMI (int 2) handler.
  287. sub esp,8
  288. // Get working space in registers
  289. push eax
  290. push ecx
  291. mov ecx, WindowsIDT.base
  292. // ecx now has base address of the windows idt
  293. mov eax, [ecx+NMIIDTINDEX*8]
  294. shr eax, 16
  295. mov dword ptr[esp+12], eax
  296. // Now the selector for the windows NMI handler is on the stack
  297. mov eax, [ecx+NMIIDTINDEX*8+4]
  298. mov ax, [ecx+NMIIDTINDEX*8]
  299. // eax now has offset of windows NMI handler
  300. mov dword ptr[esp+8], eax
  301. // stack now setup for far return to the windows NMI handler
  302. // restore our registers
  303. pop ecx
  304. pop eax
  305. retf
  306. }
  307. }
  308. #endif
  309. VOID __declspec(naked) DoWindowsNmi(VOID)
  310. {
  311. __asm push eax;
  312. __asm mov eax, cr0
  313. __asm and eax, ~(FPUEMULATION)
  314. __asm mov cr0, eax
  315. __asm pop eax;
  316. LoadIDT(WindowsIDT);
  317. // Now we jump to the NMI handler in the Windows IDT.
  318. __asm {
  319. // Save space for address of windows NMI (int 2) handler.
  320. sub esp,8
  321. // Get working space in registers
  322. push eax
  323. push ecx
  324. mov ecx, WindowsIDT.base
  325. // ecx now has base address of the windows idt
  326. mov eax, [ecx+NMIIDTINDEX*8]
  327. shr eax, 16
  328. mov dword ptr[esp+12], eax
  329. // Now the selector for the windows NMI handler is on the stack
  330. mov eax, [ecx+NMIIDTINDEX*8+4]
  331. mov ax, [ecx+NMIIDTINDEX*8]
  332. // eax now has offset of windows NMI handler
  333. mov dword ptr[esp+8], eax
  334. // stack now setup for far return to the windows NMI handler
  335. // restore our registers
  336. pop ecx
  337. pop eax
  338. retf
  339. }
  340. }
  341. #pragma warning(disable: 4414)
  342. VOID __declspec(naked) RtThreadPerfCounterNMI(VOID)
  343. {
  344. __asm {
  345. // First check if we have masked the performance counter interrupt.
  346. // If not, then jump to switchrealtimethreads.
  347. test cs:PerformanceInterruptState, MASKPERF0INT
  348. jz SwitchRealTimeThreads
  349. // Otherwise, the performance counter interrupt is masked. Eat it.
  350. }
  351. Return();
  352. }
  353. VOID __declspec(naked) RtExecPerfCounterNMI(VOID)
  354. {
  355. __asm {
  356. // First check if we have masked the performance counter interrupt.
  357. // If not, then jump to switchrealtimethreads.
  358. test cs:PerformanceInterruptState, MASKPERF0INT
  359. jz IntelReserved
  360. // Otherwise, the performance counter interrupt is masked. Eat it.
  361. }
  362. Return();
  363. }
  364. #pragma warning(default: 4414)
  365. VOID __declspec(naked) DivideByZero(VOID)
  366. {
  367. __asm push eax;
  368. DisablePerformanceCounterInterrupt();
  369. __asm mov eax, ApicTimerInterrupt
  370. __asm or dword ptr[eax], MASKED
  371. __asm mov eax, cr0
  372. __asm and eax, ~(FPUEMULATION)
  373. __asm mov cr0, eax
  374. __asm pop eax;
  375. #ifdef DEBUG
  376. LoadIDT(WindowsIDT);
  377. Break();
  378. LoadIDT(DebugIDT);
  379. #endif
  380. Return();
  381. }
  382. // When this hits, we switch IDT's back to the WindowsIDT and then break
  383. // into the debugger. Note this should catch if Winice is trapping my
  384. // use of the debug register to hold EAX. (It is NOT.)
  385. VOID __declspec(naked) DebugExceptions(VOID)
  386. {
  387. __asm push eax;
  388. DisablePerformanceCounterInterrupt();
  389. __asm mov eax, ApicTimerInterrupt
  390. __asm or dword ptr[eax], MASKED
  391. __asm mov eax, cr0
  392. __asm and eax, ~(FPUEMULATION)
  393. __asm mov cr0, eax
  394. __asm pop eax;
  395. LoadIDT(WindowsIDT);
  396. Break();
  397. Return();
  398. }
  399. VOID __declspec(naked) RtExecBreakPoint(VOID)
  400. {
  401. __asm push eax;
  402. DisablePerformanceCounterInterrupt();
  403. __asm mov eax, ApicTimerInterrupt
  404. __asm or dword ptr[eax], MASKED
  405. __asm mov eax, cr0
  406. __asm and eax, ~(FPUEMULATION)
  407. __asm mov cr0, eax
  408. #ifdef RAISE_DEBUGGER_IRQL
  409. // Now save current IRQL setting, and set IRQL to HIGH_LEVEL.
  410. // This is so we will work with the NT debugger - which pays attention to
  411. // IRQL levels. If we don't do this, then we reboot on NT when we try to
  412. // go after hitting a breakpoint inside the realtime executive.
  413. __asm push ecx
  414. __asm mov eax,pCurrentIrql
  415. __asm mov cl, 0x1f
  416. __asm xchg cl, byte ptr[eax]
  417. __asm mov byte ptr[DebuggerSavedIrql],cl
  418. __asm pop ecx
  419. #endif
  420. __asm pop eax;
  421. #ifdef DEBUG
  422. LoadIDT(WindowsIDT);
  423. Break();
  424. #ifdef RAISE_DEBUGGER_IRQL
  425. // Restore IRQL BEFORE we load our IDT. This is so we can never
  426. // overwrite the DebuggerSavedIrql before we have restored it.
  427. __asm push eax
  428. __asm push ecx
  429. __asm mov eax,pCurrentIrql
  430. __asm mov cl, byte ptr[DebuggerSavedIrql]
  431. __asm mov DebuggerSavedIrql, 0
  432. __asm mov byte ptr[eax], cl
  433. __asm pop ecx
  434. __asm pop eax
  435. #endif
  436. // Restore the appropriate IDT.
  437. LoadIDT(DebugIDT);
  438. #endif
  439. Return();
  440. }
  441. // When this hits, we switch IDT's back to the WindowsIDT and then break
  442. // into the debugger.
  443. // Note that this will obviate the need for an RtTrap instruction. Since it will
  444. // make normal breakpoints work properly automatically. Well it will make them
  445. // work like RtTrap currently does - as long as we do NOT switch back the IDT
  446. // before returning to the thread. As long as we handle the case when the
  447. // real time thread yields control to the rt executive and the IDT is NOT the
  448. // real time IDT and we don't break because of that we will be OK.
  449. // At the end of this routine, we switch back to the current IDT.
  450. // WARNING: We may need to change the limit on windows flat CS selector so that
  451. // we do NOT get gp faults in it when we are stepping through the
  452. // realtime code. Especially since we will be back in the windows IDT.
  453. VOID __declspec(naked) RtThreadBreakPoint(VOID)
  454. {
  455. // For now I do not mask the ApicIntrInterrupt - since it should ALWAYS be
  456. // masked when we hit this anyway. Since we will only hit these after
  457. // switching IDTs.
  458. __asm push eax;
  459. DisablePerformanceCounterInterrupt();
  460. __asm mov eax, ApicTimerInterrupt
  461. __asm or dword ptr[eax], MASKED
  462. __asm mov eax, cr0
  463. __asm and eax, ~(FPUEMULATION)
  464. __asm mov cr0, eax
  465. __asm pop eax;
  466. LoadIDT(WindowsIDT);
  467. // Now we jump to the int3 handler in the debugger.
  468. __asm {
  469. // But first we clear the IF flag on our stack - so that interrupts are
  470. // disabled. This means the only way we will switch out of the
  471. // realtime thread - if we break into it, is to RtYield. If the thread
  472. // never RtYields, or blocks on a spinlock, or releases a spinlock
  473. // blocking another thread, then we will be stuck in that thread forever.
  474. and dword ptr[esp+8], ~(IF)
  475. #ifdef OLDDEBUG
  476. // Keep debug check for interrupts enabled from hitting since we just
  477. // turned them off ourselves intentionally.
  478. inc HitInt3InRtThread
  479. #endif
  480. // Save space for address of windows int3 handler.
  481. sub esp,8
  482. // Get working space in registers
  483. push eax
  484. push ecx
  485. mov ecx, WindowsIDT.base
  486. // ecx now has base address of the windows idt
  487. mov eax, [ecx+3*8]
  488. shr eax, 16
  489. mov dword ptr[esp+12], eax
  490. // Now the selector for the windows int 3 handler is on the stack
  491. mov eax, [ecx+3*8+4]
  492. mov ax, [ecx+3*8]
  493. // eax now has offset of windows int 3 handler
  494. mov dword ptr[esp+8], eax
  495. // stack now setup for far return to the windows int 3 handler
  496. // restore our registers
  497. pop ecx
  498. pop eax
  499. retf
  500. }
  501. }
  502. VOID __declspec(naked) Overflow(VOID)
  503. {
  504. __asm push eax;
  505. DisablePerformanceCounterInterrupt();
  506. __asm mov eax, ApicTimerInterrupt
  507. __asm or dword ptr[eax], MASKED
  508. __asm mov eax, cr0
  509. __asm and eax, ~(FPUEMULATION)
  510. __asm mov cr0, eax
  511. __asm pop eax;
  512. #ifdef DEBUG
  513. LoadIDT(WindowsIDT);
  514. Break();
  515. LoadIDT(DebugIDT);
  516. #endif
  517. Return();
  518. }
  519. VOID __declspec(naked) BoundsCheck(VOID)
  520. {
  521. __asm push eax;
  522. DisablePerformanceCounterInterrupt();
  523. __asm mov eax, ApicTimerInterrupt
  524. __asm or dword ptr[eax], MASKED
  525. __asm mov eax, cr0
  526. __asm and eax, ~(FPUEMULATION)
  527. __asm mov cr0, eax
  528. __asm pop eax;
  529. #ifdef DEBUG
  530. LoadIDT(WindowsIDT);
  531. Break();
  532. LoadIDT(DebugIDT);
  533. #endif
  534. Return();
  535. }
  536. VOID __declspec(naked) InvalidOpcode(VOID)
  537. {
  538. __asm push eax;
  539. DisablePerformanceCounterInterrupt();
  540. __asm mov eax, ApicTimerInterrupt
  541. __asm or dword ptr[eax], MASKED
  542. __asm mov eax, cr0
  543. __asm and eax, ~(FPUEMULATION)
  544. __asm mov cr0, eax
  545. __asm pop eax;
  546. #ifdef DEBUG
  547. LoadIDT(WindowsIDT);
  548. Break();
  549. LoadIDT(DebugIDT);
  550. #endif
  551. Return();
  552. }
  553. VOID __declspec(naked) DeviceNotAvailable(VOID)
  554. {
  555. __asm push eax;
  556. DisablePerformanceCounterInterrupt();
  557. __asm mov eax, ApicTimerInterrupt
  558. __asm or dword ptr[eax], MASKED
  559. __asm mov eax, cr0
  560. __asm and eax, ~(FPUEMULATION)
  561. __asm mov cr0, eax
  562. __asm pop eax;
  563. #ifdef DEBUG
  564. LoadIDT(WindowsIDT);
  565. Break();
  566. LoadIDT(DebugIDT);
  567. #endif
  568. Return();
  569. }
  570. VOID __declspec(naked) DoubleFault(VOID)
  571. {
  572. __asm push eax;
  573. DisablePerformanceCounterInterrupt();
  574. __asm mov eax, ApicTimerInterrupt
  575. __asm or dword ptr[eax], MASKED
  576. __asm mov eax, cr0
  577. __asm and eax, ~(FPUEMULATION)
  578. __asm mov cr0, eax
  579. __asm pop eax;
  580. #ifdef DEBUG
  581. LoadIDT(WindowsIDT);
  582. Break();
  583. LoadIDT(DebugIDT);
  584. #endif
  585. Return();
  586. }
  587. VOID __declspec(naked) IntelReserved(VOID)
  588. {
  589. __asm push eax;
  590. DisablePerformanceCounterInterrupt();
  591. __asm mov eax, ApicTimerInterrupt
  592. __asm or dword ptr[eax], MASKED
  593. __asm mov eax, cr0
  594. __asm and eax, ~(FPUEMULATION)
  595. __asm mov cr0, eax
  596. __asm pop eax;
  597. #ifdef DEBUG
  598. LoadIDT(WindowsIDT);
  599. Break();
  600. LoadIDT(DebugIDT);
  601. #endif
  602. Return();
  603. }
  604. VOID __declspec(naked) InvalidTSS(VOID)
  605. {
  606. __asm push eax;
  607. DisablePerformanceCounterInterrupt();
  608. __asm mov eax, ApicTimerInterrupt
  609. __asm or dword ptr[eax], MASKED
  610. __asm mov eax, cr0
  611. __asm and eax, ~(FPUEMULATION)
  612. __asm mov cr0, eax
  613. __asm pop eax;
  614. #ifdef DEBUG
  615. LoadIDT(WindowsIDT);
  616. Break();
  617. LoadIDT(DebugIDT);
  618. #endif
  619. Return();
  620. }
  621. VOID __declspec(naked) SegmentNotPresent(VOID)
  622. {
  623. __asm push eax;
  624. DisablePerformanceCounterInterrupt();
  625. __asm mov eax, ApicTimerInterrupt
  626. __asm or dword ptr[eax], MASKED
  627. __asm mov eax, cr0
  628. __asm and eax, ~(FPUEMULATION)
  629. __asm mov cr0, eax
  630. __asm pop eax;
  631. #ifdef DEBUG
  632. LoadIDT(WindowsIDT);
  633. Break();
  634. LoadIDT(DebugIDT);
  635. #endif
  636. Return();
  637. }
  638. VOID __declspec(naked) StackException(VOID)
  639. {
  640. __asm push eax;
  641. DisablePerformanceCounterInterrupt();
  642. __asm mov eax, ApicTimerInterrupt
  643. __asm or dword ptr[eax], MASKED
  644. __asm mov eax, cr0
  645. __asm and eax, ~(FPUEMULATION)
  646. __asm mov cr0, eax
  647. __asm pop eax;
  648. #ifdef DEBUG
  649. LoadIDT(WindowsIDT);
  650. Break();
  651. LoadIDT(DebugIDT);
  652. #endif
  653. Return();
  654. }
  655. VOID __declspec(naked) GeneralProtection(VOID)
  656. {
  657. #ifdef MASKABLEINTERRUPT
  658. // Unfortunately when we are using a maskable interrupt to take control from
  659. // windows, we can get PIC interrupts queued up in the cpu between the time
  660. // that we enter our switchrealtimethreads handler, and the time that we
  661. // mask the interrupt at the local apic itself.
  662. __asm {
  663. test dword ptr[esp],1
  664. jz catchthis
  665. test dword ptr[esp],2
  666. jz catchthis
  667. // If we get here, then this is an interrupt that got logged into the
  668. // processor from the pic, between the time that interrupts were disabled
  669. // by entering switchrealtimethreads, and the time that we masked
  670. // external interrupts at the local apic.
  671. // We need to save the error code - since it has the vector information
  672. // of the interrupt that we need to call when we return back to
  673. // windows. We also need to make sure that there isn't already an error
  674. // code logged. If there is, that is a fatal error, since we don't
  675. // currently support logging of multiple interrupts that we call when
  676. // we finally get back to windows. We only support saving 1.
  677. // Trap if there is already an interrupt logged.
  678. test InjectWindowsInterrupt, 0xffffffff
  679. jnz catchthis // Taking this route is a FATAL ERROR.
  680. // Now, build the address of the windows interrupt handler for the
  681. // interrupt that we just got. Jam it into InjectWindowsInterrupt.
  682. push eax
  683. push ecx
  684. push edx
  685. // we now have registers we can use
  686. mov edx,[esp+12]
  687. and edx, 0xfffffff8
  688. // edx now has the selector index into the windows idt
  689. mov ecx, WindowsIDT.base
  690. // ecx now has base address of the windows idt
  691. mov eax, [ecx+edx]
  692. shr eax, 16
  693. mov HandleWindowsInterrupt.selector, ax
  694. // Now the selector is correct for the windows interrupt handler.
  695. mov eax, [ecx+edx+4]
  696. mov ax, [ecx+edx]
  697. // eax now has address of interrupt handler we need to call when we
  698. // eventually return to windows
  699. mov InjectWindowsInterrupt, eax
  700. pop edx
  701. pop ecx
  702. pop eax
  703. // we have handled this nasty problem and are ready to rock
  704. // exit this handler without stopping
  705. jmp exit
  706. catchthis:
  707. }
  708. #endif
  709. __asm push eax;
  710. DisablePerformanceCounterInterrupt();
  711. __asm mov eax, ApicTimerInterrupt
  712. __asm or dword ptr[eax], MASKED
  713. __asm mov eax, cr0
  714. __asm and eax, ~(FPUEMULATION)
  715. __asm mov cr0, eax
  716. __asm pop eax;
  717. #ifdef DEBUG
  718. LoadIDT(WindowsIDT);
  719. Break();
  720. LoadIDT(DebugIDT);
  721. #endif
  722. #ifdef MASKABLEINTERRUPT
  723. exit:
  724. #endif
  725. // Clean the error code off of the stack before returning.
  726. __asm add esp,4
  727. Return();
  728. }
  729. VOID __declspec(naked) PageFault(VOID)
  730. {
  731. __asm push eax;
  732. DisablePerformanceCounterInterrupt();
  733. __asm mov eax, ApicTimerInterrupt
  734. __asm or dword ptr[eax], MASKED
  735. __asm mov eax, cr0
  736. __asm and eax, ~(FPUEMULATION)
  737. __asm mov cr0, eax
  738. __asm pop eax;
  739. #ifdef DEBUG
  740. LoadIDT(WindowsIDT);
  741. Break();
  742. LoadIDT(DebugIDT);
  743. #endif
  744. // Now clean the error code off of the stack before returning.
  745. __asm add esp,4
  746. Return();
  747. }
  748. VOID __declspec(naked) FloatingPointError(VOID)
  749. {
  750. __asm push eax;
  751. DisablePerformanceCounterInterrupt();
  752. __asm mov eax, ApicTimerInterrupt
  753. __asm or dword ptr[eax], MASKED
  754. __asm mov eax, cr0
  755. __asm and eax, ~(FPUEMULATION)
  756. __asm mov cr0, eax
  757. __asm pop eax;
  758. #ifdef DEBUG
  759. LoadIDT(WindowsIDT);
  760. Break();
  761. LoadIDT(DebugIDT);
  762. #endif
  763. Return();
  764. }
  765. VOID __declspec(naked) AlignmentCheck(VOID)
  766. {
  767. __asm push eax;
  768. DisablePerformanceCounterInterrupt();
  769. __asm mov eax, ApicTimerInterrupt
  770. __asm or dword ptr[eax], MASKED
  771. __asm mov eax, cr0
  772. __asm and eax, ~(FPUEMULATION)
  773. __asm mov cr0, eax
  774. __asm pop eax;
  775. #ifdef DEBUG
  776. LoadIDT(WindowsIDT);
  777. Break();
  778. LoadIDT(DebugIDT);
  779. #endif
  780. Return();
  781. }
  782. VOID __declspec(naked) MachineCheck(VOID)
  783. {
  784. __asm push eax;
  785. DisablePerformanceCounterInterrupt();
  786. __asm mov eax, ApicTimerInterrupt
  787. __asm or dword ptr[eax], MASKED
  788. __asm mov eax, cr0
  789. __asm and eax, ~(FPUEMULATION)
  790. __asm mov cr0, eax
  791. __asm pop eax;
  792. #ifdef DEBUG
  793. LoadIDT(WindowsIDT);
  794. Break();
  795. LoadIDT(DebugIDT);
  796. #endif
  797. Return();
  798. }