From e5b4a8004a27309a0e1537423ff55f66c12adda2 Mon Sep 17 00:00:00 2001 From: Justin Miller Date: Tue, 17 Sep 2024 23:32:03 -0700 Subject: [PATCH] Revert "[NTOS:KE] Fully implement FPU Save/Restore mechanism" This reverts commit 064a35dc6717962a7b1973661566d53f6da06155. --- ntoskrnl/ke/i386/cpu.c | 245 ++++++----------------------------------- 1 file changed, 35 insertions(+), 210 deletions(-) diff --git a/ntoskrnl/ke/i386/cpu.c b/ntoskrnl/ke/i386/cpu.c index a39d27a21396c..63d733237c96b 100644 --- a/ntoskrnl/ke/i386/cpu.c +++ b/ntoskrnl/ke/i386/cpu.c @@ -12,8 +12,6 @@ #define NDEBUG #include -#include - /* GLOBALS *******************************************************************/ /* The TSS to use for Double Fault Traps (INT 0x9) */ @@ -77,9 +75,6 @@ typedef union _CPU_SIGNATURE ULONG AsULONG; } CPU_SIGNATURE; -/* FX area alignment size */ -#define FXSAVE_ALIGN 15 - /* SUPPORT ROUTINES FOR MSVC COMPATIBILITY ***********************************/ /* NSC/Cyrix CPU configuration register index */ @@ -102,6 +97,7 @@ setCx86(UCHAR reg, UCHAR data) WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)0x23, data); } + /* FUNCTIONS *****************************************************************/ CODE_SEG("INIT") @@ -1330,230 +1326,59 @@ KiCoprocessorError(VOID) __writecr0(__readcr0() | CR0_TS); } -/** - * @brief - * Saves the current floating point unit state - * context of the current calling thread. - * - * @param[out] Save - * The saved floating point context given to the - * caller at the end of function's operations. - * The structure whose data contents are opaque - * to the calling thread. - * - * @return - * Returns STATUS_SUCCESS if the function has - * successfully completed its operations. - * STATUS_INSUFFICIENT_RESOURCES is returned - * if the function couldn't allocate memory - * for FPU state information. - * - * @remarks - * The function performs a FPU state save - * in two ways. A normal FPU save (FNSAVE) - * is performed if the system doesn't have - * SSE/SSE2, otherwise the function performs - * a save of FPU, MMX and SSE states save (FXSAVE). +/* + * @implemented */ -#if defined(__clang__) -__attribute__((__target__("sse"))) -#endif NTSTATUS NTAPI -KeSaveFloatingPointState( - _Out_ PKFLOATING_SAVE Save) +KeSaveFloatingPointState(OUT PKFLOATING_SAVE Save) { - PFLOATING_SAVE_CONTEXT FsContext; - PFX_SAVE_AREA FxSaveAreaFrame; - PKPRCB CurrentPrcb; - - /* Sanity checks */ - ASSERT(Save); + PFNSAVE_FORMAT FpState; ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - ASSERT(KeI386NpxPresent); - - /* Initialize the floating point context */ - FsContext = ExAllocatePoolWithTag(NonPagedPool, - sizeof(FLOATING_SAVE_CONTEXT), - TAG_FLOATING_POINT_CONTEXT); - if (!FsContext) - { - /* Bail out if we failed */ - return STATUS_INSUFFICIENT_RESOURCES; - } + UNIMPLEMENTED_ONCE; - /* - * Allocate some memory pool for the buffer. The size - * of this allocated buffer is the FX area plus the - * alignment requirement needed for FXSAVE as a 16-byte - * aligned pointer is compulsory in order to save the - * FPU state. - */ - FsContext->Buffer = ExAllocatePoolWithTag(NonPagedPool, - sizeof(FX_SAVE_AREA) + FXSAVE_ALIGN, - TAG_FLOATING_POINT_FX); - if (!FsContext->Buffer) - { - /* Bail out if we failed */ - ExFreePoolWithTag(FsContext, TAG_FLOATING_POINT_CONTEXT); - return STATUS_INSUFFICIENT_RESOURCES; - } + FpState = ExAllocatePool(NonPagedPool, sizeof (FNSAVE_FORMAT)); + if (!FpState) return STATUS_INSUFFICIENT_RESOURCES; - /* - * Now cache the allocated buffer into the save area - * and align the said area to a 16-byte boundary. Why - * do we have to do this is because of ExAllocate function. - * We gave the necessary alignment requirement in the pool - * allocation size although the function will always return - * a 8-byte aligned pointer. Aligning the given pointer directly - * can cause issues when freeing it from memory afterwards. With - * that said, we have to cache the buffer to the area so that we - * do not touch or mess the allocated buffer any further. - */ - FsContext->PfxSaveArea = ALIGN_UP_POINTER_BY(FsContext->Buffer, 16); - - /* Disable interrupts and get the current processor control region */ - _disable(); - CurrentPrcb = KeGetCurrentPrcb(); - - /* Store the current thread to context */ - FsContext->CurrentThread = KeGetCurrentThread(); - - /* - * Save the previous NPX thread state registers (aka Numeric - * Processor eXtension) into the current context so that - * we are informing the scheduler the current FPU state - * belongs to this thread. - */ - if (FsContext->CurrentThread != CurrentPrcb->NpxThread) + *((PVOID *) Save) = FpState; +#ifdef __GNUC__ + asm volatile("fnsave %0\n\t" : "=m" (*FpState)); +#else + __asm { - if ((CurrentPrcb->NpxThread != NULL) && - (CurrentPrcb->NpxThread->NpxState == NPX_STATE_LOADED)) - { - /* Get the FX frame */ - FxSaveAreaFrame = KiGetThreadNpxArea(CurrentPrcb->NpxThread); - - /* Save the FPU state */ - Ke386SaveFpuState(FxSaveAreaFrame); - - /* NPX thread has lost its state */ - CurrentPrcb->NpxThread->NpxState = NPX_STATE_NOT_LOADED; - FxSaveAreaFrame->NpxSavedCpu = 0; - } - - /* The new NPX thread is the current thread */ - CurrentPrcb->NpxThread = FsContext->CurrentThread; - } - - /* Perform the save */ - Ke386SaveFpuState(FsContext->PfxSaveArea); - - /* Store the NPX IRQL */ - FsContext->OldNpxIrql = FsContext->CurrentThread->Header.NpxIrql; - - /* Set the current IRQL to NPX */ - FsContext->CurrentThread->Header.NpxIrql = KeGetCurrentIrql(); - - /* Initialize the FPU */ - Ke386FnInit(); - - /* Enable interrupts back */ - _enable(); + mov eax, [FpState] + fnsave [eax] + }; +#endif - /* Give the saved FPU context to the caller */ - *((PVOID *) Save) = FsContext; + KeGetCurrentThread()->Header.NpxIrql = KeGetCurrentIrql(); return STATUS_SUCCESS; } -/** - * @brief - * Restores the original FPU state context that has - * been saved by a API call of KeSaveFloatingPointState. - * Callers are expected to restore the floating point - * state by calling this function when they've finished - * doing FPU operations. - * - * @param[in] Save - * The saved floating point context that is to be given - * to the function to restore the FPU state. - * - * @return - * Returns STATUS_SUCCESS indicating the function - * has fully completed its operations. +/* + * @implemented */ -#if defined(__clang__) -__attribute__((__target__("sse"))) -#endif NTSTATUS NTAPI -KeRestoreFloatingPointState( - _In_ PKFLOATING_SAVE Save) +KeRestoreFloatingPointState(IN PKFLOATING_SAVE Save) { - PFLOATING_SAVE_CONTEXT FsContext; - - /* Sanity checks */ - ASSERT(Save); - ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - ASSERT(KeI386NpxPresent); - - /* Cache the saved FS context */ - FsContext = *((PVOID *) Save); - - /* - * We have to restore the regular saved FPU - * state. For this we must first do some - * validation checks so that we are sure - * ourselves the state context is saved - * properly. Check if we are in the same - * calling thread. - */ - if (FsContext->CurrentThread != KeGetCurrentThread()) - { - /* - * This isn't the thread that saved the - * FPU state context, crash the system! - */ - KeBugCheckEx(INVALID_FLOATING_POINT_STATE, - 0x2, - (ULONG_PTR)FsContext->CurrentThread, - (ULONG_PTR)KeGetCurrentThread(), - 0); - } + PFNSAVE_FORMAT FpState = *((PVOID *) Save); + ASSERT(KeGetCurrentThread()->Header.NpxIrql == KeGetCurrentIrql()); + UNIMPLEMENTED_ONCE; - /* Are we under the same NPX interrupt level? */ - if (FsContext->CurrentThread->Header.NpxIrql != KeGetCurrentIrql()) +#ifdef __GNUC__ + asm volatile("fnclex\n\t"); + asm volatile("frstor %0\n\t" : "=m" (*FpState)); +#else + __asm { - /* The interrupt level has changed, crash the system! */ - KeBugCheckEx(INVALID_FLOATING_POINT_STATE, - 0x1, - (ULONG_PTR)FsContext->CurrentThread->Header.NpxIrql, - (ULONG_PTR)KeGetCurrentIrql(), - 0); - } - - /* Disable interrupts */ - _disable(); - - /* - * The saved FPU state context is valid, - * it's time to restore the state. First, - * clear FPU exceptions now. - */ - Ke386ClearFpExceptions(); - - /* Restore the state */ - Ke386RestoreFpuState(FsContext->PfxSaveArea); - - /* Give the saved NPX IRQL back to the NPX thread */ - FsContext->CurrentThread->Header.NpxIrql = FsContext->OldNpxIrql; - - /* Enable interrupts back */ - _enable(); - - /* We're done, free the allocated area and context */ - ExFreePoolWithTag(FsContext->Buffer, TAG_FLOATING_POINT_FX); - ExFreePoolWithTag(FsContext, TAG_FLOATING_POINT_CONTEXT); + fnclex + mov eax, [FpState] + frstor [eax] + }; +#endif + ExFreePool(FpState); return STATUS_SUCCESS; }