[NTOS:KE][SDK:ASM] Code formatting/styling only.
[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 /* Get the current TSS, thread, and process */
481 Tss = PCR->TSS;
482 Thread = ((PKIPCR)PCR)->PrcbData.CurrentThread;
483 Process = Thread->ApcState.Process;
484
485 /* Save data usually not present in the TSS */
486 Tss->CR3 = Process->DirectoryTableBase[0];
487 Tss->IoMapBase = Process->IopmOffset;
488 Tss->LDT = Process->LdtDescriptor.LimitLow ? KGDT_LDT : 0;
489
490 /* Now get the base address of the NMI TSS */
491 TssGdt = &((PKIPCR)KeGetPcr())->GDT[KGDT_NMI_TSS / sizeof(KGDTENTRY)];
492 NmiTss = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
493 TssGdt->HighWord.Bytes.BaseMid << 16 |
494 TssGdt->HighWord.Bytes.BaseHi << 24);
495
496 /*
497 * Switch to it and activate it, masking off the nested flag.
498 *
499 * Note that in reality, we are already on the NMI TSS -- we just
500 * need to update the PCR to reflect this.
501 */
502 PCR->TSS = NmiTss;
503 __writeeflags(__readeflags() &~ EFLAGS_NESTED_TASK);
504 TssGdt->HighWord.Bits.Dpl = 0;
505 TssGdt->HighWord.Bits.Pres = 1;
506 TssGdt->HighWord.Bits.Type = I386_TSS;
507
508 /*
509 * Now build the trap frame based on the original TSS.
510 *
511 * The CPU does a hardware "Context switch" / task switch of sorts
512 * and so it takes care of saving our context in the normal TSS.
513 *
514 * We just have to go get the values...
515 */
516 RtlZeroMemory(&TrapFrame, sizeof(KTRAP_FRAME));
517 TrapFrame.HardwareSegSs = Tss->Ss0;
518 TrapFrame.HardwareEsp = Tss->Esp0;
519 TrapFrame.EFlags = Tss->EFlags;
520 TrapFrame.SegCs = Tss->Cs;
521 TrapFrame.Eip = Tss->Eip;
522 TrapFrame.Ebp = Tss->Ebp;
523 TrapFrame.Ebx = Tss->Ebx;
524 TrapFrame.Esi = Tss->Esi;
525 TrapFrame.Edi = Tss->Edi;
526 TrapFrame.SegFs = Tss->Fs;
527 TrapFrame.ExceptionList = PCR->NtTib.ExceptionList;
528 TrapFrame.PreviousPreviousMode = (ULONG)-1;
529 TrapFrame.Eax = Tss->Eax;
530 TrapFrame.Ecx = Tss->Ecx;
531 TrapFrame.Edx = Tss->Edx;
532 TrapFrame.SegDs = Tss->Ds;
533 TrapFrame.SegEs = Tss->Es;
534 TrapFrame.SegGs = Tss->Gs;
535 TrapFrame.DbgEip = Tss->Eip;
536 TrapFrame.DbgEbp = Tss->Ebp;
537
538 /* Store the trap frame in the KPRCB */
539 KiSaveProcessorState(&TrapFrame, NULL);
540
541 /* Call any registered NMI handlers and see if they handled it or not */
542 if (!KiHandleNmi())
543 {
544 /*
545 * They did not, so call the platform HAL routine to bugcheck the system
546 *
547 * Make sure the HAL believes it's running at HIGH IRQL... we can't use
548 * the normal APIs here as playing with the IRQL could change the system
549 * state.
550 */
551 OldIrql = PCR->Irql;
552 PCR->Irql = HIGH_LEVEL;
553 HalHandleNMI(NULL);
554 PCR->Irql = OldIrql;
555 }
556
557 /*
558 * Although the CPU disabled NMIs, we just did a BIOS call, which could've
559 * totally changed things.
560 *
561 * We have to make sure we're still in our original NMI -- a nested NMI
562 * will point back to the NMI TSS, and in that case we're hosed.
563 */
564 if (PCR->TSS->Backlink != KGDT_NMI_TSS)
565 {
566 /* Restore original TSS */
567 PCR->TSS = Tss;
568
569 /* Set it back to busy */
570 TssGdt->HighWord.Bits.Dpl = 0;
571 TssGdt->HighWord.Bits.Pres = 1;
572 TssGdt->HighWord.Bits.Type = I386_ACTIVE_TSS;
573
574 /* Restore nested flag */
575 __writeeflags(__readeflags() | EFLAGS_NESTED_TASK);
576
577 /* Handled, return from interrupt */
578 KiIret();
579 }
580
581 /* Unhandled: crash the system */
582 KiSystemFatalException(EXCEPTION_NMI, NULL);
583 }
584
585 DECLSPEC_NORETURN
586 VOID
587 FASTCALL
588 KiTrap03Handler(IN PKTRAP_FRAME TrapFrame)
589 {
590 /* Save trap frame */
591 KiEnterTrap(TrapFrame);
592
593 /* Continue with the common handler */
594 KiDebugHandler(TrapFrame, BREAKPOINT_BREAK, 0, 0);
595 }
596
597 DECLSPEC_NORETURN
598 VOID
599 FASTCALL
600 KiTrap04Handler(IN PKTRAP_FRAME TrapFrame)
601 {
602 /* Save trap frame */
603 KiEnterTrap(TrapFrame);
604
605 /* Check for VDM trap */
606 ASSERT(KiVdmTrap(TrapFrame) == FALSE);
607
608 /* Enable interrupts */
609 _enable();
610
611 /* Dispatch the exception */
612 KiDispatchException0Args(STATUS_INTEGER_OVERFLOW,
613 TrapFrame->Eip - 1,
614 TrapFrame);
615 }
616
617 DECLSPEC_NORETURN
618 VOID
619 FASTCALL
620 KiTrap05Handler(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 /* Check for kernel-mode fault */
629 if (!KiUserTrap(TrapFrame)) KiSystemFatalException(EXCEPTION_BOUND_CHECK, TrapFrame);
630
631 /* Enable interrupts */
632 _enable();
633
634 /* Dispatch the exception */
635 KiDispatchException0Args(STATUS_ARRAY_BOUNDS_EXCEEDED,
636 TrapFrame->Eip,
637 TrapFrame);
638 }
639
640 DECLSPEC_NORETURN
641 VOID
642 FASTCALL
643 KiTrap06Handler(IN PKTRAP_FRAME TrapFrame)
644 {
645 PUCHAR Instruction;
646 ULONG i;
647 KIRQL OldIrql;
648
649 /* Check for V86 GPF */
650 if (__builtin_expect(KiV86Trap(TrapFrame), 1))
651 {
652 /* Enter V86 trap */
653 KiEnterV86Trap(TrapFrame);
654
655 /* Must be a VDM process */
656 if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects, 0))
657 {
658 /* Enable interrupts */
659 _enable();
660
661 /* Setup illegal instruction fault */
662 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
663 TrapFrame->Eip,
664 TrapFrame);
665 }
666
667 /* Go to APC level */
668 KeRaiseIrql(APC_LEVEL, &OldIrql);
669 _enable();
670
671 /* Check for BOP */
672 if (!VdmDispatchBop(TrapFrame))
673 {
674 /* Should only happen in VDM mode */
675 UNIMPLEMENTED_FATAL();
676 }
677
678 /* Bring IRQL back */
679 KeLowerIrql(OldIrql);
680 _disable();
681
682 /* Do a quick V86 exit if possible */
683 KiExitV86Trap(TrapFrame);
684 }
685
686 /* Save trap frame */
687 KiEnterTrap(TrapFrame);
688
689 /* Enable interrupts */
690 Instruction = (PUCHAR)TrapFrame->Eip;
691 _enable();
692
693 /* Check for user trap */
694 if (KiUserTrap(TrapFrame))
695 {
696 /* FIXME: Use SEH */
697
698 /* Scan next 4 opcodes */
699 for (i = 0; i < 4; i++)
700 {
701 /* Check for LOCK instruction */
702 if (Instruction[i] == 0xF0)
703 {
704 /* Send invalid lock sequence exception */
705 KiDispatchException0Args(STATUS_INVALID_LOCK_SEQUENCE,
706 TrapFrame->Eip,
707 TrapFrame);
708 }
709 }
710
711 /* FIXME: SEH ends here */
712 }
713
714 /* Kernel-mode or user-mode fault (but not LOCK) */
715 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
716 TrapFrame->Eip,
717 TrapFrame);
718
719 }
720
721 DECLSPEC_NORETURN
722 VOID
723 FASTCALL
724 KiTrap07Handler(IN PKTRAP_FRAME TrapFrame)
725 {
726 PKTHREAD Thread, NpxThread;
727 PFX_SAVE_AREA SaveArea, NpxSaveArea;
728 ULONG Cr0;
729
730 /* Save trap frame */
731 KiEnterTrap(TrapFrame);
732
733 /* Try to handle NPX delay load */
734 for (;;)
735 {
736 /* Get the current thread */
737 Thread = KeGetCurrentThread();
738
739 /* Get the NPX frame */
740 SaveArea = KiGetThreadNpxArea(Thread);
741
742 /* Check if emulation is enabled */
743 if (SaveArea->Cr0NpxState & CR0_EM)
744 {
745 /* Not implemented */
746 UNIMPLEMENTED_FATAL();
747 }
748
749 /* Save CR0 and check NPX state */
750 Cr0 = __readcr0();
751 if (Thread->NpxState != NPX_STATE_LOADED)
752 {
753 /* Update CR0 */
754 Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
755 __writecr0(Cr0);
756
757 /* Get the NPX thread */
758 NpxThread = KeGetCurrentPrcb()->NpxThread;
759 if (NpxThread)
760 {
761 /* Get the NPX frame */
762 NpxSaveArea = KiGetThreadNpxArea(NpxThread);
763
764 /* Save FPU state */
765 Ke386SaveFpuState(NpxSaveArea);
766
767 /* Update NPX state */
768 NpxThread->NpxState = NPX_STATE_NOT_LOADED;
769 }
770
771 /* Load FPU state */
772 Ke386LoadFpuState(SaveArea);
773
774 /* Update NPX state */
775 Thread->NpxState = NPX_STATE_LOADED;
776 KeGetCurrentPrcb()->NpxThread = Thread;
777
778 /* Enable interrupts */
779 _enable();
780
781 /* Check if CR0 needs to be reloaded due to context switch */
782 if (!SaveArea->Cr0NpxState) KiEoiHelper(TrapFrame);
783
784 /* Otherwise, we need to reload CR0, disable interrupts */
785 _disable();
786
787 /* Reload CR0 */
788 Cr0 = __readcr0();
789 Cr0 |= SaveArea->Cr0NpxState;
790 __writecr0(Cr0);
791
792 /* Now restore interrupts and check for TS */
793 _enable();
794 if (Cr0 & CR0_TS) KiEoiHelper(TrapFrame);
795
796 /* We're still here -- clear TS and try again */
797 __writecr0(__readcr0() &~ CR0_TS);
798 _disable();
799 }
800 else
801 {
802 /* This is an actual fault, not a lack of FPU state */
803 break;
804 }
805 }
806
807 /* TS should not be set */
808 if (Cr0 & CR0_TS)
809 {
810 /*
811 * If it's incorrectly set, then maybe the state is actually still valid
812 * but we could have lost track of that due to a BIOS call.
813 * Make sure MP is still set, which should verify the theory.
814 */
815 if (Cr0 & CR0_MP)
816 {
817 /* Indeed, the state is actually still valid, so clear TS */
818 __writecr0(__readcr0() &~ CR0_TS);
819 KiEoiHelper(TrapFrame);
820 }
821
822 /* Otherwise, something strange is going on */
823 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 2, Cr0, 0, 0, TrapFrame);
824 }
825
826 /* It's not a delayed load, so process this trap as an NPX fault */
827 KiNpxHandler(TrapFrame, Thread, SaveArea);
828 }
829
830 DECLSPEC_NORETURN
831 VOID
832 FASTCALL
833 KiTrap08Handler(IN PKTRAP_FRAME TrapFrame)
834 {
835 /* FIXME: Not handled */
836 KiSystemFatalException(EXCEPTION_DOUBLE_FAULT, TrapFrame);
837 }
838
839 DECLSPEC_NORETURN
840 VOID
841 FASTCALL
842 KiTrap09Handler(IN PKTRAP_FRAME TrapFrame)
843 {
844 /* Save trap frame */
845 KiEnterTrap(TrapFrame);
846
847 /* Enable interrupts and kill the system */
848 _enable();
849 KiSystemFatalException(EXCEPTION_NPX_OVERRUN, TrapFrame);
850 }
851
852 DECLSPEC_NORETURN
853 VOID
854 FASTCALL
855 KiTrap0AHandler(IN PKTRAP_FRAME TrapFrame)
856 {
857 /* Save trap frame */
858 KiEnterTrap(TrapFrame);
859
860 /* Check for VDM trap */
861 ASSERT(KiVdmTrap(TrapFrame) == FALSE);
862
863 /* Kill the system */
864 KiSystemFatalException(EXCEPTION_INVALID_TSS, TrapFrame);
865 }
866
867 DECLSPEC_NORETURN
868 VOID
869 FASTCALL
870 KiTrap0BHandler(IN PKTRAP_FRAME TrapFrame)
871 {
872 /* Save trap frame */
873 KiEnterTrap(TrapFrame);
874
875 /* FIXME: Kill the system */
876 UNIMPLEMENTED;
877 KiSystemFatalException(EXCEPTION_SEGMENT_NOT_PRESENT, TrapFrame);
878 }
879
880 DECLSPEC_NORETURN
881 VOID
882 FASTCALL
883 KiTrap0CHandler(IN PKTRAP_FRAME TrapFrame)
884 {
885 /* Save trap frame */
886 KiEnterTrap(TrapFrame);
887
888 /* FIXME: Kill the system */
889 UNIMPLEMENTED;
890 KiSystemFatalException(EXCEPTION_STACK_FAULT, TrapFrame);
891 }
892
893 DECLSPEC_NORETURN
894 VOID
895 FASTCALL
896 KiTrap0DHandler(IN PKTRAP_FRAME TrapFrame)
897 {
898 ULONG i, j, Iopl;
899 BOOLEAN Privileged = FALSE;
900 PUCHAR Instructions;
901 UCHAR Instruction = 0;
902 KIRQL OldIrql;
903
904 /* Check for V86 GPF */
905 if (__builtin_expect(KiV86Trap(TrapFrame), 1))
906 {
907 /* Enter V86 trap */
908 KiEnterV86Trap(TrapFrame);
909
910 /* Must be a VDM process */
911 if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects, 0))
912 {
913 /* Enable interrupts */
914 _enable();
915
916 /* Setup illegal instruction fault */
917 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
918 TrapFrame->Eip,
919 TrapFrame);
920 }
921
922 /* Go to APC level */
923 KeRaiseIrql(APC_LEVEL, &OldIrql);
924 _enable();
925
926 /* Handle the V86 opcode */
927 if (__builtin_expect(Ki386HandleOpcodeV86(TrapFrame) == 0xFF, 0))
928 {
929 /* Should only happen in VDM mode */
930 UNIMPLEMENTED_FATAL();
931 }
932
933 /* Bring IRQL back */
934 KeLowerIrql(OldIrql);
935 _disable();
936
937 /* Do a quick V86 exit if possible */
938 KiExitV86Trap(TrapFrame);
939 }
940
941 /* Save trap frame */
942 KiEnterTrap(TrapFrame);
943
944 /* Check for user-mode GPF */
945 if (KiUserTrap(TrapFrame))
946 {
947 /* Should not be VDM */
948 ASSERT(KiVdmTrap(TrapFrame) == FALSE);
949
950 /* Enable interrupts and check error code */
951 _enable();
952 if (!TrapFrame->ErrCode)
953 {
954 /* FIXME: Use SEH */
955 Instructions = (PUCHAR)TrapFrame->Eip;
956
957 /* Scan next 15 bytes */
958 for (i = 0; i < 15; i++)
959 {
960 /* Skip prefix instructions */
961 for (j = 0; j < sizeof(KiTrapPrefixTable); j++)
962 {
963 /* Is this a prefix instruction? */
964 if (Instructions[i] == KiTrapPrefixTable[j])
965 {
966 /* Stop looking */
967 break;
968 }
969 }
970
971 /* Is this NOT any prefix instruction? */
972 if (j == sizeof(KiTrapPrefixTable))
973 {
974 /* We can go ahead and handle the fault now */
975 Instruction = Instructions[i];
976 break;
977 }
978 }
979
980 /* If all we found was prefixes, then this instruction is too long */
981 if (i == 15)
982 {
983 /* Setup illegal instruction fault */
984 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
985 TrapFrame->Eip,
986 TrapFrame);
987 }
988
989 /* Check for privileged instructions */
990 DPRINT("Instruction (%lu) at fault: %lx %lx %lx %lx\n",
991 i,
992 Instructions[i],
993 Instructions[i + 1],
994 Instructions[i + 2],
995 Instructions[i + 3]);
996 if (Instruction == 0xF4) // HLT
997 {
998 /* HLT is privileged */
999 Privileged = TRUE;
1000 }
1001 else if (Instruction == 0x0F)
1002 {
1003 /* Test if it's any of the privileged two-byte opcodes */
1004 if (((Instructions[i + 1] == 0x00) && // LLDT or LTR
1005 (((Instructions[i + 2] & 0x38) == 0x10) || // LLDT
1006 (Instructions[i + 2] == 0x18))) || // LTR
1007 ((Instructions[i + 1] == 0x01) && // LGDT or LIDT or LMSW
1008 (((Instructions[i + 2] & 0x38) == 0x10) || // LGDT
1009 (Instructions[i + 2] == 0x18) || // LIDT
1010 (Instructions[i + 2] == 0x30))) || // LMSW
1011 (Instructions[i + 1] == 0x08) || // INVD
1012 (Instructions[i + 1] == 0x09) || // WBINVD
1013 (Instructions[i + 1] == 0x35) || // SYSEXIT
1014 (Instructions[i + 1] == 0x21) || // MOV DR, XXX
1015 (Instructions[i + 1] == 0x06) || // CLTS
1016 (Instructions[i + 1] == 0x20) || // MOV CR, XXX
1017 (Instructions[i + 1] == 0x22) || // MOV XXX, CR
1018 (Instructions[i + 1] == 0x23) || // MOV YYY, DR
1019 (Instructions[i + 1] == 0x30) || // WRMSR
1020 (Instructions[i + 1] == 0x33)) // RDPMC
1021 // INVLPG, INVLPGA, SYSRET
1022 {
1023 /* These are all privileged */
1024 Privileged = TRUE;
1025 }
1026 }
1027 else
1028 {
1029 /* Get the IOPL and compare with the RPL mask */
1030 Iopl = (TrapFrame->EFlags & EFLAGS_IOPL) >> 12;
1031 if ((TrapFrame->SegCs & RPL_MASK) > Iopl)
1032 {
1033 /* I/O privilege error -- check for known instructions */
1034 if ((Instruction == 0xFA) || (Instruction == 0xFB)) // CLI or STI
1035 {
1036 /* These are privileged */
1037 Privileged = TRUE;
1038 }
1039 else
1040 {
1041 /* Last hope: an IN/OUT instruction */
1042 for (j = 0; j < sizeof(KiTrapIoTable); j++)
1043 {
1044 /* Is this an I/O instruction? */
1045 if (Instruction == KiTrapIoTable[j])
1046 {
1047 /* Then it's privileged */
1048 Privileged = TRUE;
1049 break;
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1056 /* So now... was the instruction privileged or not? */
1057 if (Privileged)
1058 {
1059 /* Whew! We have a privileged instruction, so dispatch the fault */
1060 KiDispatchException0Args(STATUS_PRIVILEGED_INSTRUCTION,
1061 TrapFrame->Eip,
1062 TrapFrame);
1063 }
1064 }
1065
1066 /* If we got here, send an access violation */
1067 KiDispatchException2Args(STATUS_ACCESS_VIOLATION,
1068 TrapFrame->Eip,
1069 0,
1070 0xFFFFFFFF,
1071 TrapFrame);
1072 }
1073
1074 /*
1075 * Check for a fault during checking of the user instruction.
1076 *
1077 * Note that the SEH handler will catch invalid EIP, but we could be dealing
1078 * with an invalid CS, which will generate another GPF instead.
1079 *
1080 */
1081 if (((PVOID)TrapFrame->Eip >= (PVOID)KiTrap0DHandler) &&
1082 ((PVOID)TrapFrame->Eip < (PVOID)KiTrap0DHandler))
1083 {
1084 /* Not implemented */
1085 UNIMPLEMENTED_FATAL();
1086 }
1087
1088 /*
1089 * NOTE: The ASM trap exit code would restore segment registers by doing
1090 * a POP <SEG>, which could cause an invalid segment if someone had messed
1091 * with the segment values.
1092 *
1093 * Another case is a bogus SS, which would hit a GPF when doing the iret.
1094 * This could only be done through a buggy or malicious driver, or perhaps
1095 * the kernel debugger.
1096 *
1097 * The kernel normally restores the "true" segment if this happens.
1098 *
1099 * However, since we're restoring in C, not ASM, we can't detect
1100 * POP <SEG> since the actual instructions will be different.
1101 *
1102 * A better technique would be to check the EIP and somehow edit the
1103 * trap frame before restarting the instruction -- but we would need to
1104 * know the extract instruction that was used first.
1105 *
1106 * We could force a special instrinsic to use stack instructions, or write
1107 * a simple instruction length checker.
1108 *
1109 * Nevertheless, this is a lot of work for the purpose of avoiding a crash
1110 * when the user is purposedly trying to create one from kernel-mode, so
1111 * we should probably table this for now since it's not a "real" issue.
1112 */
1113
1114 /*
1115 * NOTE2: Another scenario is the IRET during a V8086 restore (BIOS Call)
1116 * which will cause a GPF since the trap frame is a total mess (on purpose)
1117 * as built in KiEnterV86Mode.
1118 *
1119 * The idea is to scan for IRET, scan for the known EIP adress, validate CS
1120 * and then manually issue a jump to the V8086 return EIP.
1121 */
1122 Instructions = (PUCHAR)TrapFrame->Eip;
1123 if (Instructions[0] == 0xCF)
1124 {
1125 /*
1126 * Some evil shit is going on here -- this is not the SS:ESP you're
1127 * looking for! Instead, this is actually CS:EIP you're looking at!
1128 * Why? Because part of the trap frame actually corresponds to the IRET
1129 * stack during the trap exit!
1130 */
1131 if ((TrapFrame->HardwareEsp == (ULONG)Ki386BiosCallReturnAddress) &&
1132 (TrapFrame->HardwareSegSs == (KGDT_R0_CODE | RPL_MASK)))
1133 {
1134 /* Exit the V86 trap! */
1135 Ki386BiosCallReturnAddress(TrapFrame);
1136 }
1137 else
1138 {
1139 /* Otherwise, this is another kind of IRET fault */
1140 UNIMPLEMENTED_FATAL();
1141 }
1142 }
1143
1144 /* So since we're not dealing with the above case, check for RDMSR/WRMSR */
1145 if ((Instructions[0] == 0xF) && // 2-byte opcode
1146 ((Instructions[1] == 0x32) || // RDMSR
1147 (Instructions[1] == 0x30))) // WRMSR
1148 {
1149 /* Unknown CPU MSR, so raise an access violation */
1150 KiDispatchException0Args(STATUS_ACCESS_VIOLATION,
1151 TrapFrame->Eip,
1152 TrapFrame);
1153 }
1154
1155 /* Check for lazy segment load */
1156 if (TrapFrame->SegDs != (KGDT_R3_DATA | RPL_MASK))
1157 {
1158 /* Fix it */
1159 TrapFrame->SegDs = (KGDT_R3_DATA | RPL_MASK);
1160 }
1161 else if (TrapFrame->SegEs != (KGDT_R3_DATA | RPL_MASK))
1162 {
1163 /* Fix it */
1164 TrapFrame->SegEs = (KGDT_R3_DATA | RPL_MASK);
1165 }
1166 else
1167 {
1168 /* Whatever it is, we can't handle it */
1169 KiSystemFatalException(EXCEPTION_GP_FAULT, TrapFrame);
1170 }
1171
1172 /* Return to where we came from */
1173 KiTrapReturn(TrapFrame);
1174 }
1175
1176 DECLSPEC_NORETURN
1177 VOID
1178 FASTCALL
1179 KiTrap0EHandler(IN PKTRAP_FRAME TrapFrame)
1180 {
1181 PKTHREAD Thread;
1182 BOOLEAN StoreInstruction;
1183 ULONG_PTR Cr2;
1184 NTSTATUS Status;
1185
1186 /* Save trap frame */
1187 KiEnterTrap(TrapFrame);
1188
1189 /* Check if this is the base frame */
1190 Thread = KeGetCurrentThread();
1191 if (KeGetTrapFrame(Thread) != TrapFrame)
1192 {
1193 /* It isn't, check if this is a second nested frame */
1194 if (((ULONG_PTR)KeGetTrapFrame(Thread) - (ULONG_PTR)TrapFrame) <=
1195 FIELD_OFFSET(KTRAP_FRAME, EFlags))
1196 {
1197 /* The stack is somewhere in between frames, we need to fix it */
1198 UNIMPLEMENTED_FATAL();
1199 }
1200 }
1201
1202 /* Save CR2 */
1203 Cr2 = __readcr2();
1204
1205 /* Enable interrupts */
1206 _enable();
1207
1208 /* Interpret the error code */
1209 StoreInstruction = (TrapFrame->ErrCode & 2) != 0;
1210
1211 /* Check if we came in with interrupts disabled */
1212 if (!(TrapFrame->EFlags & EFLAGS_INTERRUPT_MASK))
1213 {
1214 /* This is completely illegal, bugcheck the system */
1215 KeBugCheckWithTf(IRQL_NOT_LESS_OR_EQUAL,
1216 Cr2,
1217 (ULONG_PTR)-1,
1218 TrapFrame->ErrCode,
1219 TrapFrame->Eip,
1220 TrapFrame);
1221 }
1222
1223 /* Check for S-List fault
1224
1225 Explanation: An S-List fault can occur due to a race condition between 2
1226 threads simultaneously trying to pop an element from the S-List. After
1227 thread 1 has read the pointer to the top element on the S-List it is
1228 preempted and thread 2 calls InterlockedPopEntrySlist on the same S-List,
1229 removing the top element and freeing it's memory. After that thread 1
1230 resumes and tries to read the address of the Next pointer from the top
1231 element, which it assumes will be the next top element.
1232 But since that memory has been freed, we get a page fault. To handle this
1233 race condition, we let thread 1 repeat the operation.
1234 We do NOT invoke the page fault handler in this case, since we do not
1235 want to trigger any side effects, like paging or a guard page fault.
1236
1237 Sequence of operations:
1238
1239 Thread 1 : mov eax, [ebp] <= eax now points to the first element
1240 Thread 1 : mov edx, [ebp + 4] <= edx is loaded with Depth and Sequence
1241 *** preempted ***
1242 Thread 2 : calls InterlockedPopEntrySlist, changing the top element
1243 Thread 2 : frees the memory of the element that was popped
1244 *** preempted ***
1245 Thread 1 : checks if eax is NULL
1246 Thread 1 : InterlockedPopEntrySListFault: mov ebx, [eax] <= faults
1247
1248 To be sure that we are dealing with exactly the case described above, we
1249 check whether the ListHeader has changed. If Thread 2 only popped one
1250 entry, the Next field in the S-List-header has changed.
1251 If after thread 1 has faulted, thread 2 allocates a new element, by
1252 chance getting the same address as the previously freed element and
1253 pushes it on the list again, we will see the same top element, but the
1254 Sequence member of the S-List header has changed. Therefore we check
1255 both fields to make sure we catch any concurrent modification of the
1256 S-List-header.
1257 */
1258 if ((TrapFrame->Eip == (ULONG_PTR)ExpInterlockedPopEntrySListFault) ||
1259 (TrapFrame->Eip == (ULONG_PTR)KeUserPopEntrySListFault))
1260 {
1261 ULARGE_INTEGER SListHeader;
1262 PVOID ResumeAddress;
1263
1264 /* Sanity check that the assembly is correct:
1265 This must be mov ebx, [eax]
1266 Followed by cmpxchg8b [ebp] */
1267 ASSERT((((UCHAR*)TrapFrame->Eip)[0] == 0x8B) &&
1268 (((UCHAR*)TrapFrame->Eip)[1] == 0x18) &&
1269 (((UCHAR*)TrapFrame->Eip)[2] == 0x0F) &&
1270 (((UCHAR*)TrapFrame->Eip)[3] == 0xC7) &&
1271 (((UCHAR*)TrapFrame->Eip)[4] == 0x4D) &&
1272 (((UCHAR*)TrapFrame->Eip)[5] == 0x00));
1273
1274 /* Check if this is a user fault */
1275 if (TrapFrame->Eip == (ULONG_PTR)KeUserPopEntrySListFault)
1276 {
1277 /* EBP points to the S-List-header. Copy it inside SEH, to protect
1278 against a bogus pointer from user mode */
1279 _SEH2_TRY
1280 {
1281 ProbeForRead((PVOID)TrapFrame->Ebp,
1282 sizeof(ULARGE_INTEGER),
1283 TYPE_ALIGNMENT(SLIST_HEADER));
1284 SListHeader = *(PULARGE_INTEGER)TrapFrame->Ebp;
1285 }
1286 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1287 {
1288 /* The S-List pointer is not valid! */
1289 goto NotSListFault;
1290 }
1291 _SEH2_END;
1292 ResumeAddress = KeUserPopEntrySListResume;
1293 }
1294 else
1295 {
1296 SListHeader = *(PULARGE_INTEGER)TrapFrame->Ebp;
1297 ResumeAddress = ExpInterlockedPopEntrySListResume;
1298 }
1299
1300 /* Check if either the Next pointer or the Sequence member in the
1301 S-List-header has changed. If any of these has changed, we restart
1302 the operation. Otherwise we only have a bogus pointer and let the
1303 page fault handler deal with it. */
1304 if ((SListHeader.LowPart != TrapFrame->Eax) ||
1305 (SListHeader.HighPart != TrapFrame->Edx))
1306 {
1307 DPRINT1("*** Got an S-List-Fault ***\n");
1308 KeGetCurrentThread()->SListFaultCount++;
1309
1310 /* Restart the operation */
1311 TrapFrame->Eip = (ULONG_PTR)ResumeAddress;
1312
1313 /* Continue execution */
1314 KiEoiHelper(TrapFrame);
1315 }
1316 }
1317 NotSListFault:
1318
1319 /* Call the access fault handler */
1320 Status = MmAccessFault(TrapFrame->ErrCode,
1321 (PVOID)Cr2,
1322 KiUserTrap(TrapFrame),
1323 TrapFrame);
1324 if (NT_SUCCESS(Status))
1325 {
1326 #ifdef _WINKD_
1327 /* Check whether the kernel debugger has owed breakpoints to be inserted */
1328 KdSetOwedBreakpoints();
1329 #endif
1330 /* We succeeded, return */
1331 KiEoiHelper(TrapFrame);
1332 }
1333
1334 /* Check for syscall fault */
1335 #if 0
1336 if ((TrapFrame->Eip == (ULONG_PTR)CopyParams) ||
1337 (TrapFrame->Eip == (ULONG_PTR)ReadBatch))
1338 {
1339 /* Not yet implemented */
1340 UNIMPLEMENTED_FATAL();
1341 }
1342 #endif
1343
1344 /* Check for VDM trap */
1345 if (KiVdmTrap(TrapFrame))
1346 {
1347 DPRINT1("VDM PAGE FAULT at %lx:%lx for address %lx\n",
1348 TrapFrame->SegCs, TrapFrame->Eip, Cr2);
1349 if (VdmDispatchPageFault(TrapFrame))
1350 {
1351 /* Return and end VDM execution */
1352 DPRINT1("VDM page fault with status 0x%lx resolved\n", Status);
1353 KiEoiHelper(TrapFrame);
1354 }
1355 DPRINT1("VDM page fault with status 0x%lx NOT resolved\n", Status);
1356 }
1357
1358 /* Either kernel or user trap (non VDM) so dispatch exception */
1359 if (Status == STATUS_ACCESS_VIOLATION)
1360 {
1361 /* This status code is repurposed so we can recognize it later */
1362 KiDispatchException2Args(KI_EXCEPTION_ACCESS_VIOLATION,
1363 TrapFrame->Eip,
1364 StoreInstruction,
1365 Cr2,
1366 TrapFrame);
1367 }
1368 else if ((Status == STATUS_GUARD_PAGE_VIOLATION) ||
1369 (Status == STATUS_STACK_OVERFLOW))
1370 {
1371 /* These faults only have two parameters */
1372 KiDispatchException2Args(Status,
1373 TrapFrame->Eip,
1374 StoreInstruction,
1375 Cr2,
1376 TrapFrame);
1377 }
1378
1379 /* Only other choice is an in-page error, with 3 parameters */
1380 KiDispatchExceptionFromTrapFrame(STATUS_IN_PAGE_ERROR,
1381 0,
1382 TrapFrame->Eip,
1383 3,
1384 StoreInstruction,
1385 Cr2,
1386 Status,
1387 TrapFrame);
1388 }
1389
1390 DECLSPEC_NORETURN
1391 VOID
1392 FASTCALL
1393 KiTrap0FHandler(IN PKTRAP_FRAME TrapFrame)
1394 {
1395 /* Save trap frame */
1396 KiEnterTrap(TrapFrame);
1397
1398 /* FIXME: Kill the system */
1399 UNIMPLEMENTED;
1400 KiSystemFatalException(EXCEPTION_RESERVED_TRAP, TrapFrame);
1401 }
1402
1403 DECLSPEC_NORETURN
1404 VOID
1405 FASTCALL
1406 KiTrap10Handler(IN PKTRAP_FRAME TrapFrame)
1407 {
1408 PKTHREAD Thread;
1409 PFX_SAVE_AREA SaveArea;
1410
1411 /* Save trap frame */
1412 KiEnterTrap(TrapFrame);
1413
1414 /* Check if this is the NPX thrad */
1415 Thread = KeGetCurrentThread();
1416 SaveArea = KiGetThreadNpxArea(Thread);
1417 if (Thread != KeGetCurrentPrcb()->NpxThread)
1418 {
1419 /* It isn't, enable interrupts and set delayed error */
1420 _enable();
1421 SaveArea->Cr0NpxState |= CR0_TS;
1422
1423 /* End trap */
1424 KiEoiHelper(TrapFrame);
1425 }
1426
1427 /* Otherwise, proceed with NPX fault handling */
1428 KiNpxHandler(TrapFrame, Thread, SaveArea);
1429 }
1430
1431 DECLSPEC_NORETURN
1432 VOID
1433 FASTCALL
1434 KiTrap11Handler(IN PKTRAP_FRAME TrapFrame)
1435 {
1436 /* Save trap frame */
1437 KiEnterTrap(TrapFrame);
1438
1439 /* Enable interrupts and kill the system */
1440 _enable();
1441 KiSystemFatalException(EXCEPTION_ALIGNMENT_CHECK, TrapFrame);
1442 }
1443
1444 DECLSPEC_NORETURN
1445 VOID
1446 FASTCALL
1447 KiTrap13Handler(IN PKTRAP_FRAME TrapFrame)
1448 {
1449 PKTHREAD Thread;
1450 PFX_SAVE_AREA SaveArea;
1451 ULONG Cr0, MxCsrMask, Error;
1452
1453 /* Save trap frame */
1454 KiEnterTrap(TrapFrame);
1455
1456 /* Check if this is the NPX thrad */
1457 Thread = KeGetCurrentThread();
1458 if (Thread != KeGetCurrentPrcb()->NpxThread)
1459 {
1460 /* It isn't, kill the system */
1461 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, (ULONG_PTR)Thread, 0, 0, TrapFrame);
1462 }
1463
1464 /* Get the NPX frame */
1465 SaveArea = KiGetThreadNpxArea(Thread);
1466
1467 /* Check for VDM trap */
1468 ASSERT(KiVdmTrap(TrapFrame) == FALSE);
1469
1470 /* Check for user trap */
1471 if (!KiUserTrap(TrapFrame))
1472 {
1473 /* Kernel should not fault on XMMI */
1474 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 2, TrapFrame);
1475 }
1476
1477 /* Update CR0 */
1478 Cr0 = __readcr0();
1479 Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
1480 __writecr0(Cr0);
1481
1482 /* Save FPU state */
1483 Ke386SaveFpuState(SaveArea);
1484
1485 /* Mark CR0 state dirty */
1486 Cr0 |= NPX_STATE_NOT_LOADED;
1487 Cr0 |= SaveArea->Cr0NpxState;
1488 __writecr0(Cr0);
1489
1490 /* Update NPX state */
1491 Thread->NpxState = NPX_STATE_NOT_LOADED;
1492 KeGetCurrentPrcb()->NpxThread = NULL;
1493
1494 /* Clear the TS bit and re-enable interrupts */
1495 SaveArea->Cr0NpxState &= ~CR0_TS;
1496 _enable();
1497
1498 /* Now look at MxCsr to get the mask of errors we should care about */
1499 MxCsrMask = ~((USHORT)SaveArea->U.FxArea.MXCsr >> 7);
1500
1501 /* Get legal exceptions that software should handle */
1502 Error = (USHORT)SaveArea->U.FxArea.MXCsr & (FSW_INVALID_OPERATION |
1503 FSW_DENORMAL |
1504 FSW_ZERO_DIVIDE |
1505 FSW_OVERFLOW |
1506 FSW_UNDERFLOW |
1507 FSW_PRECISION);
1508 Error &= MxCsrMask;
1509
1510 /* Now handle any of those legal errors */
1511 if (Error & (FSW_INVALID_OPERATION |
1512 FSW_DENORMAL |
1513 FSW_ZERO_DIVIDE |
1514 FSW_OVERFLOW |
1515 FSW_UNDERFLOW |
1516 FSW_PRECISION))
1517 {
1518 /* By issuing an exception */
1519 KiDispatchException1Args(STATUS_FLOAT_MULTIPLE_TRAPS,
1520 TrapFrame->Eip,
1521 0,
1522 TrapFrame);
1523 }
1524
1525 /* Unknown XMMI fault */
1526 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame);
1527 }
1528
1529 /* SOFTWARE SERVICES **********************************************************/
1530
1531 VOID
1532 FASTCALL
1533 KiRaiseSecurityCheckFailureHandler(IN PKTRAP_FRAME TrapFrame)
1534 {
1535 /* Save trap frame */
1536 KiEnterTrap(TrapFrame);
1537
1538 /* Decrement EIP to point to the INT29 instruction (2 bytes, not 1 like INT3) */
1539 TrapFrame->Eip -= 2;
1540
1541 /* Check if this is a user trap */
1542 if (KiUserTrap(TrapFrame))
1543 {
1544 /* Dispatch exception to user mode */
1545 KiDispatchExceptionFromTrapFrame(STATUS_STACK_BUFFER_OVERRUN,
1546 EXCEPTION_NONCONTINUABLE,
1547 TrapFrame->Eip,
1548 1,
1549 TrapFrame->Ecx,
1550 0,
1551 0,
1552 TrapFrame);
1553 }
1554 else
1555 {
1556 EXCEPTION_RECORD ExceptionRecord;
1557
1558 /* Bugcheck the system */
1559 ExceptionRecord.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
1560 ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
1561 ExceptionRecord.ExceptionRecord = NULL;
1562 ExceptionRecord.ExceptionAddress = (PVOID)TrapFrame->Eip;
1563 ExceptionRecord.NumberParameters = 1;
1564 ExceptionRecord.ExceptionInformation[0] = TrapFrame->Ecx;
1565
1566 KeBugCheckWithTf(KERNEL_SECURITY_CHECK_FAILURE,
1567 TrapFrame->Ecx,
1568 (ULONG_PTR)TrapFrame,
1569 (ULONG_PTR)&ExceptionRecord,
1570 0,
1571 TrapFrame);
1572 }
1573 }
1574
1575 VOID
1576 FASTCALL
1577 KiGetTickCountHandler(IN PKTRAP_FRAME TrapFrame)
1578 {
1579 /* Save trap frame */
1580 KiEnterTrap(TrapFrame);
1581
1582 /*
1583 * Just fail the request
1584 */
1585 DbgPrint("INT 0x2A attempted, returning 0 tick count\n");
1586 TrapFrame->Eax = 0;
1587
1588 /* Exit the trap */
1589 KiEoiHelper(TrapFrame);
1590 }
1591
1592 VOID
1593 FASTCALL
1594 KiCallbackReturnHandler(IN PKTRAP_FRAME TrapFrame)
1595 {
1596 PKTHREAD Thread;
1597 NTSTATUS Status;
1598
1599 /* Save the SEH chain, NtCallbackReturn will restore this */
1600 TrapFrame->ExceptionList = KeGetPcr()->NtTib.ExceptionList;
1601
1602 /* Set thread fields */
1603 Thread = KeGetCurrentThread();
1604 Thread->TrapFrame = TrapFrame;
1605 Thread->PreviousMode = KiUserTrap(TrapFrame);
1606 ASSERT(Thread->PreviousMode != KernelMode);
1607
1608 /* Pass the register parameters to NtCallbackReturn.
1609 Result pointer is in ecx, result length in edx, status in eax */
1610 Status = NtCallbackReturn((PVOID)TrapFrame->Ecx,
1611 TrapFrame->Edx,
1612 TrapFrame->Eax);
1613
1614 /* If we got here, something went wrong. Return an error to the caller */
1615 KiServiceExit(TrapFrame, Status);
1616 }
1617
1618 DECLSPEC_NORETURN
1619 VOID
1620 FASTCALL
1621 KiRaiseAssertionHandler(IN PKTRAP_FRAME TrapFrame)
1622 {
1623 /* Save trap frame */
1624 KiEnterTrap(TrapFrame);
1625
1626 /* Decrement EIP to point to the INT2C instruction (2 bytes, not 1 like INT3) */
1627 TrapFrame->Eip -= 2;
1628
1629 /* Dispatch the exception */
1630 KiDispatchException0Args(STATUS_ASSERTION_FAILURE,
1631 TrapFrame->Eip,
1632 TrapFrame);
1633 }
1634
1635 DECLSPEC_NORETURN
1636 VOID
1637 FASTCALL
1638 KiDebugServiceHandler(IN PKTRAP_FRAME TrapFrame)
1639 {
1640 /* Save trap frame */
1641 KiEnterTrap(TrapFrame);
1642
1643 /* Increment EIP to skip the INT3 instruction */
1644 TrapFrame->Eip++;
1645
1646 /* Continue with the common handler */
1647 KiDebugHandler(TrapFrame, TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1648 }
1649
1650
1651 FORCEINLINE
1652 VOID
1653 KiDbgPreServiceHook(ULONG SystemCallNumber, PULONG_PTR Arguments)
1654 {
1655 #if DBG && !defined(_WINKD_)
1656 if (SystemCallNumber >= 0x1000 && KeWin32PreServiceHook)
1657 KeWin32PreServiceHook(SystemCallNumber, Arguments);
1658 #endif
1659 }
1660
1661 FORCEINLINE
1662 ULONG_PTR
1663 KiDbgPostServiceHook(ULONG SystemCallNumber, ULONG_PTR Result)
1664 {
1665 #if DBG && !defined(_WINKD_)
1666 if (SystemCallNumber >= 0x1000 && KeWin32PostServiceHook)
1667 return KeWin32PostServiceHook(SystemCallNumber, Result);
1668 #endif
1669 return Result;
1670 }
1671
1672 DECLSPEC_NORETURN
1673 VOID
1674 FASTCALL
1675 KiSystemServiceHandler(IN PKTRAP_FRAME TrapFrame,
1676 IN PVOID Arguments)
1677 {
1678 PKTHREAD Thread;
1679 PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
1680 ULONG Id, Offset, StackBytes;
1681 NTSTATUS Status;
1682 PVOID Handler;
1683 ULONG SystemCallNumber = TrapFrame->Eax;
1684
1685 /* Get the current thread */
1686 Thread = KeGetCurrentThread();
1687
1688 /* Set debug header */
1689 KiFillTrapFrameDebug(TrapFrame);
1690
1691 /* Chain trap frames */
1692 TrapFrame->Edx = (ULONG_PTR)Thread->TrapFrame;
1693
1694 /* No error code */
1695 TrapFrame->ErrCode = 0;
1696
1697 /* Save previous mode */
1698 TrapFrame->PreviousPreviousMode = Thread->PreviousMode;
1699
1700 /* Save the SEH chain and terminate it for now */
1701 TrapFrame->ExceptionList = KeGetPcr()->NtTib.ExceptionList;
1702 KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
1703
1704 /* Default to debugging disabled */
1705 TrapFrame->Dr7 = 0;
1706
1707 /* Check if the frame was from user mode */
1708 if (KiUserTrap(TrapFrame))
1709 {
1710 /* Check for active debugging */
1711 if (KeGetCurrentThread()->Header.DebugActive & 0xFF)
1712 {
1713 /* Handle debug registers */
1714 KiHandleDebugRegistersOnTrapEntry(TrapFrame);
1715 }
1716 }
1717
1718 /* Set thread fields */
1719 Thread->TrapFrame = TrapFrame;
1720 Thread->PreviousMode = KiUserTrap(TrapFrame);
1721
1722 /* Enable interrupts */
1723 _enable();
1724
1725 /* Decode the system call number */
1726 Offset = (SystemCallNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
1727 Id = SystemCallNumber & SERVICE_NUMBER_MASK;
1728
1729 /* Get descriptor table */
1730 DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
1731
1732 /* Validate the system call number */
1733 if (__builtin_expect(Id >= DescriptorTable->Limit, 0))
1734 {
1735 /* Check if this is a GUI call */
1736 if (!(Offset & SERVICE_TABLE_TEST))
1737 {
1738 /* Fail the call */
1739 Status = STATUS_INVALID_SYSTEM_SERVICE;
1740 goto ExitCall;
1741 }
1742
1743 /* Convert us to a GUI thread -- must wrap in ASM to get new EBP */
1744 Status = KiConvertToGuiThread();
1745
1746 /* Reload trap frame and descriptor table pointer from new stack */
1747 TrapFrame = *(volatile PVOID*)&Thread->TrapFrame;
1748 DescriptorTable = (PVOID)(*(volatile ULONG_PTR*)&Thread->ServiceTable + Offset);
1749
1750 if (!NT_SUCCESS(Status))
1751 {
1752 /* Set the last error and fail */
1753 goto ExitCall;
1754 }
1755
1756 /* Validate the system call number again */
1757 if (Id >= DescriptorTable->Limit)
1758 {
1759 /* Fail the call */
1760 Status = STATUS_INVALID_SYSTEM_SERVICE;
1761 goto ExitCall;
1762 }
1763 }
1764
1765 /* Check if this is a GUI call */
1766 if (__builtin_expect(Offset & SERVICE_TABLE_TEST, 0))
1767 {
1768 /* Get the batch count and flush if necessary */
1769 if (NtCurrentTeb()->GdiBatchCount) KeGdiFlushUserBatch();
1770 }
1771
1772 /* Increase system call count */
1773 KeGetCurrentPrcb()->KeSystemCalls++;
1774
1775 /* FIXME: Increase individual counts on debug systems */
1776 //KiIncreaseSystemCallCount(DescriptorTable, Id);
1777
1778 /* Get stack bytes */
1779 StackBytes = DescriptorTable->Number[Id];
1780
1781 /* Probe caller stack */
1782 if (__builtin_expect((Arguments < (PVOID)MmUserProbeAddress) && !(KiUserTrap(TrapFrame)), 0))
1783 {
1784 /* Access violation */
1785 UNIMPLEMENTED_FATAL();
1786 }
1787
1788 /* Call pre-service debug hook */
1789 KiDbgPreServiceHook(SystemCallNumber, Arguments);
1790
1791 /* Get the handler and make the system call */
1792 Handler = (PVOID)DescriptorTable->Base[Id];
1793 Status = KiSystemCallTrampoline(Handler, Arguments, StackBytes);
1794
1795 /* Call post-service debug hook */
1796 Status = KiDbgPostServiceHook(SystemCallNumber, Status);
1797
1798 /* Make sure we're exiting correctly */
1799 KiExitSystemCallDebugChecks(Id, TrapFrame);
1800
1801 /* Restore the old trap frame */
1802 ExitCall:
1803 Thread->TrapFrame = (PKTRAP_FRAME)TrapFrame->Edx;
1804
1805 /* Exit from system call */
1806 KiServiceExit(TrapFrame, Status);
1807 }
1808
1809 VOID
1810 FASTCALL
1811 KiCheckForSListAddress(IN PKTRAP_FRAME TrapFrame)
1812 {
1813 UNIMPLEMENTED;
1814 }
1815
1816 /*
1817 * @implemented
1818 */
1819 VOID
1820 NTAPI
1821 Kei386EoiHelper(VOID)
1822 {
1823 /* We should never see this call happening */
1824 KeBugCheck(MISMATCHED_HAL);
1825 }
1826
1827 /* EOF */