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
9 /* INCLUDES *******************************************************************/
15 /* GLOBALS ********************************************************************/
17 UCHAR KiTrapPrefixTable
[] =
20 0xF3, /* REP INS/OUTS */
32 UCHAR KiTrapIoTable
[] =
48 PFAST_SYSTEM_CALL_EXIT KiFastCallExitHandler
;
49 #if DBG && !defined(_WINKD_)
50 PKDBG_PRESERVICEHOOK KeWin32PreServiceHook
= NULL
;
51 PKDBG_POSTSERVICEHOOK KeWin32PostServiceHook
= NULL
;
54 BOOLEAN StopChecking
= FALSE
;
58 /* TRAP EXIT CODE *************************************************************/
62 KiVdmTrap(IN PKTRAP_FRAME TrapFrame
)
64 /* Either the V8086 flag is on, or this is user-mode with a VDM */
65 return ((TrapFrame
->EFlags
& EFLAGS_V86_MASK
) ||
66 ((KiUserTrap(TrapFrame
)) && (PsGetCurrentProcess()->VdmObjects
)));
71 KiV86Trap(IN PKTRAP_FRAME TrapFrame
)
73 /* Check if the V8086 flag is on */
74 return ((TrapFrame
->EFlags
& EFLAGS_V86_MASK
) != 0);
79 KiIsFrameEdited(IN PKTRAP_FRAME TrapFrame
)
81 /* An edited frame changes esp. It is marked by clearing the bits
82 defined by FRAME_EDITED in the SegCs field of the trap frame */
83 return ((TrapFrame
->SegCs
& FRAME_EDITED
) == 0);
88 KiCommonExit(IN PKTRAP_FRAME TrapFrame
, BOOLEAN SkipPreviousMode
)
90 /* Disable interrupts until we return */
93 /* Check for APC delivery */
94 KiCheckForApcDelivery(TrapFrame
);
96 /* Restore the SEH handler chain */
97 KeGetPcr()->NtTib
.ExceptionList
= TrapFrame
->ExceptionList
;
99 /* Check if there are active debug registers */
100 if (__builtin_expect(TrapFrame
->Dr7
& ~DR7_RESERVED_MASK
, 0))
102 /* Check if the frame was from user mode or v86 mode */
103 if (KiUserTrap(TrapFrame
) ||
104 (TrapFrame
->EFlags
& EFLAGS_V86_MASK
))
106 /* Handle debug registers */
107 KiHandleDebugRegistersOnTrapExit(TrapFrame
);
111 /* Debugging checks */
112 KiExitTrapDebugChecks(TrapFrame
, SkipPreviousMode
);
118 KiEoiHelper(IN PKTRAP_FRAME TrapFrame
)
120 /* Common trap exit code */
121 KiCommonExit(TrapFrame
, TRUE
);
123 /* Check if this was a V8086 trap */
124 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
) KiTrapReturnNoSegments(TrapFrame
);
126 /* Check for user mode exit */
127 if (KiUserTrap(TrapFrame
)) KiTrapReturn(TrapFrame
);
129 /* Check for edited frame */
130 if (KiIsFrameEdited(TrapFrame
)) KiEditedTrapReturn(TrapFrame
);
132 /* Check if we have single stepping enabled */
133 if (TrapFrame
->EFlags
& EFLAGS_TF
) KiTrapReturnNoSegments(TrapFrame
);
135 /* Exit the trap to kernel mode */
136 KiTrapReturnNoSegmentsRet8(TrapFrame
);
142 KiServiceExit(IN PKTRAP_FRAME TrapFrame
,
145 ASSERT((TrapFrame
->EFlags
& EFLAGS_V86_MASK
) == 0);
146 ASSERT(!KiIsFrameEdited(TrapFrame
));
148 /* Copy the status into EAX */
149 TrapFrame
->Eax
= Status
;
151 /* Common trap exit code */
152 KiCommonExit(TrapFrame
, FALSE
);
154 /* Restore previous mode */
155 KeGetCurrentThread()->PreviousMode
= (CCHAR
)TrapFrame
->PreviousPreviousMode
;
157 /* Check for user mode exit */
158 if (KiUserTrap(TrapFrame
))
160 /* Check if we were single stepping */
161 if (TrapFrame
->EFlags
& EFLAGS_TF
)
163 /* Must use the IRET handler */
164 KiSystemCallTrapReturn(TrapFrame
);
168 /* We can use the sysexit handler */
169 KiFastCallExitHandler(TrapFrame
);
173 /* Exit to kernel mode */
174 KiSystemCallReturn(TrapFrame
);
180 KiServiceExit2(IN PKTRAP_FRAME TrapFrame
)
182 /* Common trap exit code */
183 KiCommonExit(TrapFrame
, FALSE
);
185 /* Restore previous mode */
186 KeGetCurrentThread()->PreviousMode
= (CCHAR
)TrapFrame
->PreviousPreviousMode
;
188 /* Check if this was a V8086 trap */
189 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
) KiTrapReturnNoSegments(TrapFrame
);
191 /* Check for user mode exit */
192 if (KiUserTrap(TrapFrame
)) KiTrapReturn(TrapFrame
);
194 /* Check for edited frame */
195 if (KiIsFrameEdited(TrapFrame
)) KiEditedTrapReturn(TrapFrame
);
197 /* Check if we have single stepping enabled */
198 if (TrapFrame
->EFlags
& EFLAGS_TF
) KiTrapReturnNoSegments(TrapFrame
);
200 /* Exit the trap to kernel mode */
201 KiTrapReturnNoSegmentsRet8(TrapFrame
);
205 /* TRAP HANDLERS **************************************************************/
210 KiDebugHandler(IN PKTRAP_FRAME TrapFrame
,
215 /* Check for VDM trap */
216 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
218 /* Enable interrupts if the trap came from user-mode */
219 if (KiUserTrap(TrapFrame
)) _enable();
221 /* Dispatch the exception */
222 KiDispatchExceptionFromTrapFrame(STATUS_BREAKPOINT
,
234 KiNpxHandler(IN PKTRAP_FRAME TrapFrame
,
236 IN PFX_SAVE_AREA SaveArea
)
238 ULONG Cr0
, Mask
, Error
, ErrorOffset
, DataOffset
;
240 /* Check for VDM trap */
241 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
243 /* Check for kernel trap */
244 if (!KiUserTrap(TrapFrame
))
246 /* Kernel might've tripped a delayed error */
247 SaveArea
->Cr0NpxState
|= CR0_TS
;
249 /* Only valid if it happened during a restore */
250 //if ((PVOID)TrapFrame->Eip == FrRestore)
252 /* It did, so just skip the instruction */
253 //TrapFrame->Eip += 3; /* sizeof(FRSTOR) */
254 //KiEoiHelper(TrapFrame);
258 /* User or kernel trap -- get ready to issue an exception */
259 //if (Thread->NpxState == NPX_STATE_NOT_LOADED)
263 Cr0
&= ~(CR0_MP
| CR0_EM
| CR0_TS
);
267 Ke386SaveFpuState(SaveArea
);
269 /* Mark CR0 state dirty */
270 Cr0
|= NPX_STATE_NOT_LOADED
;
271 Cr0
|= SaveArea
->Cr0NpxState
;
274 /* Update NPX state */
275 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
276 KeGetCurrentPrcb()->NpxThread
= NULL
;
279 /* Clear the TS bit and re-enable interrupts */
280 SaveArea
->Cr0NpxState
&= ~CR0_TS
;
283 /* Check if we should get the FN or FX error */
284 if (KeI386FxsrPresent
)
287 Mask
= SaveArea
->U
.FxArea
.ControlWord
;
288 Error
= SaveArea
->U
.FxArea
.StatusWord
;
290 /* Get the FPU exception address too */
291 ErrorOffset
= SaveArea
->U
.FxArea
.ErrorOffset
;
292 DataOffset
= SaveArea
->U
.FxArea
.DataOffset
;
297 Mask
= SaveArea
->U
.FnArea
.ControlWord
;
298 Error
= SaveArea
->U
.FnArea
.StatusWord
;
300 /* Get the FPU exception address too */
301 ErrorOffset
= SaveArea
->U
.FnArea
.ErrorOffset
;
302 DataOffset
= SaveArea
->U
.FnArea
.DataOffset
;
305 /* Get legal exceptions that software should handle */
306 /* We do this by first masking off from the Mask the bits we need, */
307 /* This is done so we can keep the FSW_STACK_FAULT bit in Error. */
308 Mask
&= (FSW_INVALID_OPERATION
|
316 /* Check for invalid operation */
317 if (Error
& FSW_INVALID_OPERATION
)
319 /* NOTE: Stack fault is handled differently than any other case. */
320 /* 1. It's only raised for invalid operation. */
321 /* 2. It's only raised if invalid operation is not masked. */
322 if (Error
& FSW_STACK_FAULT
)
324 /* Issue stack check fault */
325 KiDispatchException2Args(STATUS_FLOAT_STACK_CHECK
,
333 KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION
,
339 /* Check for divide by zero */
340 if (Error
& FSW_ZERO_DIVIDE
)
343 KiDispatchException1Args(STATUS_FLOAT_DIVIDE_BY_ZERO
,
349 /* Check for denormal */
350 if (Error
& FSW_DENORMAL
)
353 KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION
,
359 /* Check for overflow */
360 if (Error
& FSW_OVERFLOW
)
363 KiDispatchException1Args(STATUS_FLOAT_OVERFLOW
,
369 /* Check for underflow */
370 if (Error
& FSW_UNDERFLOW
)
373 KiDispatchException1Args(STATUS_FLOAT_UNDERFLOW
,
379 /* Check for precision fault */
380 if (Error
& FSW_PRECISION
)
383 KiDispatchException1Args(STATUS_FLOAT_INEXACT_RESULT
,
389 /* Unknown FPU fault */
390 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 1, Error
, 0, 0, TrapFrame
);
396 KiTrap00Handler(IN PKTRAP_FRAME TrapFrame
)
398 /* Save trap frame */
399 KiEnterTrap(TrapFrame
);
401 /* Check for VDM trap */
402 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
404 /* Enable interrupts */
407 /* Dispatch the exception */
408 KiDispatchException0Args(STATUS_INTEGER_DIVIDE_BY_ZERO
,
416 KiTrap01Handler(IN PKTRAP_FRAME TrapFrame
)
418 /* Save trap frame */
419 KiEnterTrap(TrapFrame
);
421 /* Check for VDM trap */
422 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
424 /* Enable interrupts if the trap came from user-mode */
425 if (KiUserTrap(TrapFrame
)) _enable();
427 /* Mask out trap flag and dispatch the exception */
428 TrapFrame
->EFlags
&= ~EFLAGS_TF
;
429 KiDispatchException0Args(STATUS_SINGLE_STEP
,
443 KTRAP_FRAME TrapFrame
;
447 // In some sort of strange recursion case, we might end up here with the IF
448 // flag incorrectly on the interrupt frame -- during a normal NMI this would
449 // normally already be set.
451 // For sanity's sake, make sure interrupts are disabled for sure.
452 // NMIs will already be since the CPU does it for us.
457 // Get the current TSS, thread, and process
460 Thread
= ((PKIPCR
)PCR
)->PrcbData
.CurrentThread
;
461 Process
= Thread
->ApcState
.Process
;
464 // Save data usually not in the TSS
466 Tss
->CR3
= Process
->DirectoryTableBase
[0];
467 Tss
->IoMapBase
= Process
->IopmOffset
;
468 Tss
->LDT
= Process
->LdtDescriptor
.LimitLow
? KGDT_LDT
: 0;
471 // Now get the base address of the NMI TSS
473 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[KGDT_NMI_TSS
/ sizeof(KGDTENTRY
)];
474 NmiTss
= (PKTSS
)(ULONG_PTR
)(TssGdt
->BaseLow
|
475 TssGdt
->HighWord
.Bytes
.BaseMid
<< 16 |
476 TssGdt
->HighWord
.Bytes
.BaseHi
<< 24);
479 // Switch to it and activate it, masking off the nested flag
481 // Note that in reality, we are already on the NMI tss -- we just need to
482 // update the PCR to reflect this
485 __writeeflags(__readeflags() &~ EFLAGS_NESTED_TASK
);
486 TssGdt
->HighWord
.Bits
.Dpl
= 0;
487 TssGdt
->HighWord
.Bits
.Pres
= 1;
488 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
491 // Now build the trap frame based on the original TSS
493 // The CPU does a hardware "Context switch" / task switch of sorts and so it
494 // takes care of saving our context in the normal TSS.
496 // We just have to go get the values...
498 RtlZeroMemory(&TrapFrame
, sizeof(KTRAP_FRAME
));
499 TrapFrame
.HardwareSegSs
= Tss
->Ss0
;
500 TrapFrame
.HardwareEsp
= Tss
->Esp0
;
501 TrapFrame
.EFlags
= Tss
->EFlags
;
502 TrapFrame
.SegCs
= Tss
->Cs
;
503 TrapFrame
.Eip
= Tss
->Eip
;
504 TrapFrame
.Ebp
= Tss
->Ebp
;
505 TrapFrame
.Ebx
= Tss
->Ebx
;
506 TrapFrame
.Esi
= Tss
->Esi
;
507 TrapFrame
.Edi
= Tss
->Edi
;
508 TrapFrame
.SegFs
= Tss
->Fs
;
509 TrapFrame
.ExceptionList
= PCR
->NtTib
.ExceptionList
;
510 TrapFrame
.PreviousPreviousMode
= -1;
511 TrapFrame
.Eax
= Tss
->Eax
;
512 TrapFrame
.Ecx
= Tss
->Ecx
;
513 TrapFrame
.Edx
= Tss
->Edx
;
514 TrapFrame
.SegDs
= Tss
->Ds
;
515 TrapFrame
.SegEs
= Tss
->Es
;
516 TrapFrame
.SegGs
= Tss
->Gs
;
517 TrapFrame
.DbgEip
= Tss
->Eip
;
518 TrapFrame
.DbgEbp
= Tss
->Ebp
;
521 // Store the trap frame in the KPRCB
523 KiSaveProcessorState(&TrapFrame
, NULL
);
526 // Call any registered NMI handlers and see if they handled it or not
531 // They did not, so call the platform HAL routine to bugcheck the system
533 // Make sure the HAL believes it's running at HIGH IRQL... we can't use
534 // the normal APIs here as playing with the IRQL could change the system
538 PCR
->Irql
= HIGH_LEVEL
;
544 // Although the CPU disabled NMIs, we just did a BIOS Call, which could've
545 // totally changed things.
547 // We have to make sure we're still in our original NMI -- a nested NMI
548 // will point back to the NMI TSS, and in that case we're hosed.
550 if (PCR
->TSS
->Backlink
!= KGDT_NMI_TSS
)
553 // Restore original TSS
558 // Set it back to busy
560 TssGdt
->HighWord
.Bits
.Dpl
= 0;
561 TssGdt
->HighWord
.Bits
.Pres
= 1;
562 TssGdt
->HighWord
.Bits
.Type
= I386_ACTIVE_TSS
;
565 // Restore nested flag
567 __writeeflags(__readeflags() | EFLAGS_NESTED_TASK
);
570 // Handled, return from interrupt
576 // Unhandled: crash the system
578 KiSystemFatalException(EXCEPTION_NMI
, NULL
);
584 KiTrap03Handler(IN PKTRAP_FRAME TrapFrame
)
586 /* Save trap frame */
587 KiEnterTrap(TrapFrame
);
589 /* Continue with the common handler */
590 KiDebugHandler(TrapFrame
, BREAKPOINT_BREAK
, 0, 0);
596 KiTrap04Handler(IN PKTRAP_FRAME TrapFrame
)
598 /* Save trap frame */
599 KiEnterTrap(TrapFrame
);
601 /* Check for VDM trap */
602 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
604 /* Enable interrupts */
607 /* Dispatch the exception */
608 KiDispatchException0Args(STATUS_INTEGER_OVERFLOW
,
616 KiTrap05Handler(IN PKTRAP_FRAME TrapFrame
)
618 /* Save trap frame */
619 KiEnterTrap(TrapFrame
);
621 /* Check for VDM trap */
622 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
624 /* Check for kernel-mode fault */
625 if (!KiUserTrap(TrapFrame
)) KiSystemFatalException(EXCEPTION_BOUND_CHECK
, TrapFrame
);
627 /* Enable interrupts */
630 /* Dispatch the exception */
631 KiDispatchException0Args(STATUS_ARRAY_BOUNDS_EXCEEDED
,
639 KiTrap06Handler(IN PKTRAP_FRAME TrapFrame
)
645 /* Check for V86 GPF */
646 if (__builtin_expect(KiV86Trap(TrapFrame
), 1))
649 KiEnterV86Trap(TrapFrame
);
651 /* Must be a VDM process */
652 if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects
, 0))
654 /* Enable interrupts */
657 /* Setup illegal instruction fault */
658 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION
,
663 /* Go to APC level */
664 OldIrql
= KfRaiseIrql(APC_LEVEL
);
668 if (!VdmDispatchBop(TrapFrame
))
670 /* Should only happen in VDM mode */
671 UNIMPLEMENTED_FATAL();
674 /* Bring IRQL back */
675 KfLowerIrql(OldIrql
);
678 /* Do a quick V86 exit if possible */
679 KiExitV86Trap(TrapFrame
);
682 /* Save trap frame */
683 KiEnterTrap(TrapFrame
);
685 /* Enable interrupts */
686 Instruction
= (PUCHAR
)TrapFrame
->Eip
;
689 /* Check for user trap */
690 if (KiUserTrap(TrapFrame
))
694 /* Scan next 4 opcodes */
695 for (i
= 0; i
< 4; i
++)
697 /* Check for LOCK instruction */
698 if (Instruction
[i
] == 0xF0)
700 /* Send invalid lock sequence exception */
701 KiDispatchException0Args(STATUS_INVALID_LOCK_SEQUENCE
,
707 /* FIXME: SEH ends here */
710 /* Kernel-mode or user-mode fault (but not LOCK) */
711 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION
,
720 KiTrap07Handler(IN PKTRAP_FRAME TrapFrame
)
722 PKTHREAD Thread
, NpxThread
;
723 PFX_SAVE_AREA SaveArea
, NpxSaveArea
;
726 /* Save trap frame */
727 KiEnterTrap(TrapFrame
);
729 /* Try to handle NPX delay load */
732 /* Get the current thread */
733 Thread
= KeGetCurrentThread();
735 /* Get the NPX frame */
736 SaveArea
= KiGetThreadNpxArea(Thread
);
738 /* Check if emulation is enabled */
739 if (SaveArea
->Cr0NpxState
& CR0_EM
)
741 /* Not implemented */
742 UNIMPLEMENTED_FATAL();
745 /* Save CR0 and check NPX state */
747 if (Thread
->NpxState
!= NPX_STATE_LOADED
)
750 Cr0
&= ~(CR0_MP
| CR0_EM
| CR0_TS
);
753 /* Get the NPX thread */
754 NpxThread
= KeGetCurrentPrcb()->NpxThread
;
757 /* Get the NPX frame */
758 NpxSaveArea
= KiGetThreadNpxArea(NpxThread
);
761 DPRINT("FIXME: Save FPU state: %p\n", NpxSaveArea
);
762 //Ke386SaveFpuState(NpxSaveArea);
764 /* Update NPX state */
765 NpxThread
->NpxState
= NPX_STATE_NOT_LOADED
;
769 //Ke386LoadFpuState(SaveArea);
771 /* Update NPX state */
772 Thread
->NpxState
= NPX_STATE_LOADED
;
773 KeGetCurrentPrcb()->NpxThread
= Thread
;
775 /* Enable interrupts */
778 /* Check if CR0 needs to be reloaded due to context switch */
779 if (!SaveArea
->Cr0NpxState
) KiEoiHelper(TrapFrame
);
781 /* Otherwise, we need to reload CR0, disable interrupts */
786 Cr0
|= SaveArea
->Cr0NpxState
;
789 /* Now restore interrupts and check for TS */
791 if (Cr0
& CR0_TS
) KiEoiHelper(TrapFrame
);
793 /* We're still here -- clear TS and try again */
794 __writecr0(__readcr0() &~ CR0_TS
);
799 /* This is an actual fault, not a lack of FPU state */
804 /* TS should not be set */
808 * If it's incorrectly set, then maybe the state is actually still valid
809 * but we could've lock track of that due to a BIOS call.
810 * Make sure MP is still set, which should verify the theory.
814 /* Indeed, the state is actually still valid, so clear TS */
815 __writecr0(__readcr0() &~ CR0_TS
);
816 KiEoiHelper(TrapFrame
);
819 /* Otherwise, something strange is going on */
820 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 2, Cr0
, 0, 0, TrapFrame
);
823 /* It's not a delayed load, so process this trap as an NPX fault */
824 KiNpxHandler(TrapFrame
, Thread
, SaveArea
);
830 KiTrap08Handler(IN PKTRAP_FRAME TrapFrame
)
832 /* FIXME: Not handled */
833 KiSystemFatalException(EXCEPTION_DOUBLE_FAULT
, TrapFrame
);
839 KiTrap09Handler(IN PKTRAP_FRAME TrapFrame
)
841 /* Save trap frame */
842 KiEnterTrap(TrapFrame
);
844 /* Enable interrupts and kill the system */
846 KiSystemFatalException(EXCEPTION_NPX_OVERRUN
, TrapFrame
);
852 KiTrap0AHandler(IN PKTRAP_FRAME TrapFrame
)
854 /* Save trap frame */
855 KiEnterTrap(TrapFrame
);
857 /* Check for VDM trap */
858 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
860 /* Kill the system */
861 KiSystemFatalException(EXCEPTION_INVALID_TSS
, TrapFrame
);
867 KiTrap0BHandler(IN PKTRAP_FRAME TrapFrame
)
869 /* Save trap frame */
870 KiEnterTrap(TrapFrame
);
872 /* FIXME: Kill the system */
874 KiSystemFatalException(EXCEPTION_SEGMENT_NOT_PRESENT
, TrapFrame
);
880 KiTrap0CHandler(IN PKTRAP_FRAME TrapFrame
)
882 /* Save trap frame */
883 KiEnterTrap(TrapFrame
);
885 /* FIXME: Kill the system */
887 KiSystemFatalException(EXCEPTION_STACK_FAULT
, TrapFrame
);
893 KiTrap0DHandler(IN PKTRAP_FRAME TrapFrame
)
896 BOOLEAN Privileged
= FALSE
;
898 UCHAR Instruction
= 0;
901 /* Check for V86 GPF */
902 if (__builtin_expect(KiV86Trap(TrapFrame
), 1))
905 KiEnterV86Trap(TrapFrame
);
907 /* Must be a VDM process */
908 if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects
, 0))
910 /* Enable interrupts */
913 /* Setup illegal instruction fault */
914 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION
,
919 /* Go to APC level */
920 OldIrql
= KfRaiseIrql(APC_LEVEL
);
923 /* Handle the V86 opcode */
924 if (__builtin_expect(Ki386HandleOpcodeV86(TrapFrame
) == 0xFF, 0))
926 /* Should only happen in VDM mode */
927 UNIMPLEMENTED_FATAL();
930 /* Bring IRQL back */
931 KfLowerIrql(OldIrql
);
934 /* Do a quick V86 exit if possible */
935 KiExitV86Trap(TrapFrame
);
938 /* Save trap frame */
939 KiEnterTrap(TrapFrame
);
941 /* Check for user-mode GPF */
942 if (KiUserTrap(TrapFrame
))
944 /* Should not be VDM */
945 ASSERT(KiVdmTrap(TrapFrame
) == FALSE
);
947 /* Enable interrupts and check error code */
949 if (!TrapFrame
->ErrCode
)
952 Instructions
= (PUCHAR
)TrapFrame
->Eip
;
954 /* Scan next 15 bytes */
955 for (i
= 0; i
< 15; i
++)
957 /* Skip prefix instructions */
958 for (j
= 0; j
< sizeof(KiTrapPrefixTable
); j
++)
960 /* Is this a prefix instruction? */
961 if (Instructions
[i
] == KiTrapPrefixTable
[j
])
968 /* Is this NOT any prefix instruction? */
969 if (j
== sizeof(KiTrapPrefixTable
))
971 /* We can go ahead and handle the fault now */
972 Instruction
= Instructions
[i
];
977 /* If all we found was prefixes, then this instruction is too long */
980 /* Setup illegal instruction fault */
981 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION
,
986 /* Check for privileged instructions */
987 DPRINT("Instruction (%lu) at fault: %lx %lx %lx %lx\n",
992 Instructions
[i
+ 3]);
993 if (Instruction
== 0xF4) // HLT
995 /* HLT is privileged */
998 else if (Instruction
== 0x0F)
1000 /* Test if it's any of the privileged two-byte opcodes */
1001 if (((Instructions
[i
+ 1] == 0x00) && // LLDT or LTR
1002 (((Instructions
[i
+ 2] & 0x38) == 0x10) || // LLDT
1003 (Instructions
[i
+ 2] == 0x18))) || // LTR
1004 ((Instructions
[i
+ 1] == 0x01) && // LGDT or LIDT or LMSW
1005 (((Instructions
[i
+ 2] & 0x38) == 0x10) || // LGDT
1006 (Instructions
[i
+ 2] == 0x18) || // LIDT
1007 (Instructions
[i
+ 2] == 0x30))) || // LMSW
1008 (Instructions
[i
+ 1] == 0x08) || // INVD
1009 (Instructions
[i
+ 1] == 0x09) || // WBINVD
1010 (Instructions
[i
+ 1] == 0x35) || // SYSEXIT
1011 (Instructions
[i
+ 1] == 0x21) || // MOV DR, XXX
1012 (Instructions
[i
+ 1] == 0x06) || // CLTS
1013 (Instructions
[i
+ 1] == 0x20) || // MOV CR, XXX
1014 (Instructions
[i
+ 1] == 0x22) || // MOV XXX, CR
1015 (Instructions
[i
+ 1] == 0x23) || // MOV YYY, DR
1016 (Instructions
[i
+ 1] == 0x30) || // WRMSR
1017 (Instructions
[i
+ 1] == 0x33)) // RDPMC
1018 // INVLPG, INVLPGA, SYSRET
1020 /* These are all privileged */
1026 /* Get the IOPL and compare with the RPL mask */
1027 Iopl
= (TrapFrame
->EFlags
& EFLAGS_IOPL
) >> 12;
1028 if ((TrapFrame
->SegCs
& RPL_MASK
) > Iopl
)
1030 /* I/O privilege error -- check for known instructions */
1031 if ((Instruction
== 0xFA) || (Instruction
== 0xFB)) // CLI or STI
1033 /* These are privileged */
1038 /* Last hope: an IN/OUT instruction */
1039 for (j
= 0; j
< sizeof(KiTrapIoTable
); j
++)
1041 /* Is this an I/O instruction? */
1042 if (Instruction
== KiTrapIoTable
[j
])
1044 /* Then it's privileged */
1053 /* So now... was the instruction privileged or not? */
1056 /* Whew! We have a privileged instruction, so dispatch the fault */
1057 KiDispatchException0Args(STATUS_PRIVILEGED_INSTRUCTION
,
1063 /* If we got here, send an access violation */
1064 KiDispatchException2Args(STATUS_ACCESS_VIOLATION
,
1072 * Check for a fault during checking of the user instruction.
1074 * Note that the SEH handler will catch invalid EIP, but we could be dealing
1075 * with an invalid CS, which will generate another GPF instead.
1078 if (((PVOID
)TrapFrame
->Eip
>= (PVOID
)KiTrap0DHandler
) &&
1079 ((PVOID
)TrapFrame
->Eip
< (PVOID
)KiTrap0DHandler
))
1081 /* Not implemented */
1082 UNIMPLEMENTED_FATAL();
1086 * NOTE: The ASM trap exit code would restore segment registers by doing
1087 * a POP <SEG>, which could cause an invalid segment if someone had messed
1088 * with the segment values.
1090 * Another case is a bogus SS, which would hit a GPF when doing the iret.
1091 * This could only be done through a buggy or malicious driver, or perhaps
1092 * the kernel debugger.
1094 * The kernel normally restores the "true" segment if this happens.
1096 * However, since we're restoring in C, not ASM, we can't detect
1097 * POP <SEG> since the actual instructions will be different.
1099 * A better technique would be to check the EIP and somehow edit the
1100 * trap frame before restarting the instruction -- but we would need to
1101 * know the extract instruction that was used first.
1103 * We could force a special instrinsic to use stack instructions, or write
1104 * a simple instruction length checker.
1106 * Nevertheless, this is a lot of work for the purpose of avoiding a crash
1107 * when the user is purposedly trying to create one from kernel-mode, so
1108 * we should probably table this for now since it's not a "real" issue.
1112 * NOTE2: Another scenario is the IRET during a V8086 restore (BIOS Call)
1113 * which will cause a GPF since the trap frame is a total mess (on purpose)
1114 * as built in KiEnterV86Mode.
1116 * The idea is to scan for IRET, scan for the known EIP adress, validate CS
1117 * and then manually issue a jump to the V8086 return EIP.
1119 Instructions
= (PUCHAR
)TrapFrame
->Eip
;
1120 if (Instructions
[0] == 0xCF)
1123 * Some evil shit is going on here -- this is not the SS:ESP you're
1124 * looking for! Instead, this is actually CS:EIP you're looking at!
1125 * Why? Because part of the trap frame actually corresponds to the IRET
1126 * stack during the trap exit!
1128 if ((TrapFrame
->HardwareEsp
== (ULONG
)Ki386BiosCallReturnAddress
) &&
1129 (TrapFrame
->HardwareSegSs
== (KGDT_R0_CODE
| RPL_MASK
)))
1131 /* Exit the V86 trap! */
1132 Ki386BiosCallReturnAddress(TrapFrame
);
1136 /* Otherwise, this is another kind of IRET fault */
1137 UNIMPLEMENTED_FATAL();
1141 /* So since we're not dealing with the above case, check for RDMSR/WRMSR */
1142 if ((Instructions
[0] == 0xF) && // 2-byte opcode
1143 ((Instructions
[1] == 0x32) || // RDMSR
1144 (Instructions
[1] == 0x30))) // WRMSR
1146 /* Unknown CPU MSR, so raise an access violation */
1147 KiDispatchException0Args(STATUS_ACCESS_VIOLATION
,
1152 /* Check for lazy segment load */
1153 if (TrapFrame
->SegDs
!= (KGDT_R3_DATA
| RPL_MASK
))
1156 TrapFrame
->SegDs
= (KGDT_R3_DATA
| RPL_MASK
);
1158 else if (TrapFrame
->SegEs
!= (KGDT_R3_DATA
| RPL_MASK
))
1161 TrapFrame
->SegEs
= (KGDT_R3_DATA
| RPL_MASK
);
1165 /* Whatever it is, we can't handle it */
1166 KiSystemFatalException(EXCEPTION_GP_FAULT
, TrapFrame
);
1169 /* Return to where we came from */
1170 KiTrapReturn(TrapFrame
);
1176 KiTrap0EHandler(IN PKTRAP_FRAME TrapFrame
)
1182 /* Save trap frame */
1183 KiEnterTrap(TrapFrame
);
1185 /* Check if this is the base frame */
1186 Thread
= KeGetCurrentThread();
1187 if (KeGetTrapFrame(Thread
) != TrapFrame
)
1189 /* It isn't, check if this is a second nested frame */
1190 if (((ULONG_PTR
)KeGetTrapFrame(Thread
) - (ULONG_PTR
)TrapFrame
) <=
1191 FIELD_OFFSET(KTRAP_FRAME
, EFlags
))
1193 /* The stack is somewhere in between frames, we need to fix it */
1194 UNIMPLEMENTED_FATAL();
1201 /* Enable interupts */
1204 /* Check if we came in with interrupts disabled */
1205 if (!(TrapFrame
->EFlags
& EFLAGS_INTERRUPT_MASK
))
1207 /* This is completely illegal, bugcheck the system */
1208 KeBugCheckWithTf(IRQL_NOT_LESS_OR_EQUAL
,
1211 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1216 /* Check for S-LIST fault in kernel mode */
1217 if (TrapFrame
->Eip
== (ULONG_PTR
)ExpInterlockedPopEntrySListFault
)
1219 PSLIST_HEADER SListHeader
;
1221 /* Sanity check that the assembly is correct:
1222 This must be mov ebx, [eax]
1223 Followed by cmpxchg8b [ebp] */
1224 ASSERT((((UCHAR
*)TrapFrame
->Eip
)[0] == 0x8B) &&
1225 (((UCHAR
*)TrapFrame
->Eip
)[1] == 0x18) &&
1226 (((UCHAR
*)TrapFrame
->Eip
)[2] == 0x0F) &&
1227 (((UCHAR
*)TrapFrame
->Eip
)[3] == 0xC7) &&
1228 (((UCHAR
*)TrapFrame
->Eip
)[4] == 0x4D) &&
1229 (((UCHAR
*)TrapFrame
->Eip
)[5] == 0x00));
1231 /* Get the pointer to the SLIST_HEADER */
1232 SListHeader
= (PSLIST_HEADER
)TrapFrame
->Ebp
;
1234 /* Check if the Next member of the SLIST_HEADER was changed */
1235 if (SListHeader
->Next
.Next
!= (PSLIST_ENTRY
)TrapFrame
->Eax
)
1237 /* Restart the operation */
1238 TrapFrame
->Eip
= (ULONG_PTR
)ExpInterlockedPopEntrySListResume
;
1240 /* Continue execution */
1241 KiEoiHelper(TrapFrame
);
1246 /* Do what windows does and issue an invalid access violation */
1247 KiDispatchException2Args(KI_EXCEPTION_ACCESS_VIOLATION
,
1249 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1256 /* Call the access fault handler */
1257 Status
= MmAccessFault(TrapFrame
->ErrCode
& 1,
1259 KiUserTrap(TrapFrame
),
1261 if (NT_SUCCESS(Status
)) KiEoiHelper(TrapFrame
);
1263 /* Check for syscall fault */
1265 if ((TrapFrame
->Eip
== (ULONG_PTR
)CopyParams
) ||
1266 (TrapFrame
->Eip
== (ULONG_PTR
)ReadBatch
))
1268 /* Not yet implemented */
1269 UNIMPLEMENTED_FATAL();
1272 /* Check for VDM trap */
1273 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
1275 /* Either kernel or user trap (non VDM) so dispatch exception */
1276 if (Status
== STATUS_ACCESS_VIOLATION
)
1278 /* This status code is repurposed so we can recognize it later */
1279 KiDispatchException2Args(KI_EXCEPTION_ACCESS_VIOLATION
,
1281 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1285 else if ((Status
== STATUS_GUARD_PAGE_VIOLATION
) ||
1286 (Status
== STATUS_STACK_OVERFLOW
))
1288 /* These faults only have two parameters */
1289 KiDispatchException2Args(Status
,
1291 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1296 /* Only other choice is an in-page error, with 3 parameters */
1297 KiDispatchExceptionFromTrapFrame(STATUS_IN_PAGE_ERROR
,
1300 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1309 KiTrap0FHandler(IN PKTRAP_FRAME TrapFrame
)
1311 /* Save trap frame */
1312 KiEnterTrap(TrapFrame
);
1314 /* FIXME: Kill the system */
1316 KiSystemFatalException(EXCEPTION_RESERVED_TRAP
, TrapFrame
);
1322 KiTrap10Handler(IN PKTRAP_FRAME TrapFrame
)
1325 PFX_SAVE_AREA SaveArea
;
1327 /* Save trap frame */
1328 KiEnterTrap(TrapFrame
);
1330 /* Check if this is the NPX thrad */
1331 Thread
= KeGetCurrentThread();
1332 SaveArea
= KiGetThreadNpxArea(Thread
);
1333 if (Thread
!= KeGetCurrentPrcb()->NpxThread
)
1335 /* It isn't, enable interrupts and set delayed error */
1337 SaveArea
->Cr0NpxState
|= CR0_TS
;
1340 KiEoiHelper(TrapFrame
);
1343 /* Otherwise, proceed with NPX fault handling */
1344 KiNpxHandler(TrapFrame
, Thread
, SaveArea
);
1350 KiTrap11Handler(IN PKTRAP_FRAME TrapFrame
)
1352 /* Save trap frame */
1353 KiEnterTrap(TrapFrame
);
1355 /* Enable interrupts and kill the system */
1357 KiSystemFatalException(EXCEPTION_ALIGNMENT_CHECK
, TrapFrame
);
1363 KiTrap13Handler(IN PKTRAP_FRAME TrapFrame
)
1366 PFX_SAVE_AREA SaveArea
;
1367 ULONG Cr0
, MxCsrMask
, Error
;
1369 /* Save trap frame */
1370 KiEnterTrap(TrapFrame
);
1372 /* Check if this is the NPX thrad */
1373 Thread
= KeGetCurrentThread();
1374 if (Thread
!= KeGetCurrentPrcb()->NpxThread
)
1376 /* It isn't, kill the system */
1377 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, (ULONG_PTR
)Thread
, 0, 0, TrapFrame
);
1380 /* Get the NPX frame */
1381 SaveArea
= KiGetThreadNpxArea(Thread
);
1383 /* Check for VDM trap */
1384 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
1386 /* Check for user trap */
1387 if (!KiUserTrap(TrapFrame
))
1389 /* Kernel should not fault on XMMI */
1390 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, 0, 0, 2, TrapFrame
);
1395 Cr0
&= ~(CR0_MP
| CR0_EM
| CR0_TS
);
1398 /* Save FPU state */
1399 Ke386SaveFpuState(SaveArea
);
1401 /* Mark CR0 state dirty */
1402 Cr0
|= NPX_STATE_NOT_LOADED
;
1403 Cr0
|= SaveArea
->Cr0NpxState
;
1406 /* Update NPX state */
1407 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
1408 KeGetCurrentPrcb()->NpxThread
= NULL
;
1410 /* Clear the TS bit and re-enable interrupts */
1411 SaveArea
->Cr0NpxState
&= ~CR0_TS
;
1414 /* Now look at MxCsr to get the mask of errors we should care about */
1415 MxCsrMask
= ~((USHORT
)SaveArea
->U
.FxArea
.MXCsr
>> 7);
1417 /* Get legal exceptions that software should handle */
1418 Error
= (USHORT
)SaveArea
->U
.FxArea
.MXCsr
& (FSW_INVALID_OPERATION
|
1426 /* Now handle any of those legal errors */
1427 if (Error
& (FSW_INVALID_OPERATION
|
1434 /* By issuing an exception */
1435 KiDispatchException1Args(STATUS_FLOAT_MULTIPLE_TRAPS
,
1441 /* Unknown XMMI fault */
1442 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, 0, 0, 1, TrapFrame
);
1445 /* SOFTWARE SERVICES **********************************************************/
1449 KiGetTickCountHandler(IN PKTRAP_FRAME TrapFrame
)
1451 UNIMPLEMENTED_DBGBREAK();
1456 KiCallbackReturnHandler(IN PKTRAP_FRAME TrapFrame
)
1460 /* Pass the register parameters to NtCallbackReturn.
1461 Result pointer is in ecx, result length in edx, status in eax */
1462 Status
= NtCallbackReturn((PVOID
)TrapFrame
->Ecx
,
1466 /* If we got here, something went wrong. Return an error to the caller */
1467 KiServiceExit(TrapFrame
, Status
);
1473 KiRaiseAssertionHandler(IN PKTRAP_FRAME TrapFrame
)
1475 /* Save trap frame */
1476 KiEnterTrap(TrapFrame
);
1478 /* Decrement EIP to point to the INT2C instruction (2 bytes, not 1 like INT3) */
1479 TrapFrame
->Eip
-= 2;
1481 /* Dispatch the exception */
1482 KiDispatchException0Args(STATUS_ASSERTION_FAILURE
,
1490 KiDebugServiceHandler(IN PKTRAP_FRAME TrapFrame
)
1492 /* Save trap frame */
1493 KiEnterTrap(TrapFrame
);
1495 /* Increment EIP to skip the INT3 instruction */
1498 /* Continue with the common handler */
1499 KiDebugHandler(TrapFrame
, TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1505 KiDbgPreServiceHook(ULONG SystemCallNumber
, PULONG_PTR Arguments
)
1507 #if DBG && !defined(_WINKD_)
1508 if (SystemCallNumber
>= 0x1000 && KeWin32PreServiceHook
)
1509 KeWin32PreServiceHook(SystemCallNumber
, Arguments
);
1515 KiDbgPostServiceHook(ULONG SystemCallNumber
, ULONG_PTR Result
)
1517 #if DBG && !defined(_WINKD_)
1518 if (SystemCallNumber
>= 0x1000 && KeWin32PostServiceHook
)
1519 return KeWin32PostServiceHook(SystemCallNumber
, Result
);
1527 KiSystemCall(IN PKTRAP_FRAME TrapFrame
,
1531 PKSERVICE_TABLE_DESCRIPTOR DescriptorTable
;
1532 ULONG Id
, Offset
, StackBytes
, Result
;
1534 ULONG SystemCallNumber
= TrapFrame
->Eax
;
1536 /* Get the current thread */
1537 Thread
= KeGetCurrentThread();
1539 /* Set debug header */
1540 KiFillTrapFrameDebug(TrapFrame
);
1542 /* Chain trap frames */
1543 TrapFrame
->Edx
= (ULONG_PTR
)Thread
->TrapFrame
;
1546 TrapFrame
->ErrCode
= 0;
1548 /* Save previous mode */
1549 TrapFrame
->PreviousPreviousMode
= Thread
->PreviousMode
;
1551 /* Save the SEH chain and terminate it for now */
1552 TrapFrame
->ExceptionList
= KeGetPcr()->NtTib
.ExceptionList
;
1553 KeGetPcr()->NtTib
.ExceptionList
= EXCEPTION_CHAIN_END
;
1555 /* Default to debugging disabled */
1558 /* Check if the frame was from user mode */
1559 if (KiUserTrap(TrapFrame
))
1561 /* Check for active debugging */
1562 if (KeGetCurrentThread()->Header
.DebugActive
& 0xFF)
1564 /* Handle debug registers */
1565 KiHandleDebugRegistersOnTrapEntry(TrapFrame
);
1569 /* Set thread fields */
1570 Thread
->TrapFrame
= TrapFrame
;
1571 Thread
->PreviousMode
= KiUserTrap(TrapFrame
);
1573 /* Enable interrupts */
1576 /* Decode the system call number */
1577 Offset
= (SystemCallNumber
>> SERVICE_TABLE_SHIFT
) & SERVICE_TABLE_MASK
;
1578 Id
= SystemCallNumber
& SERVICE_NUMBER_MASK
;
1580 /* Get descriptor table */
1581 DescriptorTable
= (PVOID
)((ULONG_PTR
)Thread
->ServiceTable
+ Offset
);
1583 /* Validate the system call number */
1584 if (__builtin_expect(Id
>= DescriptorTable
->Limit
, 0))
1586 /* Check if this is a GUI call */
1587 if (!(Offset
& SERVICE_TABLE_TEST
))
1590 Result
= STATUS_INVALID_SYSTEM_SERVICE
;
1594 /* Convert us to a GUI thread -- must wrap in ASM to get new EBP */
1595 Result
= KiConvertToGuiThread();
1597 /* Reload trap frame and descriptor table pointer from new stack */
1598 TrapFrame
= *(volatile PVOID
*)&Thread
->TrapFrame
;
1599 DescriptorTable
= (PVOID
)(*(volatile ULONG_PTR
*)&Thread
->ServiceTable
+ Offset
);
1601 if (!NT_SUCCESS(Result
))
1603 /* Set the last error and fail */
1604 //SetLastWin32Error(RtlNtStatusToDosError(Result));
1608 /* Validate the system call number again */
1609 if (Id
>= DescriptorTable
->Limit
)
1612 Result
= STATUS_INVALID_SYSTEM_SERVICE
;
1617 /* Check if this is a GUI call */
1618 if (__builtin_expect(Offset
& SERVICE_TABLE_TEST
, 0))
1620 /* Get the batch count and flush if necessary */
1621 if (NtCurrentTeb()->GdiBatchCount
) KeGdiFlushUserBatch();
1624 /* Increase system call count */
1625 KeGetCurrentPrcb()->KeSystemCalls
++;
1627 /* FIXME: Increase individual counts on debug systems */
1628 //KiIncreaseSystemCallCount(DescriptorTable, Id);
1630 /* Get stack bytes */
1631 StackBytes
= DescriptorTable
->Number
[Id
];
1633 /* Probe caller stack */
1634 if (__builtin_expect((Arguments
< (PVOID
)MmUserProbeAddress
) && !(KiUserTrap(TrapFrame
)), 0))
1636 /* Access violation */
1637 UNIMPLEMENTED_FATAL();
1640 /* Call pre-service debug hook */
1641 KiDbgPreServiceHook(SystemCallNumber
, Arguments
);
1643 /* Get the handler and make the system call */
1644 Handler
= (PVOID
)DescriptorTable
->Base
[Id
];
1645 Result
= KiSystemCallTrampoline(Handler
, Arguments
, StackBytes
);
1647 /* Call post-service debug hook */
1648 Result
= KiDbgPostServiceHook(SystemCallNumber
, Result
);
1650 /* Make sure we're exiting correctly */
1651 KiExitSystemCallDebugChecks(Id
, TrapFrame
);
1653 /* Restore the old trap frame */
1655 Thread
->TrapFrame
= (PKTRAP_FRAME
)TrapFrame
->Edx
;
1657 /* Exit from system call */
1658 KiServiceExit(TrapFrame
, Result
);
1664 KiSystemServiceHandler(IN PKTRAP_FRAME TrapFrame
,
1667 /* Call the shared handler (inline) */
1668 KiSystemCall(TrapFrame
, Arguments
);
1674 KiFastCallEntryHandler(IN PKTRAP_FRAME TrapFrame
,
1677 /* Set up a fake INT Stack and enable interrupts */
1678 TrapFrame
->HardwareSegSs
= KGDT_R3_DATA
| RPL_MASK
;
1679 TrapFrame
->HardwareEsp
= (ULONG_PTR
)Arguments
;
1680 TrapFrame
->EFlags
= __readeflags() | EFLAGS_INTERRUPT_MASK
;
1681 TrapFrame
->SegCs
= KGDT_R3_CODE
| RPL_MASK
;
1682 TrapFrame
->Eip
= SharedUserData
->SystemCallReturn
;
1683 TrapFrame
->SegFs
= KGDT_R3_TEB
| RPL_MASK
;
1686 /* Arguments are actually 2 frames down (because of the double indirection) */
1687 Arguments
= (PVOID
)(TrapFrame
->HardwareEsp
+ 8);
1689 /* Call the shared handler (inline) */
1690 KiSystemCall(TrapFrame
, Arguments
);
1698 Kei386EoiHelper(VOID
)
1700 /* We should never see this call happening */
1701 ERROR_FATAL("Mismatched NT/HAL version");