Trap handlers in C patch 6 of X:

[NTOS]: Implement Trap 7 and 16 in C. These are the last two FPU/NPX traps. Not really good with x86 FPU stuff, so there might be some mistakes to look over later.
    [NTOS]: Fixed a bug in Trap 19 handler (forgot to write CR0 back).

svn path=/trunk/; revision=45014
This commit is contained in:
Sir Richard
2010-01-09 15:07:44 +00:00
parent 1b346fdad3
commit f1e6ecbc60
4 changed files with 319 additions and 339 deletions
@@ -27,7 +27,21 @@ Ke386SaveFpuState(IN PFX_SAVE_AREA SaveArea)
{
__asm__ __volatile__ ("fnsave %0\n wait\n" : : "m"(SaveArea));
}
}
FORCEINLINE
VOID
Ke386LoadFpuState(IN PFX_SAVE_AREA SaveArea)
{
extern ULONG KeI386FxsrPresent;
if (KeI386FxsrPresent)
{
__asm__ __volatile__ ("fxrstor %0\n" : "=m"(SaveArea) : );
}
else
{
__asm__ __volatile__ (".globl _FrRestore\n _FrRestore: \n frstor %0\n wait\n" : "=m"(SaveArea) : );
}
}
FORCEINLINE
@@ -137,6 +137,13 @@ KiVdmTrap(IN PKTRAP_FRAME TrapFrame)
((KiUserTrap(TrapFrame)) && (PsGetCurrentProcess()->VdmObjects)));
}
PFX_SAVE_AREA
FORCEINLINE
KiGetThreadNpxArea(IN PKTHREAD Thread)
{
return (PFX_SAVE_AREA)((ULONG_PTR)Thread->InitialStack - sizeof(FX_SAVE_AREA));
}
VOID
FORCEINLINE
KiTrapFrameFromPushaStack(IN PKTRAP_FRAME TrapFrame)
+2 -338
View File
@@ -693,317 +693,7 @@ GENERATE_TRAP_HANDLER KiTrap3, 1
GENERATE_TRAP_HANDLER KiTrap4, 1
GENERATE_TRAP_HANDLER KiTrap5, 1
GENERATE_TRAP_HANDLER KiTrap6, 1
.func KiTrap7
TRAP_FIXUPS kit7_a, kit7_t, DoFixupV86, DoNotFixupAbios
_KiTrap7:
/* Push error code */
push 0
/* Enter trap */
TRAP_PROLOG kit7_a, kit7_t
/* Get the current thread and stack */
StartTrapHandle:
mov eax, PCR[KPCR_CURRENT_THREAD]
mov ecx, [eax+KTHREAD_INITIAL_STACK]
sub ecx, NPX_FRAME_LENGTH
/* Check if emulation is enabled */
test byte ptr [ecx+FN_CR0_NPX_STATE], CR0_EM
jnz EmulationEnabled
CheckState:
/* Check if the NPX state is loaded */
cmp byte ptr [eax+KTHREAD_NPX_STATE], NPX_STATE_LOADED
mov ebx, cr0
jz IsLoaded
/* Remove flags */
and ebx, ~(CR0_MP + CR0_TS + CR0_EM)
mov cr0, ebx
/* Check the NPX thread */
mov edx, PCR[KPCR_NPX_THREAD]
or edx, edx
jz NoNpxThread
/* Get the NPX Stack */
mov esi, [edx+KTHREAD_INITIAL_STACK]
sub esi, NPX_FRAME_LENGTH
/* Check if we have FXSR and check which operand to use */
test byte ptr _KeI386FxsrPresent, 1
jz FnSave
fxsave [esi]
jmp AfterSave
FnSave:
fnsave [esi]
AfterSave:
/* Set the thread's state to dirty */
mov byte ptr [edx+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED
NoNpxThread:
/* Check if we have FXSR and choose which operand to use */
test byte ptr _KeI386FxsrPresent, 1
jz FrRestore
fxrstor [ecx]
jmp AfterRestore
FrRestore:
frstor [ecx]
AfterRestore:
/* Set state loaded */
mov byte ptr [eax+KTHREAD_NPX_STATE], NPX_STATE_LOADED
mov PCR[KPCR_NPX_THREAD], eax
/* Enable interrupts to happen now */
sti
nop
/* Check if CR0 needs to be reloaded due to a context switch */
cmp dword ptr [ecx+FN_CR0_NPX_STATE], 0
jz _Kei386EoiHelper@0
/* We have to reload CR0... disable interrupts */
cli
/* Get CR0 and update it */
mov ebx, cr0
or ebx, [ecx+FN_CR0_NPX_STATE]
mov cr0, ebx
/* Restore interrupts and check if TS is back on */
sti
test bl, CR0_TS
jz _Kei386EoiHelper@0
/* Clear TS, and loop handling again */
clts
cli
jmp StartTrapHandle
KernelNpx:
/* Set delayed error */
or dword ptr [ecx+FN_CR0_NPX_STATE], CR0_TS
/* Check if this happened during restore */
cmp dword ptr [ebp+KTRAP_FRAME_EIP], offset FrRestore
jnz UserNpx
/* Skip instruction and dispatch the exception */
add dword ptr [ebp+KTRAP_FRAME_EIP], 3
jmp _Kei386EoiHelper@0
IsLoaded:
/* Check if TS is set */
test bl, CR0_TS
jnz TsSetOnLoadedState
HandleNpxFault:
/* Check if the trap came from V86 mode */
test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
jnz V86Npx
/* Check if it came from kernel mode */
test byte ptr [ebp+KTRAP_FRAME_CS], MODE_MASK
jz KernelNpx
/* Check if it came from a VDM */
cmp word ptr [ebp+KTRAP_FRAME_CS], KGDT_R3_CODE + RPL_MASK
jne V86Npx
UserNpx:
/* Get the current thread */
mov eax, PCR[KPCR_CURRENT_THREAD]
/* Check NPX state */
cmp byte ptr [eax+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED
/* Get the NPX save area */
mov ecx, [eax+KTHREAD_INITIAL_STACK]
lea ecx, [ecx-NPX_FRAME_LENGTH]
jz NoSaveRestore
HandleUserNpx:
/* Set new CR0 */
mov ebx, cr0
and ebx, ~(CR0_MP + CR0_EM + CR0_TS)
mov cr0, ebx
/* Check if we have FX support */
test byte ptr _KeI386FxsrPresent, 1
jz FnSave2
/* Save the state */
fxsave [ecx]
jmp MakeCr0Dirty
FnSave2:
fnsave [ecx]
wait
MakeCr0Dirty:
/* Make CR0 state not loaded */
or ebx, NPX_STATE_NOT_LOADED
or ebx, [ecx+FN_CR0_NPX_STATE]
mov cr0, ebx
/* Update NPX state */
mov byte ptr [eax+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED
mov dword ptr PCR[KPCR_NPX_THREAD], 0
NoSaveRestore:
/* Clear the TS bit and re-enable interrupts */
and dword ptr [ecx+FN_CR0_NPX_STATE], ~CR0_TS
sti
/* Check if we have FX support */
test byte ptr _KeI386FxsrPresent, 1
jz FnError
/* Get error offset, control and status words */
mov ebx, [ecx+FX_ERROR_OFFSET]
movzx eax, word ptr [ecx+FX_CONTROL_WORD]
movzx edx, word ptr [ecx+FX_STATUS_WORD]
/* Get the faulting opcode */
mov esi, [ecx+FX_DATA_OFFSET]
jmp CheckError
FnError:
/* Get error offset, control and status words */
mov ebx, [ecx+FP_ERROR_OFFSET]
movzx eax, word ptr [ecx+FP_CONTROL_WORD]
movzx edx, word ptr [ecx+FP_STATUS_WORD]
/* Get the faulting opcode */
mov esi, [ecx+FP_DATA_OFFSET]
CheckError:
/* Mask exceptions */
and eax, 0x3F
not eax
and eax, edx
/* Check if what's left is invalid */
test al, 1
jz ValidNpxOpcode
/* Check if it was a stack fault */
test al, 64
jnz InvalidStack
/* Raise exception */
mov eax, STATUS_FLOAT_INVALID_OPERATION
jmp _DispatchOneParamZero
InvalidStack:
/* Raise exception */
mov eax, STATUS_FLOAT_STACK_CHECK
jmp _DispatchTwoParamZero
ValidNpxOpcode:
/* Check for divide by 0 */
test al, 4
jz 1f
/* Raise exception */
mov eax, STATUS_FLOAT_DIVIDE_BY_ZERO
jmp _DispatchOneParamZero
1:
/* Check for denormal */
test al, 2
jz 1f
/* Raise exception */
mov eax, STATUS_FLOAT_INVALID_OPERATION
jmp _DispatchOneParamZero
1:
/* Check for overflow */
test al, 8
jz 1f
/* Raise exception */
mov eax, STATUS_FLOAT_OVERFLOW
jmp _DispatchOneParamZero
1:
/* Check for underflow */
test al, 16
jz 1f
/* Raise exception */
mov eax, STATUS_FLOAT_UNDERFLOW
jmp _DispatchOneParamZero
1:
/* Check for precision fault */
test al, 32
jz UnexpectedNpx
/* Raise exception */
mov eax, STATUS_FLOAT_INEXACT_RESULT
jmp _DispatchOneParamZero
UnexpectedNpx:
/* Strange result, bugcheck the OS */
sti
push ebp
push 0
push 0
push eax
push 1
push TRAP_CAUSE_UNKNOWN
call _KeBugCheckWithTf@24
V86Npx:
/* Check if this is a VDM */
mov eax, PCR[KPCR_CURRENT_THREAD]
mov ebx, [eax+KTHREAD_APCSTATE_PROCESS]
cmp dword ptr [ebx+EPROCESS_VDM_OBJECTS], 0
jz HandleUserNpx
/* V86 NPX not handled */
UNHANDLED_V86_PATH
EmulationEnabled:
/* Did this come from kernel-mode? */
cmp word ptr [ebp+KTRAP_FRAME_CS], KGDT_R0_CODE
jz CheckState
/* It came from user-mode, so this would only be valid inside a VDM */
/* Since we don't actually have VDMs in ROS, bugcheck. */
jmp UnexpectedNpx
TsSetOnLoadedState:
/* TS shouldn't be set, unless this we don't have a Math Processor */
test bl, CR0_MP
jnz BogusTrap
/* Strange that we got a trap at all, but ignore and continue */
clts
jmp _Kei386EoiHelper@0
BogusTrap:
/* Cause a bugcheck */
push 0
push 0
push ebx
push 2
push TRAP_CAUSE_UNKNOWN
call _KeBugCheckEx@20
.endfunc
GENERATE_TRAP_HANDLER KiTrap7, 1
GENERATE_TRAP_HANDLER KiTrap8, 0
GENERATE_TRAP_HANDLER KiTrap9, 1
GENERATE_TRAP_HANDLER KiTrap10, 0
@@ -1496,33 +1186,7 @@ DispatchV86Gpf:
GENERATE_TRAP_HANDLER KiTrap14, 0
GENERATE_TRAP_HANDLER KiTrap0F, 1
.func KiTrap16
TRAP_FIXUPS kit10_a, kit10_t, DoFixupV86, DoNotFixupAbios
_KiTrap16:
/* Push error code */
push 0
/* Enter trap */
TRAP_PROLOG kit10_a, kit10_t
/* Check if this is the NPX Thread */
mov eax, PCR[KPCR_CURRENT_THREAD]
cmp eax, PCR[KPCR_NPX_THREAD]
/* Get the initial stack and NPX frame */
mov ecx, [eax+KTHREAD_INITIAL_STACK]
lea ecx, [ecx-NPX_FRAME_LENGTH]
/* If this is a valid fault, handle it */
jz HandleNpxFault
/* Otherwise, re-enable interrupts and set delayed error */
sti
or dword ptr [ecx+FN_CR0_NPX_STATE], CR0_TS
jmp _Kei386EoiHelper@0
.endfunc
GENERATE_TRAP_HANDLER KiTrap16, 1
GENERATE_TRAP_HANDLER KiTrap17, 1
GENERATE_TRAP_HANDLER KiTrap19, 1
+296 -1
View File
@@ -251,6 +251,163 @@ KiDebugHandler(IN PKTRAP_FRAME TrapFrame,
TrapFrame);
}
VOID
FASTCALL
KiNpxHandler(IN PKTRAP_FRAME TrapFrame,
IN PKTHREAD Thread,
IN PFX_SAVE_AREA SaveArea)
{
ULONG Cr0, Mask, Error, ErrorOffset, DataOffset;
extern VOID FrRestore(VOID);
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Check for kernel trap */
if (!KiUserTrap(TrapFrame))
{
/* Kernel might've tripped a delayed error */
SaveArea->Cr0NpxState |= CR0_TS;
/* Only valid if it happened during a restore */
if ((PVOID)TrapFrame->Eip == FrRestore)
{
/* It did, so just skip the instruction */
TrapFrame->Eip += 3; /* sizeof(FRSTOR) */
KiEoiHelper(TrapFrame);
}
}
/* User or kernel trap -- get ready to issue an exception */
if (Thread->NpxState == NPX_STATE_NOT_LOADED)
{
/* Update CR0 */
Cr0 = __readcr0();
Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
__writecr0(Cr0);
/* Save FPU state */
Ke386SaveFpuState(SaveArea);
/* Mark CR0 state dirty */
Cr0 |= NPX_STATE_NOT_LOADED;
Cr0 |= SaveArea->Cr0NpxState;
__writecr0(Cr0);
/* Update NPX state */
Thread->NpxState = NPX_STATE_NOT_LOADED;
KeGetCurrentPrcb()->NpxThread = NULL;
}
/* Clear the TS bit and re-enable interrupts */
SaveArea->Cr0NpxState &= ~CR0_TS;
_enable();
/* Check if we should get the FN or FX error */
if (KeI386FxsrPresent)
{
/* Get it from FX */
Mask = SaveArea->U.FxArea.ControlWord;
Error = SaveArea->U.FxArea.StatusWord;
/* Get the FPU exception address too */
ErrorOffset = SaveArea->U.FxArea.ErrorOffset;
DataOffset = SaveArea->U.FxArea.DataOffset;
}
else
{
/* Get it from FN */
Mask = SaveArea->U.FnArea.ControlWord;
Error = SaveArea->U.FnArea.StatusWord;
/* Get the FPU exception address too */
ErrorOffset = SaveArea->U.FnArea.ErrorOffset;
DataOffset = SaveArea->U.FnArea.DataOffset;
}
/* Get legal exceptions that software should handle */
Error &= (FSW_INVALID_OPERATION |
FSW_DENORMAL |
FSW_ZERO_DIVIDE |
FSW_OVERFLOW |
FSW_UNDERFLOW |
FSW_PRECISION);
Error &= ~Mask;
if (Error & FSW_STACK_FAULT)
{
/* Issue stack check fault */
KiDispatchException2Args(STATUS_FLOAT_STACK_CHECK,
ErrorOffset,
0,
DataOffset,
TrapFrame);
}
/* Check for invalid operation */
if (Error & FSW_INVALID_OPERATION)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION,
ErrorOffset,
0,
TrapFrame);
}
/* Check for divide by zero */
if (Error & FSW_ZERO_DIVIDE)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_DIVIDE_BY_ZERO,
ErrorOffset,
0,
TrapFrame);
}
/* Check for denormal */
if (Error & FSW_DENORMAL)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION,
ErrorOffset,
0,
TrapFrame);
}
/* Check for overflow */
if (Error & FSW_OVERFLOW)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_OVERFLOW,
ErrorOffset,
0,
TrapFrame);
}
/* Check for underflow */
if (Error & FSW_UNDERFLOW)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_UNDERFLOW,
ErrorOffset,
0,
TrapFrame);
}
/* Check for precision fault */
if (Error & FSW_PRECISION)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_INEXACT_RESULT,
ErrorOffset,
0,
TrapFrame);
}
/* Unknown FPU fault */
KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 1, Error, 0, 0, TrapFrame);
}
VOID
FASTCALL
KiTrap0Handler(IN PKTRAP_FRAME TrapFrame)
@@ -387,6 +544,115 @@ KiTrap6Handler(IN PKTRAP_FRAME TrapFrame)
}
VOID
FASTCALL
KiTrap7Handler(IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread, NpxThread;
PFX_SAVE_AREA SaveArea, NpxSaveArea;
ULONG Cr0;
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Try to handle NPX delay load */
while (TRUE)
{
/* Get the current thread */
Thread = KeGetCurrentThread();
/* Get the NPX frame */
SaveArea = KiGetThreadNpxArea(Thread);
/* Check if emulation is enabled */
if (SaveArea->Cr0NpxState & CR0_EM)
{
/* Not implemented */
UNIMPLEMENTED;
while (TRUE);
}
/* Save CR0 and check NPX state */
Cr0 = __readcr0();
if (Thread->NpxState != NPX_STATE_LOADED)
{
/* Update CR0 */
Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
__writecr0(Cr0);
/* Get the NPX thread */
NpxThread = KeGetCurrentPrcb()->NpxThread;
if (NpxThread)
{
/* Get the NPX frame */
NpxSaveArea = KiGetThreadNpxArea(NpxThread);
/* Save FPU state */
Ke386SaveFpuState(NpxSaveArea);
/* Update NPX state */
Thread->NpxState = NPX_STATE_NOT_LOADED;
}
/* Load FPU state */
Ke386LoadFpuState(SaveArea);
/* Update NPX state */
Thread->NpxState = NPX_STATE_LOADED;
KeGetCurrentPrcb()->NpxThread = Thread;
/* Enable interrupts */
_enable();
/* Check if CR0 needs to be reloaded due to context switch */
if (!SaveArea->Cr0NpxState) KiEoiHelper(TrapFrame);
/* Otherwise, we need to reload CR0, disable interrupts */
_disable();
/* Reload CR0 */
Cr0 = __readcr0();
Cr0 |= SaveArea->Cr0NpxState;
__writecr0(Cr0);
/* Now restore interrupts and check for TS */
_enable();
if (Cr0 & CR0_TS) KiEoiHelper(TrapFrame);
/* We're still here -- clear TS and try again */
__writecr0(__readcr0() &~ CR0_TS);
_disable();
}
else
{
/* This is an actual fault, not a lack of FPU state */
break;
}
}
/* TS should not be set */
if (Cr0 & CR0_TS)
{
/*
* If it's incorrectly set, then maybe the state is actually still valid
* but we could've lock track of that due to a BIOS call.
* Make sure MP is still set, which should verify the theory.
*/
if (Cr0 & CR0_MP)
{
/* Indeed, the state is actually still valid, so clear TS */
__writecr0(__readcr0() &~ CR0_TS);
KiEoiHelper(TrapFrame);
}
/* Otherwise, something strange is going on */
KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 2, Cr0, 0, 0, TrapFrame);
}
/* It's not a delayed load, so process this trap as an NPX fault */
KiNpxHandler(TrapFrame, Thread, SaveArea);
}
VOID
FASTCALL
KiTrap8Handler(IN PKTRAP_FRAME TrapFrame)
@@ -581,6 +847,34 @@ KiTrap0FHandler(IN PKTRAP_FRAME TrapFrame)
KiSystemFatalException(EXCEPTION_RESERVED_TRAP, TrapFrame);
}
VOID
FASTCALL
KiTrap16Handler(IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread;
PFX_SAVE_AREA SaveArea;
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check if this is the NPX thrad */
Thread = KeGetCurrentThread();
SaveArea = KiGetThreadNpxArea(Thread);
if (Thread != KeGetCurrentPrcb()->NpxThread)
{
/* It isn't, enable interrupts and set delayed error */
_enable();
SaveArea->Cr0NpxState |= CR0_TS;
/* End trap */
KiEoiHelper(TrapFrame);
}
/* Otherwise, proceed with NPX fault handling */
KiNpxHandler(TrapFrame, Thread, SaveArea);
}
VOID
FASTCALL
KiTrap17Handler(IN PKTRAP_FRAME TrapFrame)
@@ -613,7 +907,7 @@ KiTrap19Handler(IN PKTRAP_FRAME TrapFrame)
}
/* Get the NPX frame */
SaveArea = (PFX_SAVE_AREA)((ULONG_PTR)Thread->InitialStack - sizeof(FX_SAVE_AREA));
SaveArea = KiGetThreadNpxArea(Thread);
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
@@ -636,6 +930,7 @@ KiTrap19Handler(IN PKTRAP_FRAME TrapFrame)
/* Mark CR0 state dirty */
Cr0 |= NPX_STATE_NOT_LOADED;
Cr0 |= SaveArea->Cr0NpxState;
__writecr0(Cr0);
/* Update NPX state */
Thread->NpxState = NPX_STATE_NOT_LOADED;