diff --git a/reactos/ntoskrnl/include/internal/i386/fpu.h b/reactos/ntoskrnl/include/internal/i386/fpu.h index b23db2b0a90..bee120bf41e 100644 --- a/reactos/ntoskrnl/include/internal/i386/fpu.h +++ b/reactos/ntoskrnl/include/internal/i386/fpu.h @@ -12,10 +12,13 @@ NTSTATUS KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr); VOID -KiFloatingSaveAreaToFxSaveArea(PFX_SAVE_AREA FxSaveArea, CONST FLOATING_SAVE_AREA *FloatingSaveArea); +KiFxSaveAreaToFloatingSaveArea(FLOATING_SAVE_AREA *FloatingSaveArea, CONST PFX_SAVE_AREA FxSaveArea); BOOL KiContextToFxSaveArea(PFX_SAVE_AREA FxSaveArea, PCONTEXT Context); +PFX_SAVE_AREA +KiGetFpuState(PKTHREAD Thread); + #endif /* __NTOSKRNL_INCLUDE_INTERNAL_I386_FPU_H */ diff --git a/reactos/ntoskrnl/ke/i386/exp.c b/reactos/ntoskrnl/ke/i386/exp.c index 8f0cc2f35a2..8271fc73c67 100644 --- a/reactos/ntoskrnl/ke/i386/exp.c +++ b/reactos/ntoskrnl/ke/i386/exp.c @@ -820,6 +820,8 @@ KeTrapFrameToContext(IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame, IN OUT PCONTEXT Context) { + PFX_SAVE_AREA FxSaveArea = NULL; + /* Start with the Control flags */ if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) { @@ -890,40 +892,39 @@ KeTrapFrameToContext(IN PKTRAP_FRAME TrapFrame, Context->Edi = TrapFrame->Edi; } - if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS) - { - /* - * FIXME: Implement this case - */ - Context->ContextFlags &= (~CONTEXT_DEBUG_REGISTERS) | CONTEXT_i386; - } - if ((Context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) - { - /* - * FIXME: Implement this case - * - * I think this should only be filled for FPU exceptions, otherwise I - * would not know where to get it from as it can be the current state - * of the FPU or already saved in the thread's FPU save area. - * -blight - */ - Context->ContextFlags &= (~CONTEXT_FLOATING_POINT) | CONTEXT_i386; - } -#if 0 - if ((Context->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) - { - /* - * FIXME: Investigate this - * - * This is the XMM state (first 512 bytes of FXSAVE_FORMAT/FX_SAVE_AREA) - * This should only be filled in case of a SIMD exception I think, so - * this is not the right place (like for FPU the state could already be - * saved in the thread's FX_SAVE_AREA or still be in the CPU) - * -blight - */ - Context->ContextFlags &= ~CONTEXT_EXTENDED_REGISTERS; - } -#endif + if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS) + { + /* + * FIXME: Implement this case + */ + Context->ContextFlags &= (~CONTEXT_DEBUG_REGISTERS) | CONTEXT_i386; + } + if ((Context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) + { + FxSaveArea = KiGetFpuState(KeGetCurrentThread()); + if (FxSaveArea != NULL) + { + KiFxSaveAreaToFloatingSaveArea(&Context->FloatSave, FxSaveArea); + } + else + { + Context->ContextFlags &= (~CONTEXT_FLOATING_POINT) | CONTEXT_i386; + } + } + if ((Context->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) + { + if (FxSaveArea == NULL) + FxSaveArea = KiGetFpuState(KeGetCurrentThread()); + if (FxSaveArea != NULL) + { + memcpy(Context->ExtendedRegisters, &FxSaveArea->U.FxArea, + min(sizeof (Context->ExtendedRegisters), sizeof (FxSaveArea->U.FxArea)) ); + } + else + { + Context->ContextFlags &= (~CONTEXT_EXTENDED_REGISTERS) | CONTEXT_i386; + } + } } VOID @@ -1180,8 +1181,11 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, /* Check if User Mode */ if (PreviousMode == UserMode) { + extern ULONG FxsrSupport; /* Add the FPU Flag */ Context.ContextFlags |= CONTEXT_FLOATING_POINT; + if (FxsrSupport) + Context.ContextFlags |= CONTEXT_EXTENDED_REGISTERS; } /* Get a Context */ @@ -1259,7 +1263,7 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, /* Probe stack and copy Context */ ProbeForWrite((PVOID)Stack, Size, sizeof(ULONG)); - RtlMoveMemory((PVOID)Stack, &Context, sizeof(CONTEXT)); + RtlCopyMemory((PVOID)Stack, &Context, sizeof(CONTEXT)); /* Align exception record size and get stack pointer */ Size = (sizeof(EXCEPTION_RECORD) - @@ -1272,7 +1276,7 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, ProbeForWrite((PVOID)(NewStack - 2 * sizeof(ULONG_PTR)), Size + 2 * sizeof(ULONG_PTR), sizeof(ULONG)); - RtlMoveMemory((PVOID)NewStack, ExceptionRecord, Size); + RtlCopyMemory((PVOID)NewStack, ExceptionRecord, Size); /* Now write the two params for the user-mode dispatcher */ *(PULONG_PTR)(NewStack - 1 * sizeof(ULONG_PTR)) = Stack; diff --git a/reactos/ntoskrnl/ke/i386/fpu.c b/reactos/ntoskrnl/ke/i386/fpu.c index 22be8a23e45..901885c990d 100644 --- a/reactos/ntoskrnl/ke/i386/fpu.c +++ b/reactos/ntoskrnl/ke/i386/fpu.c @@ -62,6 +62,7 @@ KiTagWordFnsaveToFxsave(USHORT TagWord) return tmp; } + STATIC USHORT KiTagWordFxsaveToFnsave(PFXSAVE_FORMAT FxSave) { @@ -107,6 +108,7 @@ KiTagWordFxsaveToFnsave(PFXSAVE_FORMAT FxSave) return TagWord; } + STATIC VOID KiFnsaveToFxsaveFormat(PFXSAVE_FORMAT FxSave, CONST PFNSAVE_FORMAT FnSave) { @@ -154,7 +156,8 @@ KiFxsaveToFnsaveFormat(PFNSAVE_FORMAT FnSave, CONST PFXSAVE_FORMAT FxSave) } } -VOID + +STATIC VOID KiFloatingSaveAreaToFxSaveArea(PFX_SAVE_AREA FxSaveArea, CONST FLOATING_SAVE_AREA *FloatingSaveArea) { if (FxsrSupport) @@ -169,6 +172,22 @@ KiFloatingSaveAreaToFxSaveArea(PFX_SAVE_AREA FxSaveArea, CONST FLOATING_SAVE_ARE FxSaveArea->Cr0NpxState = FloatingSaveArea->Cr0NpxState; } + +VOID +KiFxSaveAreaToFloatingSaveArea(FLOATING_SAVE_AREA *FloatingSaveArea, CONST PFX_SAVE_AREA FxSaveArea) +{ + if (FxsrSupport) + { + KiFxsaveToFnsaveFormat((PFNSAVE_FORMAT)FloatingSaveArea, &FxSaveArea->U.FxArea); + } + else + { + memcpy(FloatingSaveArea, &FxSaveArea->U.FnArea, sizeof(FxSaveArea->U.FnArea)); + } + FloatingSaveArea->Cr0NpxState = FxSaveArea->Cr0NpxState; +} + + BOOL KiContextToFxSaveArea(PFX_SAVE_AREA FxSaveArea, PCONTEXT Context) { @@ -214,6 +233,7 @@ KiContextToFxSaveArea(PFX_SAVE_AREA FxSaveArea, PCONTEXT Context) return FpuContextChanged; } + VOID INIT_FUNCTION KiCheckFPU(VOID) { @@ -300,68 +320,38 @@ KiCheckFPU(VOID) Ke386RestoreFlags(Flags); } -/* This is a rather naive implementation of Ke(Save/Restore)FloatingPointState - which will not work for WDM drivers. Please feel free to improve */ -#define FPU_STATE_SIZE 108 - -NTSTATUS STDCALL -KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save) +PFX_SAVE_AREA +KiGetFpuState(PKTHREAD Thread) { - char *FpState; + PFX_SAVE_AREA FxSaveArea = NULL; + KIRQL OldIrql; - ASSERT_IRQL(DISPATCH_LEVEL); - - /* check if we are doing software emulation */ - if (!HardwareMathSupport) + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + if (Thread->NpxState & NPX_STATE_VALID) { - return STATUS_ILLEGAL_FLOAT_CONTEXT; + FxSaveArea = (PFX_SAVE_AREA)((ULONG_PTR)Thread->InitialStack - sizeof (FX_SAVE_AREA)); + if (Thread->NpxState & NPX_STATE_DIRTY) + { + ASSERT(KeGetCurrentPrcb()->NpxThread == Thread); + ASSERT((Ke386GetCr0() & X86_CR0_TS) == 0); + + if (FxsrSupport) + asm volatile("fxsave %0" : : "m"(FxSaveArea->U.FxArea)); + else + { + KeGetCurrentPrcb()->NpxThread = NULL; + asm volatile("fnsave %0" : : "m"(FxSaveArea->U.FnArea)); + Ke386SetCr0(Ke386GetCr0() | X86_CR0_TS); /* FPU state has to be reloaded because fnsave changes it. */ + } + Thread->NpxState = NPX_STATE_VALID; + } } + KeLowerIrql(OldIrql); - FpState = ExAllocatePool(NonPagedPool, FPU_STATE_SIZE); - if (NULL == FpState) - { - return STATUS_INSUFFICIENT_RESOURCES; - } - *((PVOID *) Save) = FpState; - -#if defined(__GNUC__) - asm volatile("fsave %0\n\t" : "=m" (*FpState)); -#elif defined(_MSC_VER) - __asm mov eax, FpState; - __asm fsave [eax]; -#else -#error Unknown compiler for inline assembler -#endif - - KeGetCurrentThread()->NpxIrql = KeGetCurrentIrql(); - - return STATUS_SUCCESS; + return FxSaveArea; } -NTSTATUS STDCALL -KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save) -{ - char *FpState = *((PVOID *) Save); - - if (KeGetCurrentThread()->NpxIrql != KeGetCurrentIrql()) - { - KEBUGCHECK(UNDEFINED_BUG_CODE); - } - -#if defined(__GNUC__) - __asm__("frstor %0\n\t" : "=m" (*FpState)); -#elif defined(_MSC_VER) - __asm mov eax, FpState; - __asm frstor [eax]; -#else -#error Unknown compiler for inline assembler -#endif - - ExFreePool(FpState); - - return STATUS_SUCCESS; -} NTSTATUS KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) @@ -403,7 +393,7 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) if (NpxThread != NULL) { KeGetCurrentPrcb()->NpxThread = NULL; - FxSaveArea = (PFX_SAVE_AREA)((char *)NpxThread->InitialStack - sizeof (FX_SAVE_AREA)); + FxSaveArea = (PFX_SAVE_AREA)((ULONG_PTR)NpxThread->InitialStack - sizeof (FX_SAVE_AREA)); /* the fnsave might raise a delayed #MF exception */ if (FxsrSupport) { @@ -420,17 +410,17 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) /* restore the state of the current thread */ ASSERT((CurrentThread->NpxState & NPX_STATE_DIRTY) == 0); - FxSaveArea = (PFX_SAVE_AREA)((char *)CurrentThread->InitialStack - sizeof (FX_SAVE_AREA)); + FxSaveArea = (PFX_SAVE_AREA)((ULONG_PTR)CurrentThread->InitialStack - sizeof (FX_SAVE_AREA)); if (CurrentThread->NpxState & NPX_STATE_VALID) { if (FxsrSupport) { - FxSaveArea->U.FxArea.MXCsr &= MxcsrFeatureMask; - asm volatile("fxrstor %0" : : "m"(FxSaveArea->U.FxArea)); + FxSaveArea->U.FxArea.MXCsr &= MxcsrFeatureMask; + asm volatile("fxrstor %0" : : "m"(FxSaveArea->U.FxArea)); } else { - asm volatile("frstor %0" : : "m"(FxSaveArea->U.FnArea)); + asm volatile("frstor %0" : : "m"(FxSaveArea->U.FnArea)); } } else /* NpxState & NPX_STATE_INVALID */ @@ -448,7 +438,7 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) } else if (!FpuInitialized) { - asm volatile("finit"); + asm volatile("fninit"); } } KeGetCurrentPrcb()->NpxThread = CurrentThread; @@ -465,24 +455,31 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) else /* ExceptionNr == 16 || ExceptionNr == 19 */ { EXCEPTION_RECORD Er; - UCHAR DummyContext[sizeof(CONTEXT) + 16]; - PCONTEXT Context; KPROCESSOR_MODE PreviousMode; PKTHREAD CurrentThread, NpxThread; - KIRQL oldIrql; + KIRQL OldIrql; + ULONG FpuEnvBuffer[7]; + PFNSAVE_FORMAT FpuEnv = (PFNSAVE_FORMAT)FpuEnvBuffer; ASSERT(ExceptionNr == 16 || ExceptionNr == 19); /* math fault or XMM fault*/ - KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); NpxThread = KeGetCurrentPrcb()->NpxThread; CurrentThread = KeGetCurrentThread(); if (NpxThread == NULL) { - KeLowerIrql(oldIrql); - DPRINT1("!!! Math/Xmm fault ignored! (NpxThread == NULL)\n"); + KeLowerIrql(OldIrql); + DPRINT("Math/Xmm fault ignored! (NpxThread == NULL)\n"); return STATUS_SUCCESS; } + if (ExceptionNr == 16) + { + asm volatile("fnstenv %0" : : "m"(*FpuEnv)); + asm volatile("fldenv %0" : : "m"(*FpuEnv)); /* Stupid x87... */ + FpuEnv->StatusWord &= 0xffff; + } + KeLowerIrql(OldIrql); PreviousMode = ((Tf->Cs & 0xffff) == USER_CS) ? (UserMode) : (KernelMode); DPRINT("Math/Xmm fault happened! (PreviousMode = %s)\n", @@ -490,63 +487,34 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) ASSERT(NpxThread == CurrentThread); /* FIXME: Is not always true I think */ - /* For fxsave we have to align Context->ExtendedRegisters on 16 bytes */ - Context = (PCONTEXT)DummyContext; - Context = (PCONTEXT)((ULONG_PTR)Context + 0x10 - ((ULONG_PTR)Context->ExtendedRegisters & 0x0f)); - /* Get FPU/XMM state */ - Context->FloatSave.Cr0NpxState = 0; - if (FxsrSupport) - { - PFXSAVE_FORMAT FxSave = (PFXSAVE_FORMAT)Context->ExtendedRegisters; - FxSave->MXCsrMask = MxcsrFeatureMask; - memset(FxSave->RegisterArea, 0, sizeof(FxSave->RegisterArea) + - sizeof(FxSave->Reserved3) + sizeof(FxSave->Reserved4)); - asm volatile("fxsave %0" : : "m"(*FxSave)); - KeLowerIrql(oldIrql); - KiFxsaveToFnsaveFormat((PFNSAVE_FORMAT)&Context->FloatSave, FxSave); - } - else - { - PFNSAVE_FORMAT FnSave = (PFNSAVE_FORMAT)&Context->FloatSave; - asm volatile("fnsave %0" : : "m"(*FnSave)); - KeLowerIrql(oldIrql); - KiFnsaveToFxsaveFormat((PFXSAVE_FORMAT)Context->ExtendedRegisters, FnSave); - } - - /* Fill the rest of the context */ - Context->ContextFlags = CONTEXT_FULL; - KeTrapFrameToContext(Tf, NULL, Context); - Context->ContextFlags |= CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; + KeLowerIrql(OldIrql); /* Determine exception code */ if (ExceptionNr == 16) { - USHORT FpuStatusWord = Context->FloatSave.StatusWord & 0xffff; DPRINT("FpuStatusWord = 0x%04x\n", FpuStatusWord); - if (FpuStatusWord & X87_SW_IE) + if (FpuEnv->StatusWord & X87_SW_IE) Er.ExceptionCode = STATUS_FLOAT_INVALID_OPERATION; - else if (FpuStatusWord & X87_SW_DE) + else if (FpuEnv->StatusWord & X87_SW_DE) Er.ExceptionCode = STATUS_FLOAT_DENORMAL_OPERAND; - else if (FpuStatusWord & X87_SW_ZE) + else if (FpuEnv->StatusWord & X87_SW_ZE) Er.ExceptionCode = STATUS_FLOAT_DIVIDE_BY_ZERO; - else if (FpuStatusWord & X87_SW_OE) - Er.ExceptionCode = STATUS_FLOAT_OVERFLOW; - else if (FpuStatusWord & X87_SW_UE) + else if (FpuEnv->StatusWord & X87_SW_OE) + Er.ExceptionCode = STATUS_FLOAT_OVERFLOW; + else if (FpuEnv->StatusWord & X87_SW_UE) Er.ExceptionCode = STATUS_FLOAT_UNDERFLOW; - else if (FpuStatusWord & X87_SW_PE) + else if (FpuEnv->StatusWord & X87_SW_PE) Er.ExceptionCode = STATUS_FLOAT_INEXACT_RESULT; - else if (FpuStatusWord & X87_SW_SE) + else if (FpuEnv->StatusWord & X87_SW_SE) Er.ExceptionCode = STATUS_FLOAT_STACK_CHECK; else ASSERT(0); /* not reached */ - /* FIXME: is this the right way to get the correct EIP of the faulting instruction? */ - Er.ExceptionAddress = (PVOID)Context->FloatSave.ErrorOffset; + Er.ExceptionAddress = (PVOID)FpuEnv->ErrorOffset; } else /* ExceptionNr == 19 */ { - /* FIXME: When should we use STATUS_FLOAT_MULTIPLE_FAULTS? */ Er.ExceptionCode = STATUS_FLOAT_MULTIPLE_TRAPS; Er.ExceptionAddress = (PVOID)Tf->Eip; } @@ -565,3 +533,67 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) return STATUS_UNSUCCESSFUL; } + + +/* This is a rather naive implementation of Ke(Save/Restore)FloatingPointState + which will not work for WDM drivers. Please feel free to improve */ + +NTSTATUS STDCALL +KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save) +{ + PFNSAVE_FORMAT FpState; + + ASSERT_IRQL(DISPATCH_LEVEL); + + /* check if we are doing software emulation */ + if (!HardwareMathSupport) + { + return STATUS_ILLEGAL_FLOAT_CONTEXT; + } + + FpState = ExAllocatePool(NonPagedPool, sizeof (FNSAVE_FORMAT)); + if (NULL == FpState) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + *((PVOID *) Save) = FpState; + +#if defined(__GNUC__) + asm volatile("fnsave %0\n\t" : "=m" (*FpState)); +#elif defined(_MSC_VER) + __asm mov eax, FpState; + __asm fsave [eax]; +#else +#error Unknown compiler for inline assembler +#endif + + KeGetCurrentThread()->NpxIrql = KeGetCurrentIrql(); + + return STATUS_SUCCESS; +} + + +NTSTATUS STDCALL +KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save) +{ + PFNSAVE_FORMAT FpState = *((PVOID *) Save); + + if (KeGetCurrentThread()->NpxIrql != KeGetCurrentIrql()) + { + KEBUGCHECK(UNDEFINED_BUG_CODE); + } + +#if defined(__GNUC__) + asm volatile("fnclex\n\t"); + asm volatile("frstor %0\n\t" : "=m" (*FpState)); +#elif defined(_MSC_VER) + __asm mov eax, FpState; + __asm frstor [eax]; +#else +#error Unknown compiler for inline assembler +#endif + + ExFreePool(FpState); + + return STATUS_SUCCESS; +}