From 01d2a9503398d651414fac7d6acb9fe2a028715d Mon Sep 17 00:00:00 2001 From: Sir Richard Date: Wed, 20 Jan 2010 04:05:08 +0000 Subject: [PATCH] [NTOS]: Implement KiSwapProcess in C. [NTOS]: Implement KiIsNpxPresent and KiIsNpxErrataPresent in C. It's much clearer what these are doing now. [NTOS]: Implement KiFlushNPXState and fix some bugs that were present in the ASM version, such as a wrong NPX state check. [NTOS]: Implement working intrinsics for fxrstor, fxsave, fnsave and enable them for flushing. We'll update the FPU trap code to use these later. svn path=/trunk/; revision=45156 --- .../ntoskrnl/include/internal/i386/intrin_i.h | 22 ++ reactos/ntoskrnl/ke/i386/context.c | 51 ++++ reactos/ntoskrnl/ke/i386/cpu.c | 149 +++++++++ reactos/ntoskrnl/ke/i386/ctxswitch.S | 287 ------------------ reactos/ntoskrnl/ntoskrnl-generic.rbuild | 1 + 5 files changed, 223 insertions(+), 287 deletions(-) create mode 100644 reactos/ntoskrnl/ke/i386/context.c diff --git a/reactos/ntoskrnl/include/internal/i386/intrin_i.h b/reactos/ntoskrnl/include/internal/i386/intrin_i.h index 7c1716c715a..7414dfb7191 100644 --- a/reactos/ntoskrnl/include/internal/i386/intrin_i.h +++ b/reactos/ntoskrnl/include/internal/i386/intrin_i.h @@ -14,6 +14,28 @@ : /* no input */ \ : "memory"); +FORCEINLINE +VOID +Ke386FxStore(IN PFX_SAVE_AREA SaveArea) +{ + asm volatile ("fxrstor (%0)" : : "r"(SaveArea)); +} + +FORCEINLINE +VOID +Ke386FxSave(IN PFX_SAVE_AREA SaveArea) +{ + asm volatile ("fxsave (%0)" : : "r"(SaveArea)); +} + + +FORCEINLINE +VOID +Ke386FnSave(IN PFLOATING_SAVE_AREA SaveArea) +{ + asm volatile ("fnsave (%0); wait" : : "r"(SaveArea)); +} + FORCEINLINE VOID Ke386SaveFpuState(IN PFX_SAVE_AREA SaveArea) diff --git a/reactos/ntoskrnl/ke/i386/context.c b/reactos/ntoskrnl/ke/i386/context.c new file mode 100644 index 00000000000..5b3345a257a --- /dev/null +++ b/reactos/ntoskrnl/ke/i386/context.c @@ -0,0 +1,51 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: BSD - See COPYING.ARM in the top level directory + * FILE: ntoskrnl/ke/i386/context.c + * PURPOSE: Context Switching Related Code + * PROGRAMMERS: ReactOS Portable Systems Group + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +/* FUNCTIONS ******************************************************************/ + +VOID +NTAPI +KiSwapProcess(IN PKPROCESS NewProcess, + IN PKPROCESS OldProcess) +{ + PKIPCR Pcr = (PKIPCR)KeGetPcr(); +#ifdef CONFIG_SMP + ULONG SetMember; + + /* Update active processor mask */ + SetMember = Pcr->SetMember; + InterlockedXor(NewProcess->ActiveProcessors, SetMember); + InterlockedXor(OldProcess->ActiveProcessors, SetMember); +#endif + + /* Check for new LDT */ + if (NewProcess->LdtDescriptor.LimitLow != OldProcess->LdtDescriptor.LimitLow) + { + /* Not handled yet */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Update CR3 */ + __writecr3(NewProcess->DirectoryTableBase[0]); + + /* Clear GS */ + Ke386SetGs(0); + + /* Update IOPM offset */ + Pcr->TSS->IoMapBase = NewProcess->IopmOffset; +} + diff --git a/reactos/ntoskrnl/ke/i386/cpu.c b/reactos/ntoskrnl/ke/i386/cpu.c index 60f23a02b49..4aadb0f3317 100644 --- a/reactos/ntoskrnl/ke/i386/cpu.c +++ b/reactos/ntoskrnl/ke/i386/cpu.c @@ -944,6 +944,155 @@ KiSaveProcessorState(IN PKTRAP_FRAME TrapFrame, KiSaveProcessorControlState(&Prcb->ProcessorState); } +BOOLEAN +NTAPI +KiIsNpxPresent(VOID) +{ + ULONG Cr0; + USHORT Magic; + + /* Set magic */ + Magic = 0xFFFF; + + /* Read CR0 and mask out FPU flags */ + Cr0 = __readcr0() & ~(CR0_MP | CR0_TS | CR0_EM | CR0_ET); + + /* Store on FPU stack */ + asm volatile ("fninit;" "fnstsw %0" : "+m"(Magic)); + + /* Magic should now be cleared */ + if (Magic & 0xFF) + { + /* You don't have an FPU -- enable emulation for now */ + __writecr0(Cr0 | CR0_EM | CR0_TS); + return FALSE; + } + + /* You have an FPU, enable it */ + Cr0 |= CR0_ET; + + /* Enable INT 16 on 486 and higher */ + if (KeGetCurrentPrcb()->CpuType >= 3) Cr0 |= CR0_NE; + + /* Set FPU state */ + __writecr0(Cr0 | CR0_EM | CR0_TS); + return TRUE; +} + +BOOLEAN +NTAPI +KiIsNpxErrataPresent(VOID) +{ + BOOLEAN ErrataPresent; + ULONG Cr0; + volatile double Value1, Value2; + + /* Disable interrupts */ + _disable(); + + /* Read CR0 and remove FPU flags */ + Cr0 = __readcr0(); + __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM)); + + /* Initialize FPU state */ + asm volatile ("fninit"); + + /* Multiply the magic values and divide, we should get the result back */ + Value1 = 4195835.0; + Value2 = 3145727.0; + ErrataPresent = (Value1 * Value2 / 3145727.0) != 4195835.0; + + /* Restore CR0 */ + __writecr0(Cr0); + + /* Enable interrupts */ + _enable(); + + /* Return if there's an errata */ + return ErrataPresent; +} + +NTAPI +VOID +KiFlushNPXState(IN PFLOATING_SAVE_AREA SaveArea) +{ + ULONG EFlags, Cr0; + PKTHREAD Thread, NpxThread; + PFX_SAVE_AREA FxSaveArea; + + /* Save volatiles and disable interrupts */ + EFlags = __readeflags(); + _disable(); + + /* Save the PCR and get the current thread */ + Thread = KeGetCurrentThread(); + + /* Check if we're already loaded */ + if (Thread->NpxState != NPX_STATE_LOADED) + { + /* If there's nothing to load, quit */ + if (!SaveArea) return; + + /* Need FXSR support for this */ + ASSERT(KeI386FxsrPresent == TRUE); + + /* Check for sane CR0 */ + Cr0 = __readcr0(); + if (Cr0 & (CR0_MP | CR0_TS | CR0_EM)) + { + /* Mask out FPU flags */ + __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM)); + } + + /* Get the NPX thread and check its FPU state */ + NpxThread = KeGetCurrentPrcb()->NpxThread; + if ((NpxThread) && (NpxThread->NpxState == NPX_STATE_LOADED)) + { + /* Get the FX frame and store the state there */ + FxSaveArea = KiGetThreadNpxArea(NpxThread); + Ke386FxSave(FxSaveArea); + + /* NPX thread has lost its state */ + NpxThread->NpxState = NPX_STATE_NOT_LOADED; + } + + /* Now load NPX state from the NPX area */ + FxSaveArea = KiGetThreadNpxArea(Thread); + Ke386FxStore(FxSaveArea); + } + else + { + /* Check for sane CR0 */ + Cr0 = __readcr0(); + if (Cr0 & (CR0_MP | CR0_TS | CR0_EM)) + { + /* Mask out FPU flags */ + __writecr0(Cr0 & ~(CR0_MP | CR0_TS | CR0_EM)); + } + + /* Get FX frame */ + FxSaveArea = KiGetThreadNpxArea(Thread); + Thread->NpxState = NPX_STATE_NOT_LOADED; + + /* Save state if supported by CPU */ + if (KeI386FxsrPresent) Ke386FxSave(FxSaveArea); + } + + /* Now save the FN state wherever it was requested */ + if (SaveArea) Ke386FnSave(SaveArea); + + /* Clear NPX thread */ + KeGetCurrentPrcb()->NpxThread = NULL; + + /* Add the CR0 from the NPX frame */ + Cr0 |= NPX_STATE_NOT_LOADED; + Cr0 |= FxSaveArea->Cr0NpxState; + __writecr0(Cr0); + + /* Restore interrupt state */ + __writeeflags(EFlags); +} + /* PUBLIC FUNCTIONS **********************************************************/ /* diff --git a/reactos/ntoskrnl/ke/i386/ctxswitch.S b/reactos/ntoskrnl/ke/i386/ctxswitch.S index e4edf477270..3cd98b916fe 100644 --- a/reactos/ntoskrnl/ke/i386/ctxswitch.S +++ b/reactos/ntoskrnl/ke/i386/ctxswitch.S @@ -17,237 +17,8 @@ #define Running 2 #define WrDispatchInt 0x1F -Dividend: .float 4195835.0 -Divisor: .float 3145727.0 -Result1: .float 0 -Result2: .float 0 - /* FUNCTIONS ****************************************************************/ -.globl _KiIsNpxErrataPresent@0 -.func KiIsNpxErrataPresent@0 -_KiIsNpxErrataPresent@0: - - /* Disable interrupts */ - cli - - /* Get CR0 and mask out FPU flags */ - mov eax, cr0 - mov ecx, eax - and eax, ~(CR0_MP + CR0_TS + CR0_EM) - mov cr0, eax - - /* Initialize the FPU */ - fninit - - /* Do the divison and inverse multiplication */ - fld qword ptr Dividend - fstp qword ptr Result1 - fld qword ptr Divisor - fstp qword ptr Result2 - fld qword ptr Result1 - fdiv qword ptr Result2 - fmul qword ptr Result2 - - /* Do the compare and check flags */ - fcomp qword ptr Result1 - fstsw ax - sahf - - /* Restore CR0 and interrupts */ - mov cr0, ecx - sti - - /* Return errata status */ - xor eax, eax - jz NoErrata - inc eax - -NoErrata: - ret -.endfunc - -.globl _KiIsNpxPresent@0 -.func KiIsNpxPresent@0 -_KiIsNpxPresent@0: - - /* Save stack */ - push ebp - - /* Get CR0 and mask out FPU flags */ - mov eax, cr0 - and eax, ~(CR0_MP + CR0_TS + CR0_EM + CR0_ET) - - /* Initialize the FPU and assume FALSE for return */ - xor edx, edx - fninit - - /* Save magic value on stack */ - mov ecx, 0x42424242 - push ecx - - /* Setup stack for FPU store */ - mov ebp ,esp - fnstsw [ebp] - - /* Now check if our magic got cleared */ - cmp byte ptr [ebp], 0 - jnz NoFpu - - /* Enable FPU, set return to TRUE */ - or eax, CR0_ET - mov edx, 1 - - /* If this is a 486 or higher, enable INT 16 as well */ - cmp dword ptr fs:KPCR_PRCB_CPU_TYPE, 3 - jbe NoFpu - or eax, CR0_NE - -NoFpu: - /* Set emulation enabled during the first boot phase and set the CR0 */ - or eax, (CR0_EM + CR0_TS) - mov cr0, eax - - /* Restore stack */ - pop eax - pop ebp - - /* Return true or false */ - mov eax, edx - ret -.endfunc - -.globl _KiFlushNPXState@4 -.func KiFlushNPXState@4 -_KiFlushNPXState@4: - - /* Save volatiles and disable interrupts */ - push esi - push edi - push ebx - pushfd - cli - - /* Save the PCR and get the current thread */ - mov edi, fs:[KPCR_SELF] - mov esi, [edi+KPCR_CURRENT_THREAD] - - /* Check if we're already loaded */ - cmp byte ptr [esi+KTHREAD_NPX_STATE], NPX_STATE_LOADED - je IsValid - - /* Check if we're supposed to get it */ - cmp dword ptr [esp+20], 0 - je Return - -#if DBG - /* Assert Fxsr support */ - test byte ptr _KeI386FxsrPresent, 1 - jnz AssertOk - int 3 -AssertOk: -#endif - - /* Get CR0 and test if it's valid */ - mov ebx, cr0 - test bl, CR0_MP + CR0_TS + CR0_EM - jz Cr0OK - - /* Enable fnsave to work */ - and ebx, ~(CR0_MP + CR0_TS + CR0_EM) - mov cr0, ebx - -Cr0OK: - /* Check if we are the NPX Thread */ - mov eax, [edi+KPCR_NPX_THREAD] - or eax, eax - jz DontSave - - /* Check if it's not loaded */ - cmp byte ptr [eax+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED - jnz DontSave - -#if DBG - /* We are the NPX Thread with an unloaded NPX State... this isn't normal! */ - int 3 -#endif - - /* Save the NPX State */ - mov ecx, [eax+KTHREAD_INITIAL_STACK] - sub ecx, NPX_FRAME_LENGTH - fxsave [ecx] - mov byte ptr [eax+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED - -DontSave: - /* Load the NPX State */ - mov ecx, [esi+KTHREAD_INITIAL_STACK] - sub ecx, NPX_FRAME_LENGTH - fxrstor [ecx] - - /* Get the CR0 state and destination */ - mov edx, [ecx+FN_CR0_NPX_STATE] - mov ecx, [esp+20] - jmp DoneLoad - -IsValid: - /* We already have a valid state, flush it */ - mov ebx, cr0 - test bl, CR0_MP + CR0_TS + CR0_EM - jz Cr0OK2 - - /* Enable fnsave to work */ - and ebx, ~(CR0_MP + CR0_TS + CR0_EM) - mov cr0, ebx - -Cr0OK2: - /* Get the kernel stack */ - mov ecx, [esi+KTHREAD_INITIAL_STACK] - test byte ptr _KeI386FxsrPresent, 1 - lea ecx, [ecx-NPX_FRAME_LENGTH] - - /* Set the NPX State */ - mov byte ptr [esi+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED - - /* Get Cr0 */ - mov edx, [ecx+FN_CR0_NPX_STATE] - jz DoneLoad - - /* Save the FX State */ - fxsave [ecx] - - /* Check if we also have to save it in the parameter */ - mov ecx, [esp+20] - jecxz NoSave - -DoneLoad: - /* Save the Fn state in the parameter we got */ - fnsave [ecx] - fwait - -NoSave: - /* Clear eax */ - xor eax, eax - - /* Add NPX State */ - or ebx, NPX_STATE_NOT_LOADED - - /* Clear the NPX thread */ - mov [edi+KPCR_NPX_THREAD], eax - - /* Add saved CR0 into NPX State, and set it */ - or ebx, edx - mov cr0, ebx - - /* Re-enable interrupts and return */ -Return: - popf - pop ebx - pop edi - pop esi - ret 4 - -.endfunc - /*++ * KiSwapContextInternal * @@ -736,64 +507,6 @@ NoNextThread: #endif .endfunc -.globl _KiSwapProcess@8 -.func KiSwapProcess@8 -_KiSwapProcess@8: - - /* Get process pointers */ - mov edx, [esp+4] - mov eax, [esp+8] - -#ifdef CONFIG_SMP - /* Update active processors */ - mov ecx, fs:[KPCR_SET_MEMBER] - lock xor [edx+KPROCESS_ACTIVE_PROCESSORS], ecx - lock xor [eax+KPROCESS_ACTIVE_PROCESSORS], ecx - - /* Sanity check */ -#if DBG - test [edx+KPROCESS_ACTIVE_PROCESSORS], ecx - jz WrongCpu1 - test [eax+KPROCESS_ACTIVE_PROCESSORS], ecx - jnz WrongCpu2 -#endif -#endif - - /* Check if their LDTs changed */ - mov ecx, [edx+KPROCESS_LDT_DESCRIPTOR0] - or ecx, [eax+KPROCESS_LDT_DESCRIPTOR0] - jnz NewLdt - - /* Update CR3 */ - mov eax, [edx+KPROCESS_DIRECTORY_TABLE_BASE] - mov cr3, eax - - /* Get the KTSS */ - mov ecx, fs:[KPCR_TSS] - - /* Clear GS on process swap */ - xor eax, eax - mov gs, ax - - /* Update IOPM offset */ - mov ax, [edx+KPROCESS_IOPM_OFFSET] - mov [ecx+KTSS_IOMAPBASE], ax - - /* Return */ - ret 8 - -NewLdt: - /* FIXME: TODO */ - int 3 - -#if DBG -WrongCpu1: - int 3 -WrongCpu2: - int 3 -#endif -.endfunc - .globl _Ki386SetupAndExitToV86Mode@4 .func Ki386SetupAndExitToV86Mode@4 _Ki386SetupAndExitToV86Mode@4: diff --git a/reactos/ntoskrnl/ntoskrnl-generic.rbuild b/reactos/ntoskrnl/ntoskrnl-generic.rbuild index 3ec87dbec17..1c6a6da7d53 100644 --- a/reactos/ntoskrnl/ntoskrnl-generic.rbuild +++ b/reactos/ntoskrnl/ntoskrnl-generic.rbuild @@ -40,6 +40,7 @@ abios.c cpu.c + context.c ctxswitch.S exp.c irqobj.c