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