0a8cf6ffc334535b3ebea4fa254efe612faedb12
[reactos.git] / ntoskrnl / ke / i386 / traphdlr.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/ke/i386/traphdlr.c
5 * PURPOSE: Kernel Trap Handlers
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 VOID __cdecl KiFastCallEntry(VOID);
16 VOID __cdecl KiFastCallEntryWithSingleStep(VOID);
17
18 extern PVOID KeUserPopEntrySListFault;
19 extern PVOID KeUserPopEntrySListResume;
20 extern PVOID FrRestore;
21 VOID FASTCALL Ke386LoadFpuState(IN PFX_SAVE_AREA SaveArea);
22
23 /* GLOBALS ********************************************************************/
24
25 UCHAR KiTrapPrefixTable[] =
26 {
27 0xF2, /* REP */
28 0xF3, /* REP INS/OUTS */
29 0x67, /* ADDR */
30 0xF0, /* LOCK */
31 0x66, /* OP */
32 0x2E, /* SEG */
33 0x3E, /* DS */
34 0x26, /* ES */
35 0x64, /* FS */
36 0x65, /* GS */
37 0x36, /* SS */
38 };
39
40 UCHAR KiTrapIoTable[] =
41 {
42 0xE4, /* IN */
43 0xE5, /* IN */
44 0xEC, /* IN */
45 0xED, /* IN */
46 0x6C, /* INS */
47 0x6D, /* INS */
48 0xE6, /* OUT */
49 0xE7, /* OUT */
50 0xEE, /* OUT */
51 0xEF, /* OUT */
52 0x6E, /* OUTS */
53 0x6F, /* OUTS */
54 };
55
56 PFAST_SYSTEM_CALL_EXIT KiFastCallExitHandler;
57 #if DBG && defined(_M_IX86) && !defined(_WINKD_)
58 PKDBG_PRESERVICEHOOK KeWin32PreServiceHook = NULL;
59 PKDBG_POSTSERVICEHOOK KeWin32PostServiceHook = NULL;
60 #endif
61 #if DBG
62 BOOLEAN StopChecking = FALSE;
63 #endif
64
65
66 /* TRAP EXIT CODE *************************************************************/
67
68 FORCEINLINE
69 BOOLEAN
70 KiVdmTrap(IN PKTRAP_FRAME TrapFrame)
71 {
72 /* Either the V8086 flag is on, or this is user-mode with a VDM */
73 return ((TrapFrame->EFlags & EFLAGS_V86_MASK) ||
74 ((KiUserTrap(TrapFrame)) && (PsGetCurrentProcess()->VdmObjects)));
75 }
76
77 FORCEINLINE
78 BOOLEAN
79 KiV86Trap(IN PKTRAP_FRAME TrapFrame)
80 {
81 /* Check if the V8086 flag is on */
82 return ((TrapFrame->EFlags & EFLAGS_V86_MASK) != 0);
83 }
84
85 FORCEINLINE
86 BOOLEAN
87 KiIsFrameEdited(IN PKTRAP_FRAME TrapFrame)
88 {
89 /* An edited frame changes esp. It is marked by clearing the bits
90 defined by FRAME_EDITED in the SegCs field of the trap frame */
91 return ((TrapFrame->SegCs & FRAME_EDITED) == 0);
92 }
93
94 FORCEINLINE
95 VOID
96 KiCommonExit(IN PKTRAP_FRAME TrapFrame, BOOLEAN SkipPreviousMode)
97 {
98 /* Disable interrupts until we return */
99 _disable();
100
101 /* Check for APC delivery */
102 KiCheckForApcDelivery(TrapFrame);
103
104 /* Restore the SEH handler chain */
105 KeGetPcr()->NtTib.ExceptionList = TrapFrame->ExceptionList;
106
107 /* Check if there are active debug registers */
108 if (__builtin_expect(TrapFrame->Dr7 & ~DR7_RESERVED_MASK, 0))
109 {
110 /* Check if the frame was from user mode or v86 mode */
111 if (KiUserTrap(TrapFrame) ||
112 (TrapFrame->EFlags & EFLAGS_V86_MASK))
113 {
114 /* Handle debug registers */
115 KiHandleDebugRegistersOnTrapExit(TrapFrame);
116 }
117 }
118
119 /* Debugging checks */
120 KiExitTrapDebugChecks(TrapFrame, SkipPreviousMode);
121 }
122
123 DECLSPEC_NORETURN
124 VOID
125 FASTCALL
126 KiEoiHelper(IN PKTRAP_FRAME TrapFrame)
127 {
128 /* Common trap exit code */
129 KiCommonExit(TrapFrame, TRUE);
130
131 /* Check if this was a V8086 trap */
132 if (TrapFrame->EFlags & EFLAGS_V86_MASK) KiTrapReturnNoSegments(TrapFrame);
133
134 /* Check for user mode exit */
135 if (KiUserTrap(TrapFrame)) KiTrapReturn(TrapFrame);
136
137 /* Check for edited frame */
138 if (KiIsFrameEdited(TrapFrame)) KiEditedTrapReturn(TrapFrame);
139
140 /* Check if we have single stepping enabled */
141 if (TrapFrame->EFlags & EFLAGS_TF) KiTrapReturnNoSegments(TrapFrame);
142
143 /* Exit the trap to kernel mode */
144 KiTrapReturnNoSegmentsRet8(TrapFrame);
145 }
146
147 DECLSPEC_NORETURN
148 VOID
149 FASTCALL
150 KiServiceExit(IN PKTRAP_FRAME TrapFrame,
151 IN NTSTATUS Status)
152 {
153 ASSERT((TrapFrame->EFlags & EFLAGS_V86_MASK) == 0);
154 ASSERT(!KiIsFrameEdited(TrapFrame));
155
156 /* Copy the status into EAX */
157 TrapFrame->Eax = Status;
158
159 /* Common trap exit code */
160 KiCommonExit(TrapFrame, FALSE);
161
162 /* Restore previous mode */
163 KeGetCurrentThread()->PreviousMode = (CCHAR)TrapFrame->PreviousPreviousMode;
164
165 /* Check for user mode exit */
166 if (KiUserTrap(TrapFrame))
167 {
168 /* Check if we were single stepping */
169 if (TrapFrame->EFlags & EFLAGS_TF)
170 {
171 /* Must use the IRET handler */
172 KiSystemCallTrapReturn(TrapFrame);
173 }
174 else
175 {
176 /* We can use the sysexit handler */
177 KiFastCallExitHandler(TrapFrame);
178 UNREACHABLE;
179 }
180 }
181
182 /* Exit to kernel mode */
183 KiSystemCallReturn(TrapFrame);
184 }
185
186 DECLSPEC_NORETURN
187 VOID
188 FASTCALL
189 KiServiceExit2(IN PKTRAP_FRAME TrapFrame)
190 {
191 /* Common trap exit code */
192 KiCommonExit(TrapFrame, FALSE);
193
194 /* Restore previous mode */
195 KeGetCurrentThread()->PreviousMode = (CCHAR)TrapFrame->PreviousPreviousMode;
196
197 /* Check if this was a V8086 trap */
198 if (TrapFrame->EFlags & EFLAGS_V86_MASK) KiTrapReturnNoSegments(TrapFrame);
199
200 /* Check for user mode exit */
201 if (KiUserTrap(TrapFrame)) KiTrapReturn(TrapFrame);
202
203 /* Check for edited frame */
204 if (KiIsFrameEdited(TrapFrame)) KiEditedTrapReturn(TrapFrame);
205
206 /* Check if we have single stepping enabled */
207 if (TrapFrame->EFlags & EFLAGS_TF) KiTrapReturnNoSegments(TrapFrame);
208
209 /* Exit the trap to kernel mode */
210 KiTrapReturnNoSegmentsRet8(TrapFrame);
211 }
212
213
214 /* TRAP HANDLERS **************************************************************/
215
216 DECLSPEC_NORETURN
217 VOID
218 FASTCALL
219 KiDebugHandler(IN PKTRAP_FRAME TrapFrame,
220 IN ULONG Parameter1,
221 IN ULONG Parameter2,
222 IN ULONG Parameter3)
223 {
224 /* Check for VDM trap */
225 ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
226
227 /* Enable interrupts if the trap came from user-mode */
228 if (KiUserTrap(TrapFrame)) _enable();
229
230 /* Dispatch the exception */
231 KiDispatchExceptionFromTrapFrame(STATUS_BREAKPOINT,
232 0,
233 TrapFrame->Eip - 1,
234 3,
235 Parameter1,
236 Parameter2,
237 Parameter3,
238 TrapFrame);
239 }
240
241 DECLSPEC_NORETURN
242 VOID
243 FASTCALL
244 KiNpxHandler(IN PKTRAP_FRAME TrapFrame,
245 IN PKTHREAD Thread,
246 IN PFX_SAVE_AREA SaveArea)
247 {
248 ULONG Cr0, Mask, Error, ErrorOffset, DataOffset;
249
250 /* Check for VDM trap */
251 ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
252
253 /* Check for kernel trap */
254 if (!KiUserTrap(TrapFrame))
255 {
256 /* Kernel might've tripped a delayed error */
257 SaveArea->Cr0NpxState |= CR0_TS;
258
259 /* Only valid if it happened during a restore */
260 if ((PVOID)TrapFrame->Eip == FrRestore)
261 {
262 /* It did, so just skip the instruction */
263 TrapFrame->Eip += 3; /* Size of FRSTOR instruction */
264 KiEoiHelper(TrapFrame);
265 }
266 }
267
268 /* User or kernel trap -- check if we need to unload the current state */
269 if (Thread->NpxState == NPX_STATE_LOADED)
270 {
271 /* Update CR0 */
272 Cr0 = __readcr0();
273 Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
274 __writecr0(Cr0);
275
276 /* Save FPU state */
277 Ke386SaveFpuState(SaveArea);
278
279 /* Mark CR0 state dirty */
280 Cr0 |= NPX_STATE_NOT_LOADED;
281 Cr0 |= SaveArea->Cr0NpxState;
282 __writecr0(Cr0);
283
284 /* Update NPX state */
285 Thread->NpxState = NPX_STATE_NOT_LOADED;
286 KeGetCurrentPrcb()->NpxThread = NULL;
287 }
288
289 /* Clear the TS bit and re-enable interrupts */
290 SaveArea->Cr0NpxState &= ~CR0_TS;
291 _enable();
292
293 /* Check if we should get the FN or FX error */
294 if (KeI386FxsrPresent)
295 {
296 /* Get it from FX */
297 Mask = SaveArea->U.FxArea.ControlWord;
298 Error = SaveArea->U.FxArea.StatusWord;
299
300 /* Get the FPU exception address too */
301 ErrorOffset = SaveArea->U.FxArea.ErrorOffset;
302 DataOffset = SaveArea->U.FxArea.DataOffset;
303 }
304 else
305 {
306 /* Get it from FN */
307 Mask = SaveArea->U.FnArea.ControlWord;
308 Error = SaveArea->U.FnArea.StatusWord;
309
310 /* Get the FPU exception address too */
311 ErrorOffset = SaveArea->U.FnArea.ErrorOffset;
312 DataOffset = SaveArea->U.FnArea.DataOffset;
313 }
314
315 /* Get legal exceptions that software should handle */
316 Mask &= (FSW_INVALID_OPERATION |
317 FSW_DENORMAL |
318 FSW_ZERO_DIVIDE |
319 FSW_OVERFLOW |
320 FSW_UNDERFLOW |
321 FSW_PRECISION);
322 Error &= ~Mask;
323
324 /* Check for invalid operation */
325 if (Error & FSW_INVALID_OPERATION)
326 {
327 /*
328 * Now check if this is actually a Stack Fault. This is needed because
329 * on x86 the Invalid Operation error is set for Stack Check faults as well.
330 */
331 if (Error & FSW_STACK_FAULT)
332 {
333 /* Issue stack check fault */
334 KiDispatchException2Args(STATUS_FLOAT_STACK_CHECK,
335 ErrorOffset,
336 0,
337 DataOffset,
338 TrapFrame);
339 }
340 else
341 {
342 /* This is an invalid operation fault after all, so raise that instead */
343 KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION,
344 ErrorOffset,
345 0,
346 TrapFrame);
347 }
348 }
349
350 /* Check for divide by zero */
351 if (Error & FSW_ZERO_DIVIDE)
352 {
353 /* Issue fault */
354 KiDispatchException1Args(STATUS_FLOAT_DIVIDE_BY_ZERO,
355 ErrorOffset,
356 0,
357 TrapFrame);
358 }
359
360 /* Check for denormal */
361 if (Error & FSW_DENORMAL)
362 {
363 /* Issue fault */
364 KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION,
365 ErrorOffset,
366 0,
367 TrapFrame);
368 }
369
370 /* Check for overflow */
371 if (Error & FSW_OVERFLOW)
372 {
373 /* Issue fault */
374 KiDispatchException1Args(STATUS_FLOAT_OVERFLOW,
375 ErrorOffset,
376 0,
377 TrapFrame);
378 }
379
380 /* Check for underflow */
381 if (Error & FSW_UNDERFLOW)
382 {
383 /* Issue fault */
384 KiDispatchException1Args(STATUS_FLOAT_UNDERFLOW,
385 ErrorOffset,
386 0,
387 TrapFrame);
388 }
389
390 /* Check for precision fault */
391 if (Error & FSW_PRECISION)
392 {
393 /* Issue fault */
394 KiDispatchException1Args(STATUS_FLOAT_INEXACT_RESULT,
395 ErrorOffset,
396 0,
397 TrapFrame);
398 }
399
400 /* Unknown FPU fault */
401 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 1, Error, 0, 0, TrapFrame);
402 }
403
404 DECLSPEC_NORETURN
405 VOID
406 FASTCALL
407 KiTrap00Handler(IN PKTRAP_FRAME TrapFrame)
408 {
409 /* Save trap frame */
410 KiEnterTrap(TrapFrame);
411
412 /* Check for VDM trap */
413 ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
414
415 /* Enable interrupts */
416 _enable();
417
418 /* Dispatch the exception */
419 KiDispatchException0Args(STATUS_INTEGER_DIVIDE_BY_ZERO,
420 TrapFrame->Eip,
421 TrapFrame);
422 }
423
424 DECLSPEC_NORETURN
425 VOID
426 FASTCALL
427 KiTrap01Handler(IN PKTRAP_FRAME TrapFrame)
428 {
429 /* Save trap frame */
430 KiEnterTrap(TrapFrame);
431
432 /* Check for VDM trap */
433 ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
434
435 /* Check if this was a single step after sysenter */
436 if (TrapFrame->Eip == (ULONG)KiFastCallEntry)
437 {
438 /* Disable single stepping */
439 TrapFrame->EFlags &= ~EFLAGS_TF;
440
441 /* Re-enter at the alternative sysenter entry point */
442 TrapFrame->Eip = (ULONG)KiFastCallEntryWithSingleStep;
443
444 /* End this trap */
445 KiEoiHelper(TrapFrame);
446 }
447
448 /* Enable interrupts if the trap came from user-mode */
449 if (KiUserTrap(TrapFrame)) _enable();
450
451 /* Mask out trap flag and dispatch the exception */
452 TrapFrame->EFlags &= ~EFLAGS_TF;
453 KiDispatchException0Args(STATUS_SINGLE_STEP,
454 TrapFrame->Eip,
455 TrapFrame);
456 }
457
458 DECLSPEC_NORETURN
459 VOID
460 __cdecl
461 KiTrap02(VOID)
462 {
463 PKTSS Tss, NmiTss;
464 PKTHREAD Thread;
465 PKPROCESS Process;
466 PKGDTENTRY TssGdt;
467 KTRAP_FRAME TrapFrame;
468 KIRQL OldIrql;
469
470 //
471 // In some sort of strange recursion case, we might end up here with the IF
472 // flag incorrectly on the interrupt frame -- during a normal NMI this would
473 // normally already be set.
474 //
475 // For sanity's sake, make sure interrupts are disabled for sure.
476 // NMIs will already be since the CPU does it for us.
477 //
478 _disable();
479
480 //
481 // Get the current TSS, thread, and process
482 //
483 Tss = PCR->TSS;
484 Thread = ((PKIPCR)PCR)->PrcbData.CurrentThread;
485 Process = Thread->ApcState.Process;
486
487 //
488 // Save data usually not in the TSS
489 //
490 Tss->CR3 = Process->DirectoryTableBase[0];
491 Tss->IoMapBase = Process->IopmOffset;
492 Tss->LDT = Process->LdtDescriptor.LimitLow ? KGDT_LDT : 0;
493
494 //
495 // Now get the base address of the NMI TSS
496 //
497 TssGdt = &((PKIPCR)KeGetPcr())->GDT[KGDT_NMI_TSS / sizeof(KGDTENTRY)];
498 NmiTss = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
499 TssGdt->HighWord.Bytes.BaseMid << 16 |
500 TssGdt->HighWord.Bytes.BaseHi << 24);
501
502 //
503 // Switch to it and activate it, masking off the nested flag
504 //
505 // Note that in reality, we are already on the NMI tss -- we just need to
506 // update the PCR to reflect this
507 //
508 PCR->TSS = NmiTss;
509 __writeeflags(__readeflags() &~ EFLAGS_NESTED_TASK);
510 TssGdt->HighWord.Bits.Dpl = 0;
511 TssGdt->HighWord.Bits.Pres = 1;
512 TssGdt->HighWord.Bits.Type = I386_TSS;
513
514 //
515 // Now build the trap frame based on the original TSS
516 //
517 // The CPU does a hardware "Context switch" / task switch of sorts and so it
518 // takes care of saving our context in the normal TSS.
519 //
520 // We just have to go get the values...
521 //
522 RtlZeroMemory(&TrapFrame, sizeof(KTRAP_FRAME));
523 TrapFrame.HardwareSegSs = Tss->Ss0;
524 TrapFrame.HardwareEsp = Tss->Esp0;
525 TrapFrame.EFlags = Tss->EFlags;
526 TrapFrame.SegCs = Tss->Cs;
527 TrapFrame.Eip = Tss->Eip;
528 TrapFrame.Ebp = Tss->Ebp;
529 TrapFrame.Ebx = Tss->Ebx;
530 TrapFrame.Esi = Tss->Esi;
531 TrapFrame.Edi = Tss->Edi;
532 TrapFrame.SegFs = Tss->Fs;
533 TrapFrame.ExceptionList = PCR->NtTib.ExceptionList;
534 TrapFrame.PreviousPreviousMode = (ULONG)-1;
535 TrapFrame.Eax = Tss->Eax;
536 TrapFrame.Ecx = Tss->Ecx;
537 TrapFrame.Edx = Tss->Edx;
538 TrapFrame.SegDs = Tss->Ds;
539 TrapFrame.SegEs = Tss->Es;
540 TrapFrame.SegGs = Tss->Gs;
541 TrapFrame.DbgEip = Tss->Eip;
542 TrapFrame.DbgEbp = Tss->Ebp;
543
544 //
545 // Store the trap frame in the KPRCB
546 //
547 KiSaveProcessorState(&TrapFrame, NULL);
548
549 //
550 // Call any registered NMI handlers and see if they handled it or not
551 //
552 if (!KiHandleNmi())
553 {
554 //
555 // They did not, so call the platform HAL routine to bugcheck the system
556 //
557 // Make sure the HAL believes it's running at HIGH IRQL... we can't use
558 // the normal APIs here as playing with the IRQL could change the system
559 // state
560 //
561 OldIrql = PCR->Irql;
562 PCR->Irql = HIGH_LEVEL;
563 HalHandleNMI(NULL);
564 PCR->Irql = OldIrql;
565 }
566
567 //
568 // Although the CPU disabled NMIs, we just did a BIOS Call, which could've
569 // totally changed things.
570 //
571 // We have to make sure we're still in our original NMI -- a nested NMI
572 // will point back to the NMI TSS, and in that case we're hosed.
573 //
574 if (PCR->TSS->Backlink != KGDT_NMI_TSS)
575 {
576 //
577 // Restore original TSS
578 //
579 PCR->TSS = Tss;
580
581 //
582 // Set it back to busy
583 //
584 TssGdt->HighWord.Bits.Dpl = 0;
585 TssGdt->HighWord.Bits.Pres = 1;
586 TssGdt->HighWord.Bits.Type = I386_ACTIVE_TSS;
587
588 //
589 // Restore nested flag
590 //
591 __writeeflags(__readeflags() | EFLAGS_NESTED_TASK);
592
593 //
594 // Handled, return from interrupt
595 //
596 KiIret();
597 }
598
599 //
600 // Unhandled: crash the system
601 //
602 KiSystemFatalException(EXCEPTION_NMI, NULL);
603 }
604
605 DECLSPEC_NORETURN
606 VOID
607 FASTCALL
608 KiTrap03Handler(IN PKTRAP_FRAME TrapFrame)
609 {
610 /* Save trap frame */
611 KiEnterTrap(TrapFrame);
612
613 /* Continue with the common handler */
614 KiDebugHandler(TrapFrame, BREAKPOINT_BREAK, 0, 0);
615 }
616
617 DECLSPEC_NORETURN
618 VOID
619 FASTCALL
620 KiTrap04Handler(IN PKTRAP_FRAME TrapFrame)
621 {
622 /* Save trap frame */
623 KiEnterTrap(TrapFrame);
624
625 /* Check for VDM trap */
626 ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
627
628 /* Enable interrupts */
629 _enable();
630
631 /* Dispatch the exception */
632 KiDispatchException0Args(STATUS_INTEGER_OVERFLOW,
633 TrapFrame->Eip - 1,
634 TrapFrame);
635 }
636
637 DECLSPEC_NORETURN
638 VOID
639 FASTCALL
640 KiTrap05Handler(IN PKTRAP_FRAME TrapFrame)
641 {
642 /* Save trap frame */
643 KiEnterTrap(TrapFrame);
644
645 /* Check for VDM trap */
646 ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
647
648 /* Check for kernel-mode fault */
649 if (!KiUserTrap(TrapFrame)) KiSystemFatalException(EXCEPTION_BOUND_CHECK, TrapFrame);
650
651 /* Enable interrupts */
652 _enable();
653
654 /* Dispatch the exception */
655 KiDispatchException0Args(STATUS_ARRAY_BOUNDS_EXCEEDED,
656 TrapFrame->Eip,
657 TrapFrame);
658 }
659
660 DECLSPEC_NORETURN
661 VOID
662 FASTCALL
663 KiTrap06Handler(IN PKTRAP_FRAME TrapFrame)
664 {
665 PUCHAR Instruction;
666 ULONG i;
667 KIRQL OldIrql;
668
669 /* Check for V86 GPF */
670 if (__builtin_expect(KiV86Trap(TrapFrame), 1))
671 {
672 /* Enter V86 trap */
673 KiEnterV86Trap(TrapFrame);
674
675 /* Must be a VDM process */
676 if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects, 0))
677 {
678 /* Enable interrupts */
679 _enable();
680
681 /* Setup illegal instruction fault */
682 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
683 TrapFrame->Eip,
684 TrapFrame);
685 }
686
687 /* Go to APC level */
688 KeRaiseIrql(APC_LEVEL, &OldIrql);
689 _enable();
690
691 /* Check for BOP */
692 if (!VdmDispatchBop(TrapFrame))
693 {
694 /* Should only happen in VDM mode */
695 UNIMPLEMENTED_FATAL();
696 }
697
698 /* Bring IRQL back */
699 KeLowerIrql(OldIrql);
700 _disable();
701
702 /* Do a quick V86 exit if possible */
703 KiExitV86Trap(TrapFrame);
704 }
705
706 /* Save trap frame */
707 KiEnterTrap(TrapFrame);
708
709 /* Enable interrupts */
710 Instruction = (PUCHAR)TrapFrame->Eip;
711 _enable();
712
713 /* Check for user trap */
714 if (KiUserTrap(TrapFrame))
715 {
716 /* FIXME: Use SEH */
717
718 /* Scan next 4 opcodes */
719 for (i = 0; i < 4; i++)
720 {
721 /* Check for LOCK instruction */
722 if (Instruction[i] == 0xF0)
723 {
724 /* Send invalid lock sequence exception */
725 KiDispatchException0Args(STATUS_INVALID_LOCK_SEQUENCE,
726 TrapFrame->Eip,
727 TrapFrame);
728 }
729 }
730
731 /* FIXME: SEH ends here */
732 }
733
734 /* Kernel-mode or user-mode fault (but not LOCK) */
735 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
736 TrapFrame->Eip,
737 TrapFrame);
738
739 }
740
741 DECLSPEC_NORETURN
742 VOID
743 FASTCALL
744 KiTrap07Handler(IN PKTRAP_FRAME TrapFrame)
745 {
746 PKTHREAD Thread, NpxThread;
747 PFX_SAVE_AREA SaveArea, NpxSaveArea;
748 ULONG Cr0;
749
750 /* Save trap frame */
751 KiEnterTrap(TrapFrame);
752
753 /* Try to handle NPX delay load */
754 for (;;)
755 {
756 /* Get the current thread */
757 Thread = KeGetCurrentThread();
758
759 /* Get the NPX frame */
760 SaveArea = KiGetThreadNpxArea(Thread);
761
762 /* Check if emulation is enabled */
763 if (SaveArea->Cr0NpxState & CR0_EM)
764 {
765 /* Not implemented */
766 UNIMPLEMENTED_FATAL();
767 }
768
769 /* Save CR0 and check NPX state */
770 Cr0 = __readcr0();
771 if (Thread->NpxState != NPX_STATE_LOADED)
772 {
773 /* Update CR0 */
774 Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
775 __writecr0(Cr0);
776
777 /* Get the NPX thread */
778 NpxThread = KeGetCurrentPrcb()->NpxThread;
779 if (NpxThread)
780 {
781 /* Get the NPX frame */
782 NpxSaveArea = KiGetThreadNpxArea(NpxThread);
783
784 /* Save FPU state */
785 Ke386SaveFpuState(NpxSaveArea);
786
787 /* Update NPX state */
788 NpxThread->NpxState = NPX_STATE_NOT_LOADED;
789 }
790
791 /* Load FPU state */
792 Ke386LoadFpuState(SaveArea);
793
794 /* Update NPX state */
795 Thread->NpxState = NPX_STATE_LOADED;
796 KeGetCurrentPrcb()->NpxThread = Thread;
797
798 /* Enable interrupts */
799 _enable();
800
801 /* Check if CR0 needs to be reloaded due to context switch */
802 if (!SaveArea->Cr0NpxState) KiEoiHelper(TrapFrame);
803
804 /* Otherwise, we need to reload CR0, disable interrupts */
805 _disable();
806
807 /* Reload CR0 */
808 Cr0 = __readcr0();
809 Cr0 |= SaveArea->Cr0NpxState;
810 __writecr0(Cr0);
811
812 /* Now restore interrupts and check for TS */
813 _enable();
814 if (Cr0 & CR0_TS) KiEoiHelper(TrapFrame);
815
816 /* We're still here -- clear TS and try again */
817 __writecr0(__readcr0() &~ CR0_TS);
818 _disable();
819 }
820 else
821 {
822 /* This is an actual fault, not a lack of FPU state */
823 break;
824 }
825 }
826
827 /* TS should not be set */
828 if (Cr0 & CR0_TS)
829 {
830 /*
831 * If it's incorrectly set, then maybe the state is actually still valid
832 * but we could have lost track of that due to a BIOS call.
833 * Make sure MP is still set, which should verify the theory.
834 */
835 if (Cr0 & CR0_MP)
836 {
837 /* Indeed, the state is actually still valid, so clear TS */
838 __writecr0(__readcr0() &~ CR0_TS);
839 KiEoiHelper(TrapFrame);
840 }
841
842 /* Otherwise, something strange is going on */
843 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 2, Cr0, 0, 0, TrapFrame);
844 }
845
846 /* It's not a delayed load, so process this trap as an NPX fault */
847 KiNpxHandler(TrapFrame, Thread, SaveArea);
848 }
849
850 DECLSPEC_NORETURN
851 VOID
852 FASTCALL
853 KiTrap08Handler(IN PKTRAP_FRAME TrapFrame)
854 {
855 /* FIXME: Not handled */
856 KiSystemFatalException(EXCEPTION_DOUBLE_FAULT, TrapFrame);
857 }
858
859 DECLSPEC_NORETURN
860 VOID
861 FASTCALL
862 KiTrap09Handler(IN PKTRAP_FRAME TrapFrame)
863 {
864 /* Save trap frame */
865 KiEnterTrap(TrapFrame);
866
867 /* Enable interrupts and kill the system */
868 _enable();
869 KiSystemFatalException(EXCEPTION_NPX_OVERRUN, TrapFrame);
870 }
871
872 DECLSPEC_NORETURN
873 VOID
874 FASTCALL
875 KiTrap0AHandler(IN PKTRAP_FRAME TrapFrame)
876 {
877 /* Save trap frame */
878 KiEnterTrap(TrapFrame);
879
880 /* Check for VDM trap */
881 ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
882
883 /* Kill the system */
884 KiSystemFatalException(EXCEPTION_INVALID_TSS, TrapFrame);
885 }
886
887 DECLSPEC_NORETURN
888 VOID
889 FASTCALL
890 KiTrap0BHandler(IN PKTRAP_FRAME TrapFrame)
891 {
892 /* Save trap frame */
893 KiEnterTrap(TrapFrame);
894
895 /* FIXME: Kill the system */
896 UNIMPLEMENTED;
897 KiSystemFatalException(EXCEPTION_SEGMENT_NOT_PRESENT, TrapFrame);
898 }
899
900 DECLSPEC_NORETURN
901 VOID
902 FASTCALL
903 KiTrap0CHandler(IN PKTRAP_FRAME TrapFrame)
904 {
905 /* Save trap frame */
906 KiEnterTrap(TrapFrame);
907
908 /* FIXME: Kill the system */
909 UNIMPLEMENTED;
910 KiSystemFatalException(EXCEPTION_STACK_FAULT, TrapFrame);
911 }
912
913 DECLSPEC_NORETURN
914 VOID
915 FASTCALL
916 KiTrap0DHandler(IN PKTRAP_FRAME TrapFrame)
917 {
918 ULONG i, j, Iopl;
919 BOOLEAN Privileged = FALSE;
920 PUCHAR Instructions;
921 UCHAR Instruction = 0;
922 KIRQL OldIrql;
923
924 /* Check for V86 GPF */
925 if (__builtin_expect(KiV86Trap(TrapFrame), 1))
926 {
927 /* Enter V86 trap */
928 KiEnterV86Trap(TrapFrame);
929
930 /* Must be a VDM process */
931 if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects, 0))
932 {
933 /* Enable interrupts */
934 _enable();
935
936 /* Setup illegal instruction fault */
937 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
938 TrapFrame->Eip,
939 TrapFrame);
940 }
941
942 /* Go to APC level */
943 KeRaiseIrql(APC_LEVEL, &OldIrql);
944 _enable();
945
946 /* Handle the V86 opcode */
947 if (__builtin_expect(Ki386HandleOpcodeV86(TrapFrame) == 0xFF, 0))
948 {
949 /* Should only happen in VDM mode */
950 UNIMPLEMENTED_FATAL();
951 }
952
953 /* Bring IRQL back */
954 KeLowerIrql(OldIrql);
955 _disable();
956
957 /* Do a quick V86 exit if possible */
958 KiExitV86Trap(TrapFrame);
959 }
960
961 /* Save trap frame */
962 KiEnterTrap(TrapFrame);
963
964 /* Check for user-mode GPF */
965 if (KiUserTrap(TrapFrame))
966 {
967 /* Should not be VDM */
968 ASSERT(KiVdmTrap(TrapFrame) == FALSE);
969
970 /* Enable interrupts and check error code */
971 _enable();
972 if (!TrapFrame->ErrCode)
973 {
974 /* FIXME: Use SEH */
975 Instructions = (PUCHAR)TrapFrame->Eip;
976
977 /* Scan next 15 bytes */
978 for (i = 0; i < 15; i++)
979 {
980 /* Skip prefix instructions */
981 for (j = 0; j < sizeof(KiTrapPrefixTable); j++)
982 {
983 /* Is this a prefix instruction? */
984 if (Instructions[i] == KiTrapPrefixTable[j])
985 {
986 /* Stop looking */
987 break;
988 }
989 }
990
991 /* Is this NOT any prefix instruction? */
992 if (j == sizeof(KiTrapPrefixTable))
993 {
994 /* We can go ahead and handle the fault now */
995 Instruction = Instructions[i];
996 break;
997 }
998 }
999
1000 /* If all we found was prefixes, then this instruction is too long */
1001 if (i == 15)
1002 {
1003 /* Setup illegal instruction fault */
1004 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
1005 TrapFrame->Eip,
1006 TrapFrame);
1007 }
1008
1009 /* Check for privileged instructions */
1010 DPRINT("Instruction (%lu) at fault: %lx %lx %lx %lx\n",
1011 i,
1012 Instructions[i],
1013 Instructions[i + 1],
1014 Instructions[i + 2],
1015 Instructions[i + 3]);
1016 if (Instruction == 0xF4) // HLT
1017 {
1018 /* HLT is privileged */
1019 Privileged = TRUE;
1020 }
1021 else if (Instruction == 0x0F)
1022 {
1023 /* Test if it's any of the privileged two-byte opcodes */
1024 if (((Instructions[i + 1] == 0x00) && // LLDT or LTR
1025 (((Instructions[i + 2] & 0x38) == 0x10) || // LLDT
1026 (Instructions[i + 2] == 0x18))) || // LTR
1027 ((Instructions[i + 1] == 0x01) && // LGDT or LIDT or LMSW
1028 (((Instructions[i + 2] & 0x38) == 0x10) || // LGDT
1029 (Instructions[i + 2] == 0x18) || // LIDT
1030 (Instructions[i + 2] == 0x30))) || // LMSW
1031 (Instructions[i + 1] == 0x08) || // INVD
1032 (Instructions[i + 1] == 0x09) || // WBINVD
1033 (Instructions[i + 1] == 0x35) || // SYSEXIT
1034 (Instructions[i + 1] == 0x21) || // MOV DR, XXX
1035 (Instructions[i + 1] == 0x06) || // CLTS
1036 (Instructions[i + 1] == 0x20) || // MOV CR, XXX
1037 (Instructions[i + 1] == 0x22) || // MOV XXX, CR
1038 (Instructions[i + 1] == 0x23) || // MOV YYY, DR
1039 (Instructions[i + 1] == 0x30) || // WRMSR
1040 (Instructions[i + 1] == 0x33)) // RDPMC
1041 // INVLPG, INVLPGA, SYSRET
1042 {
1043 /* These are all privileged */
1044 Privileged = TRUE;
1045 }
1046 }
1047 else
1048 {
1049 /* Get the IOPL and compare with the RPL mask */
1050 Iopl = (TrapFrame->EFlags & EFLAGS_IOPL) >> 12;
1051 if ((TrapFrame->SegCs & RPL_MASK) > Iopl)
1052 {
1053 /* I/O privilege error -- check for known instructions */
1054 if ((Instruction == 0xFA) || (Instruction == 0xFB)) // CLI or STI
1055 {
1056 /* These are privileged */
1057 Privileged = TRUE;
1058 }
1059 else
1060 {
1061 /* Last hope: an IN/OUT instruction */
1062 for (j = 0; j < sizeof(KiTrapIoTable); j++)
1063 {
1064 /* Is this an I/O instruction? */
1065 if (Instruction == KiTrapIoTable[j])
1066 {
1067 /* Then it's privileged */
1068 Privileged = TRUE;
1069 break;
1070 }
1071 }
1072 }
1073 }
1074 }
1075
1076 /* So now... was the instruction privileged or not? */
1077 if (Privileged)
1078 {
1079 /* Whew! We have a privileged instruction, so dispatch the fault */
1080 KiDispatchException0Args(STATUS_PRIVILEGED_INSTRUCTION,
1081 TrapFrame->Eip,
1082 TrapFrame);
1083 }
1084 }
1085
1086 /* If we got here, send an access violation */
1087 KiDispatchException2Args(STATUS_ACCESS_VIOLATION,
1088 TrapFrame->Eip,
1089 0,
1090 0xFFFFFFFF,
1091 TrapFrame);
1092 }
1093
1094 /*
1095 * Check for a fault during checking of the user instruction.
1096 *
1097 * Note that the SEH handler will catch invalid EIP, but we could be dealing
1098 * with an invalid CS, which will generate another GPF instead.
1099 *
1100 */
1101 if (((PVOID)TrapFrame->Eip >= (PVOID)KiTrap0DHandler) &&
1102 ((PVOID)TrapFrame->Eip < (PVOID)KiTrap0DHandler))
1103 {
1104 /* Not implemented */
1105 UNIMPLEMENTED_FATAL();
1106 }
1107
1108 /*
1109 * NOTE: The ASM trap exit code would restore segment registers by doing
1110 * a POP <SEG>, which could cause an invalid segment if someone had messed
1111 * with the segment values.
1112 *
1113 * Another case is a bogus SS, which would hit a GPF when doing the iret.
1114 * This could only be done through a buggy or malicious driver, or perhaps
1115 * the kernel debugger.
1116 *
1117 * The kernel normally restores the "true" segment if this happens.
1118 *
1119 * However, since we're restoring in C, not ASM, we can't detect
1120 * POP <SEG> since the actual instructions will be different.
1121 *
1122 * A better technique would be to check the EIP and somehow edit the
1123 * trap frame before restarting the instruction -- but we would need to
1124 * know the extract instruction that was used first.
1125 *
1126 * We could force a special instrinsic to use stack instructions, or write
1127 * a simple instruction length checker.
1128 *
1129 * Nevertheless, this is a lot of work for the purpose of avoiding a crash
1130 * when the user is purposedly trying to create one from kernel-mode, so
1131 * we should probably table this for now since it's not a "real" issue.
1132 */
1133
1134 /*
1135 * NOTE2: Another scenario is the IRET during a V8086 restore (BIOS Call)
1136 * which will cause a GPF since the trap frame is a total mess (on purpose)
1137 * as built in KiEnterV86Mode.
1138 *
1139 * The idea is to scan for IRET, scan for the known EIP adress, validate CS
1140 * and then manually issue a jump to the V8086 return EIP.
1141 */
1142 Instructions = (PUCHAR)TrapFrame->Eip;
1143 if (Instructions[0] == 0xCF)
1144 {
1145 /*
1146 * Some evil shit is going on here -- this is not the SS:ESP you're
1147 * looking for! Instead, this is actually CS:EIP you're looking at!
1148 * Why? Because part of the trap frame actually corresponds to the IRET
1149 * stack during the trap exit!
1150 */
1151 if ((TrapFrame->HardwareEsp == (ULONG)Ki386BiosCallReturnAddress) &&
1152 (TrapFrame->HardwareSegSs == (KGDT_R0_CODE | RPL_MASK)))
1153 {
1154 /* Exit the V86 trap! */
1155 Ki386BiosCallReturnAddress(TrapFrame);
1156 }
1157 else
1158 {
1159 /* Otherwise, this is another kind of IRET fault */
1160 UNIMPLEMENTED_FATAL();
1161 }
1162 }
1163
1164 /* So since we're not dealing with the above case, check for RDMSR/WRMSR */
1165 if ((Instructions[0] == 0xF) && // 2-byte opcode
1166 ((Instructions[1] == 0x32) || // RDMSR
1167 (Instructions[1] == 0x30))) // WRMSR
1168 {
1169 /* Unknown CPU MSR, so raise an access violation */
1170 KiDispatchException0Args(STATUS_ACCESS_VIOLATION,
1171 TrapFrame->Eip,
1172 TrapFrame);
1173 }
1174
1175 /* Check for lazy segment load */
1176 if (TrapFrame->SegDs != (KGDT_R3_DATA | RPL_MASK))
1177 {
1178 /* Fix it */
1179 TrapFrame->SegDs = (KGDT_R3_DATA | RPL_MASK);
1180 }
1181 else if (TrapFrame->SegEs != (KGDT_R3_DATA | RPL_MASK))
1182 {
1183 /* Fix it */
1184 TrapFrame->SegEs = (KGDT_R3_DATA | RPL_MASK);
1185 }
1186 else
1187 {
1188 /* Whatever it is, we can't handle it */
1189 KiSystemFatalException(EXCEPTION_GP_FAULT, TrapFrame);
1190 }
1191
1192 /* Return to where we came from */
1193 KiTrapReturn(TrapFrame);
1194 }
1195
1196 DECLSPEC_NORETURN
1197 VOID
1198 FASTCALL
1199 KiTrap0EHandler(IN PKTRAP_FRAME TrapFrame)
1200 {
1201 PKTHREAD Thread;
1202 BOOLEAN Present;
1203 BOOLEAN StoreInstruction;
1204 ULONG_PTR Cr2;
1205 NTSTATUS Status;
1206
1207 /* Save trap frame */
1208 KiEnterTrap(TrapFrame);
1209
1210 /* Check if this is the base frame */
1211 Thread = KeGetCurrentThread();
1212 if (KeGetTrapFrame(Thread) != TrapFrame)
1213 {
1214 /* It isn't, check if this is a second nested frame */
1215 if (((ULONG_PTR)KeGetTrapFrame(Thread) - (ULONG_PTR)TrapFrame) <=
1216 FIELD_OFFSET(KTRAP_FRAME, EFlags))
1217 {
1218 /* The stack is somewhere in between frames, we need to fix it */
1219 UNIMPLEMENTED_FATAL();
1220 }
1221 }
1222
1223 /* Save CR2 */
1224 Cr2 = __readcr2();
1225
1226 /* Enable interrupts */
1227 _enable();
1228
1229 /* Interpret the error code */
1230 Present = (TrapFrame->ErrCode & 1) != 0;
1231 StoreInstruction = (TrapFrame->ErrCode & 2) != 0;
1232
1233 /* Check if we came in with interrupts disabled */
1234 if (!(TrapFrame->EFlags & EFLAGS_INTERRUPT_MASK))
1235 {
1236 /* This is completely illegal, bugcheck the system */
1237 KeBugCheckWithTf(IRQL_NOT_LESS_OR_EQUAL,
1238 Cr2,
1239 (ULONG_PTR)-1,
1240 StoreInstruction,
1241 TrapFrame->Eip,
1242 TrapFrame);
1243 }
1244
1245 /* Check for S-List fault
1246
1247 Explanation: An S-List fault can occur due to a race condition between 2
1248 threads simultaneously trying to pop an element from the S-List. After
1249 thread 1 has read the pointer to the top element on the S-List it is
1250 preempted and thread 2 calls InterlockedPopEntrySlist on the same S-List,
1251 removing the top element and freeing it's memory. After that thread 1
1252 resumes and tries to read the address of the Next pointer from the top
1253 element, which it assumes will be the next top element.
1254 But since that memory has been freed, we get a page fault. To handle this
1255 race condition, we let thread 1 repeat the operation.
1256 We do NOT invoke the page fault handler in this case, since we do not
1257 want to trigger any side effects, like paging or a guard page fault.
1258
1259 Sequence of operations:
1260
1261 Thread 1 : mov eax, [ebp] <= eax now points to the first element
1262 Thread 1 : mov edx, [ebp + 4] <= edx is loaded with Depth and Sequence
1263 *** preempted ***
1264 Thread 2 : calls InterlockedPopEntrySlist, changing the top element
1265 Thread 2 : frees the memory of the element that was popped
1266 *** preempted ***
1267 Thread 1 : checks if eax is NULL
1268 Thread 1 : InterlockedPopEntrySListFault: mov ebx, [eax] <= faults
1269
1270 To be sure that we are dealing with exactly the case described above, we
1271 check whether the ListHeader has changed. If Thread 2 only popped one
1272 entry, the Next field in the S-List-header has changed.
1273 If after thread 1 has faulted, thread 2 allocates a new element, by
1274 chance getting the same address as the previously freed element and
1275 pushes it on the list again, we will see the same top element, but the
1276 Sequence member of the S-List header has changed. Therefore we check
1277 both fields to make sure we catch any concurrent modification of the
1278 S-List-header.
1279 */
1280 if ((TrapFrame->Eip == (ULONG_PTR)ExpInterlockedPopEntrySListFault) ||
1281 (TrapFrame->Eip == (ULONG_PTR)KeUserPopEntrySListFault))
1282 {
1283 ULARGE_INTEGER SListHeader;
1284 PVOID ResumeAddress;
1285
1286 /* Sanity check that the assembly is correct:
1287 This must be mov ebx, [eax]
1288 Followed by cmpxchg8b [ebp] */
1289 ASSERT((((UCHAR*)TrapFrame->Eip)[0] == 0x8B) &&
1290 (((UCHAR*)TrapFrame->Eip)[1] == 0x18) &&
1291 (((UCHAR*)TrapFrame->Eip)[2] == 0x0F) &&
1292 (((UCHAR*)TrapFrame->Eip)[3] == 0xC7) &&
1293 (((UCHAR*)TrapFrame->Eip)[4] == 0x4D) &&
1294 (((UCHAR*)TrapFrame->Eip)[5] == 0x00));
1295
1296 /* Check if this is a user fault */
1297 if (TrapFrame->Eip == (ULONG_PTR)KeUserPopEntrySListFault)
1298 {
1299 /* EBP points to the S-List-header. Copy it inside SEH, to protect
1300 against a bogus pointer from user mode */
1301 _SEH2_TRY
1302 {
1303 ProbeForRead((PVOID)TrapFrame->Ebp,
1304 sizeof(ULARGE_INTEGER),
1305 TYPE_ALIGNMENT(SLIST_HEADER));
1306 SListHeader = *(PULARGE_INTEGER)TrapFrame->Ebp;
1307 }
1308 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1309 {
1310 /* The S-List pointer is not valid! */
1311 goto NotSListFault;
1312 }
1313 _SEH2_END;
1314 ResumeAddress = KeUserPopEntrySListResume;
1315 }
1316 else
1317 {
1318 SListHeader = *(PULARGE_INTEGER)TrapFrame->Ebp;
1319 ResumeAddress = ExpInterlockedPopEntrySListResume;
1320 }
1321
1322 /* Check if either the Next pointer or the Sequence member in the
1323 S-List-header has changed. If any of these has changed, we restart
1324 the operation. Otherwise we only have a bogus pointer and let the
1325 page fault handler deal with it. */
1326 if ((SListHeader.LowPart != TrapFrame->Eax) ||
1327 (SListHeader.HighPart != TrapFrame->Edx))
1328 {
1329 DPRINT1("*** Got an S-List-Fault ***\n");
1330 KeGetCurrentThread()->SListFaultCount++;
1331
1332 /* Restart the operation */
1333 TrapFrame->Eip = (ULONG_PTR)ResumeAddress;
1334
1335 /* Continue execution */
1336 KiEoiHelper(TrapFrame);
1337 }
1338 }
1339 NotSListFault:
1340
1341 /* Call the access fault handler */
1342 Status = MmAccessFault(Present,
1343 (PVOID)Cr2,
1344 KiUserTrap(TrapFrame),
1345 TrapFrame);
1346 if (NT_SUCCESS(Status))
1347 {
1348 #ifdef _WINKD_
1349 /* Check whether the kernel debugger has owed breakpoints to be inserted */
1350 KdSetOwedBreakpoints();
1351 #endif
1352 /* We succeeded, return */
1353 KiEoiHelper(TrapFrame);
1354 }
1355
1356 /* Check for syscall fault */
1357 #if 0
1358 if ((TrapFrame->Eip == (ULONG_PTR)CopyParams) ||
1359 (TrapFrame->Eip == (ULONG_PTR)ReadBatch))
1360 {
1361 /* Not yet implemented */
1362 UNIMPLEMENTED_FATAL();
1363 }
1364 #endif
1365
1366 /* Check for VDM trap */
1367 if (KiVdmTrap(TrapFrame))
1368 {
1369 DPRINT1("VDM PAGE FAULT at %lx:%lx for address %lx\n",
1370 TrapFrame->SegCs, TrapFrame->Eip, Cr2);
1371 if (VdmDispatchPageFault(TrapFrame))
1372 {
1373 /* Return and end VDM execution */
1374 DPRINT1("VDM page fault with status 0x%lx resolved\n", Status);
1375 KiEoiHelper(TrapFrame);
1376 }
1377 DPRINT1("VDM page fault with status 0x%lx NOT resolved\n", Status);
1378 }
1379
1380 /* Either kernel or user trap (non VDM) so dispatch exception */
1381 if (Status == STATUS_ACCESS_VIOLATION)
1382 {
1383 /* This status code is repurposed so we can recognize it later */
1384 KiDispatchException2Args(KI_EXCEPTION_ACCESS_VIOLATION,
1385 TrapFrame->Eip,
1386 StoreInstruction,
1387 Cr2,
1388 TrapFrame);
1389 }
1390 else if ((Status == STATUS_GUARD_PAGE_VIOLATION) ||
1391 (Status == STATUS_STACK_OVERFLOW))
1392 {
1393 /* These faults only have two parameters */
1394 KiDispatchException2Args(Status,
1395 TrapFrame->Eip,
1396 StoreInstruction,
1397 Cr2,
1398 TrapFrame);
1399 }
1400
1401 /* Only other choice is an in-page error, with 3 parameters */
1402 KiDispatchExceptionFromTrapFrame(STATUS_IN_PAGE_ERROR,
1403 0,
1404 TrapFrame->Eip,
1405 3,
1406 StoreInstruction,
1407 Cr2,
1408 Status,
1409 TrapFrame);
1410 }
1411
1412 DECLSPEC_NORETURN
1413 VOID
1414 FASTCALL
1415 KiTrap0FHandler(IN PKTRAP_FRAME TrapFrame)
1416 {
1417 /* Save trap frame */
1418 KiEnterTrap(TrapFrame);
1419
1420 /* FIXME: Kill the system */
1421 UNIMPLEMENTED;
1422 KiSystemFatalException(EXCEPTION_RESERVED_TRAP, TrapFrame);
1423 }
1424
1425 DECLSPEC_NORETURN
1426 VOID
1427 FASTCALL
1428 KiTrap10Handler(IN PKTRAP_FRAME TrapFrame)
1429 {
1430 PKTHREAD Thread;
1431 PFX_SAVE_AREA SaveArea;
1432
1433 /* Save trap frame */
1434 KiEnterTrap(TrapFrame);
1435
1436 /* Check if this is the NPX thrad */
1437 Thread = KeGetCurrentThread();
1438 SaveArea = KiGetThreadNpxArea(Thread);
1439 if (Thread != KeGetCurrentPrcb()->NpxThread)
1440 {
1441 /* It isn't, enable interrupts and set delayed error */
1442 _enable();
1443 SaveArea->Cr0NpxState |= CR0_TS;
1444
1445 /* End trap */
1446 KiEoiHelper(TrapFrame);
1447 }
1448
1449 /* Otherwise, proceed with NPX fault handling */
1450 KiNpxHandler(TrapFrame, Thread, SaveArea);
1451 }
1452
1453 DECLSPEC_NORETURN
1454 VOID
1455 FASTCALL
1456 KiTrap11Handler(IN PKTRAP_FRAME TrapFrame)
1457 {
1458 /* Save trap frame */
1459 KiEnterTrap(TrapFrame);
1460
1461 /* Enable interrupts and kill the system */
1462 _enable();
1463 KiSystemFatalException(EXCEPTION_ALIGNMENT_CHECK, TrapFrame);
1464 }
1465
1466 DECLSPEC_NORETURN
1467 VOID
1468 FASTCALL
1469 KiTrap13Handler(IN PKTRAP_FRAME TrapFrame)
1470 {
1471 PKTHREAD Thread;
1472 PFX_SAVE_AREA SaveArea;
1473 ULONG Cr0, MxCsrMask, Error;
1474
1475 /* Save trap frame */
1476 KiEnterTrap(TrapFrame);
1477
1478 /* Check if this is the NPX thrad */
1479 Thread = KeGetCurrentThread();
1480 if (Thread != KeGetCurrentPrcb()->NpxThread)
1481 {
1482 /* It isn't, kill the system */
1483 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, (ULONG_PTR)Thread, 0, 0, TrapFrame);
1484 }
1485
1486 /* Get the NPX frame */
1487 SaveArea = KiGetThreadNpxArea(Thread);
1488
1489 /* Check for VDM trap */
1490 ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
1491
1492 /* Check for user trap */
1493 if (!KiUserTrap(TrapFrame))
1494 {
1495 /* Kernel should not fault on XMMI */
1496 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 2, TrapFrame);
1497 }
1498
1499 /* Update CR0 */
1500 Cr0 = __readcr0();
1501 Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
1502 __writecr0(Cr0);
1503
1504 /* Save FPU state */
1505 Ke386SaveFpuState(SaveArea);
1506
1507 /* Mark CR0 state dirty */
1508 Cr0 |= NPX_STATE_NOT_LOADED;
1509 Cr0 |= SaveArea->Cr0NpxState;
1510 __writecr0(Cr0);
1511
1512 /* Update NPX state */
1513 Thread->NpxState = NPX_STATE_NOT_LOADED;
1514 KeGetCurrentPrcb()->NpxThread = NULL;
1515
1516 /* Clear the TS bit and re-enable interrupts */
1517 SaveArea->Cr0NpxState &= ~CR0_TS;
1518 _enable();
1519
1520 /* Now look at MxCsr to get the mask of errors we should care about */
1521 MxCsrMask = ~((USHORT)SaveArea->U.FxArea.MXCsr >> 7);
1522
1523 /* Get legal exceptions that software should handle */
1524 Error = (USHORT)SaveArea->U.FxArea.MXCsr & (FSW_INVALID_OPERATION |
1525 FSW_DENORMAL |
1526 FSW_ZERO_DIVIDE |
1527 FSW_OVERFLOW |
1528 FSW_UNDERFLOW |
1529 FSW_PRECISION);
1530 Error &= MxCsrMask;
1531
1532 /* Now handle any of those legal errors */
1533 if (Error & (FSW_INVALID_OPERATION |
1534 FSW_DENORMAL |
1535 FSW_ZERO_DIVIDE |
1536 FSW_OVERFLOW |
1537 FSW_UNDERFLOW |
1538 FSW_PRECISION))
1539 {
1540 /* By issuing an exception */
1541 KiDispatchException1Args(STATUS_FLOAT_MULTIPLE_TRAPS,
1542 TrapFrame->Eip,
1543 0,
1544 TrapFrame);
1545 }
1546
1547 /* Unknown XMMI fault */
1548 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame);
1549 }
1550
1551 /* SOFTWARE SERVICES **********************************************************/
1552
1553 VOID
1554 FASTCALL
1555 KiRaiseSecurityCheckFailureHandler(IN PKTRAP_FRAME TrapFrame)
1556 {
1557 /* Save trap frame */
1558 KiEnterTrap(TrapFrame);
1559
1560 /* Decrement EIP to point to the INT29 instruction (2 bytes, not 1 like INT3) */
1561 TrapFrame->Eip -= 2;
1562
1563 /* Check if this is a user trap */
1564 if (KiUserTrap(TrapFrame))
1565 {
1566 /* Dispatch exception to user mode */
1567 KiDispatchExceptionFromTrapFrame(STATUS_STACK_BUFFER_OVERRUN,
1568 EXCEPTION_NONCONTINUABLE,
1569 TrapFrame->Eip,
1570 1,
1571 TrapFrame->Ecx,
1572 0,
1573 0,
1574 TrapFrame);
1575 }
1576 else
1577 {
1578 EXCEPTION_RECORD ExceptionRecord;
1579
1580 /* Bugcheck the system */
1581 ExceptionRecord.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
1582 ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
1583 ExceptionRecord.ExceptionRecord = NULL;
1584 ExceptionRecord.ExceptionAddress = (PVOID)TrapFrame->Eip;
1585 ExceptionRecord.NumberParameters = 1;
1586 ExceptionRecord.ExceptionInformation[0] = TrapFrame->Ecx;
1587
1588 KeBugCheckWithTf(KERNEL_SECURITY_CHECK_FAILURE,
1589 TrapFrame->Ecx,
1590 (ULONG_PTR)TrapFrame,
1591 (ULONG_PTR)&ExceptionRecord,
1592 0,
1593 TrapFrame);
1594 }
1595 }
1596
1597 VOID
1598 FASTCALL
1599 KiGetTickCountHandler(IN PKTRAP_FRAME TrapFrame)
1600 {
1601 /* Save trap frame */
1602 KiEnterTrap(TrapFrame);
1603
1604 /*
1605 * Just fail the request
1606 */
1607 DbgPrint("INT 0x2A attempted, returning 0 tick count\n");
1608 TrapFrame->Eax = 0;
1609
1610 /* Exit the trap */
1611 KiEoiHelper(TrapFrame);
1612 }
1613
1614 VOID
1615 FASTCALL
1616 KiCallbackReturnHandler(IN PKTRAP_FRAME TrapFrame)
1617 {
1618 PKTHREAD Thread;
1619 NTSTATUS Status;
1620
1621 /* Save the SEH chain, NtCallbackReturn will restore this */
1622 TrapFrame->ExceptionList = KeGetPcr()->NtTib.ExceptionList;
1623
1624 /* Set thread fields */
1625 Thread = KeGetCurrentThread();
1626 Thread->TrapFrame = TrapFrame;
1627 Thread->PreviousMode = KiUserTrap(TrapFrame);
1628 ASSERT(Thread->PreviousMode != KernelMode);
1629
1630 /* Pass the register parameters to NtCallbackReturn.
1631 Result pointer is in ecx, result length in edx, status in eax */
1632 Status = NtCallbackReturn((PVOID)TrapFrame->Ecx,
1633 TrapFrame->Edx,
1634 TrapFrame->Eax);
1635
1636 /* If we got here, something went wrong. Return an error to the caller */
1637 KiServiceExit(TrapFrame, Status);
1638 }
1639
1640 DECLSPEC_NORETURN
1641 VOID
1642 FASTCALL
1643 KiRaiseAssertionHandler(IN PKTRAP_FRAME TrapFrame)
1644 {
1645 /* Save trap frame */
1646 KiEnterTrap(TrapFrame);
1647
1648 /* Decrement EIP to point to the INT2C instruction (2 bytes, not 1 like INT3) */
1649 TrapFrame->Eip -= 2;
1650
1651 /* Dispatch the exception */
1652 KiDispatchException0Args(STATUS_ASSERTION_FAILURE,
1653 TrapFrame->Eip,
1654 TrapFrame);
1655 }
1656
1657 DECLSPEC_NORETURN
1658 VOID
1659 FASTCALL
1660 KiDebugServiceHandler(IN PKTRAP_FRAME TrapFrame)
1661 {
1662 /* Save trap frame */
1663 KiEnterTrap(TrapFrame);
1664
1665 /* Increment EIP to skip the INT3 instruction */
1666 TrapFrame->Eip++;
1667
1668 /* Continue with the common handler */
1669 KiDebugHandler(TrapFrame, TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1670 }
1671
1672
1673 FORCEINLINE
1674 VOID
1675 KiDbgPreServiceHook(ULONG SystemCallNumber, PULONG_PTR Arguments)
1676 {
1677 #if DBG && !defined(_WINKD_)
1678 if (SystemCallNumber >= 0x1000 && KeWin32PreServiceHook)
1679 KeWin32PreServiceHook(SystemCallNumber, Arguments);
1680 #endif
1681 }
1682
1683 FORCEINLINE
1684 ULONG_PTR
1685 KiDbgPostServiceHook(ULONG SystemCallNumber, ULONG_PTR Result)
1686 {
1687 #if DBG && !defined(_WINKD_)
1688 if (SystemCallNumber >= 0x1000 && KeWin32PostServiceHook)
1689 return KeWin32PostServiceHook(SystemCallNumber, Result);
1690 #endif
1691 return Result;
1692 }
1693
1694 DECLSPEC_NORETURN
1695 VOID
1696 FASTCALL
1697 KiSystemServiceHandler(IN PKTRAP_FRAME TrapFrame,
1698 IN PVOID Arguments)
1699 {
1700 PKTHREAD Thread;
1701 PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
1702 ULONG Id, Offset, StackBytes;
1703 NTSTATUS Status;
1704 PVOID Handler;
1705 ULONG SystemCallNumber = TrapFrame->Eax;
1706
1707 /* Get the current thread */
1708 Thread = KeGetCurrentThread();
1709
1710 /* Set debug header */
1711 KiFillTrapFrameDebug(TrapFrame);
1712
1713 /* Chain trap frames */
1714 TrapFrame->Edx = (ULONG_PTR)Thread->TrapFrame;
1715
1716 /* No error code */
1717 TrapFrame->ErrCode = 0;
1718
1719 /* Save previous mode */
1720 TrapFrame->PreviousPreviousMode = Thread->PreviousMode;
1721
1722 /* Save the SEH chain and terminate it for now */
1723 TrapFrame->ExceptionList = KeGetPcr()->NtTib.ExceptionList;
1724 KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
1725
1726 /* Default to debugging disabled */
1727 TrapFrame->Dr7 = 0;
1728
1729 /* Check if the frame was from user mode */
1730 if (KiUserTrap(TrapFrame))
1731 {
1732 /* Check for active debugging */
1733 if (KeGetCurrentThread()->Header.DebugActive & 0xFF)
1734 {
1735 /* Handle debug registers */
1736 KiHandleDebugRegistersOnTrapEntry(TrapFrame);
1737 }
1738 }
1739
1740 /* Set thread fields */
1741 Thread->TrapFrame = TrapFrame;
1742 Thread->PreviousMode = KiUserTrap(TrapFrame);
1743
1744 /* Enable interrupts */
1745 _enable();
1746
1747 /* Decode the system call number */
1748 Offset = (SystemCallNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
1749 Id = SystemCallNumber & SERVICE_NUMBER_MASK;
1750
1751 /* Get descriptor table */
1752 DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
1753
1754 /* Validate the system call number */
1755 if (__builtin_expect(Id >= DescriptorTable->Limit, 0))
1756 {
1757 /* Check if this is a GUI call */
1758 if (!(Offset & SERVICE_TABLE_TEST))
1759 {
1760 /* Fail the call */
1761 Status = STATUS_INVALID_SYSTEM_SERVICE;
1762 goto ExitCall;
1763 }
1764
1765 /* Convert us to a GUI thread -- must wrap in ASM to get new EBP */
1766 Status = KiConvertToGuiThread();
1767
1768 /* Reload trap frame and descriptor table pointer from new stack */
1769 TrapFrame = *(volatile PVOID*)&Thread->TrapFrame;
1770 DescriptorTable = (PVOID)(*(volatile ULONG_PTR*)&Thread->ServiceTable + Offset);
1771
1772 if (!NT_SUCCESS(Status))
1773 {
1774 /* Set the last error and fail */
1775 goto ExitCall;
1776 }
1777
1778 /* Validate the system call number again */
1779 if (Id >= DescriptorTable->Limit)
1780 {
1781 /* Fail the call */
1782 Status = STATUS_INVALID_SYSTEM_SERVICE;
1783 goto ExitCall;
1784 }
1785 }
1786
1787 /* Check if this is a GUI call */
1788 if (__builtin_expect(Offset & SERVICE_TABLE_TEST, 0))
1789 {
1790 /* Get the batch count and flush if necessary */
1791 if (NtCurrentTeb()->GdiBatchCount) KeGdiFlushUserBatch();
1792 }
1793
1794 /* Increase system call count */
1795 KeGetCurrentPrcb()->KeSystemCalls++;
1796
1797 /* FIXME: Increase individual counts on debug systems */
1798 //KiIncreaseSystemCallCount(DescriptorTable, Id);
1799
1800 /* Get stack bytes */
1801 StackBytes = DescriptorTable->Number[Id];
1802
1803 /* Probe caller stack */
1804 if (__builtin_expect((Arguments < (PVOID)MmUserProbeAddress) && !(KiUserTrap(TrapFrame)), 0))
1805 {
1806 /* Access violation */
1807 UNIMPLEMENTED_FATAL();
1808 }
1809
1810 /* Call pre-service debug hook */
1811 KiDbgPreServiceHook(SystemCallNumber, Arguments);
1812
1813 /* Get the handler and make the system call */
1814 Handler = (PVOID)DescriptorTable->Base[Id];
1815 Status = KiSystemCallTrampoline(Handler, Arguments, StackBytes);
1816
1817 /* Call post-service debug hook */
1818 Status = KiDbgPostServiceHook(SystemCallNumber, Status);
1819
1820 /* Make sure we're exiting correctly */
1821 KiExitSystemCallDebugChecks(Id, TrapFrame);
1822
1823 /* Restore the old trap frame */
1824 ExitCall:
1825 Thread->TrapFrame = (PKTRAP_FRAME)TrapFrame->Edx;
1826
1827 /* Exit from system call */
1828 KiServiceExit(TrapFrame, Status);
1829 }
1830
1831 VOID
1832 FASTCALL
1833 KiCheckForSListAddress(IN PKTRAP_FRAME TrapFrame)
1834 {
1835 UNIMPLEMENTED;
1836 }
1837
1838 /*
1839 * @implemented
1840 */
1841 VOID
1842 NTAPI
1843 Kei386EoiHelper(VOID)
1844 {
1845 /* We should never see this call happening */
1846 KeBugCheck(MISMATCHED_HAL);
1847 }
1848
1849 /* EOF */