/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: pat.c Abstract: This module implements interfaces that set the Page Attribute Table. These entry points only exist on i386 machines. Author: Shivnandan Kaushik (Intel Corp.) Environment: Kernel mode only. Revision History: --*/ #include "ki.h" #include "pat.h" // // Use lockstep mechanism from mtrr code. // #include "mtrr.h" #if DBG #define DBGMSG(a) DbgPrint(a) #else #define DBGMSG(a) #endif // // Structure used for PAT initialization // typedef struct _NEW_PAT { PAT Attributes; // // IPI context to coordinate concurrent PAT update // PROCESSOR_LOCKSTEP Synchronize; } NEW_PAT, *PNEW_PAT; // Prototypes VOID KeRestorePAT ( VOID ); VOID KiInitializePAT ( VOID ); VOID KiLoadPAT ( IN PNEW_PAT Context ); VOID KiLoadPATTarget ( IN PKIPI_CONTEXT SignalDone, IN PVOID Context, IN PVOID Parameter2, IN PVOID Parameter3 ); #if DBG VOID KiDumpPAT ( PUCHAR DebugString, PAT Attributes ); #endif #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGELK,KiInitializePAT) #pragma alloc_text(PAGELK,KiLoadPAT) #pragma alloc_text(PAGELK,KiLoadPATTarget) #endif VOID KeRestorePAT ( VOID ) /*++ Routine Description: Reinitialize the Page Attribute Table (PAT) on all processors. N.B. The caller must have the PAGELK code locked Arguments: None. Return Value: None. --*/ { if (KeFeatureBits & KF_PAT) { KiInitializePAT(); } } VOID KiInitializePAT ( VOID ) /*++ Routine Description: Initialize the Page Attribute Table (PAT) on all processors. PAT is setup to provide WB, WC, STRONG_UC and WEAK_UC as the memory types such that mm macros for enabling/disabling/querying caching (MI_DISABLE_CACHING, MI_ENABLE_CACHING and MI_IS_CACHING_ENABLED) are unaffected. PAT_Entry PAT Index PCD PWT Memory Type 0 0 0 0 WB 1 0 0 1 WC * 2 0 1 0 WEAK_UC 3 0 1 1 STRONG_UC 4 1 0 0 WB 5 1 0 1 WC * 6 1 1 0 WEAK_UC 7 1 1 1 STRONG_UC N.B. The caller must have the PAGELK code locked and ensure that the PAT feature is supported. Arguments: None. Return Value: None. --*/ { PAT PatAttributes; ULONG Size; KIRQL OldIrql, NewIrql; PKPRCB Prcb; NEW_PAT NewPAT; KAFFINITY TargetProcessors; ASSERT ((KeFeatureBits & KF_PAT) != 0); // // Initialize the PAT // PatAttributes.hw.Pat[0] = PAT_TYPE_WB; PatAttributes.hw.Pat[1] = PAT_TYPE_USWC; PatAttributes.hw.Pat[2] = PAT_TYPE_WEAK_UC; PatAttributes.hw.Pat[3] = PAT_TYPE_STRONG_UC; PatAttributes.hw.Pat[4] = PAT_TYPE_WB; PatAttributes.hw.Pat[5] = PAT_TYPE_USWC; PatAttributes.hw.Pat[6] = PAT_TYPE_WEAK_UC; PatAttributes.hw.Pat[7] = PAT_TYPE_STRONG_UC; // // Synchronize with other IPI functions which may stall // KiLockContextSwap(&OldIrql); Prcb = KeGetCurrentPrcb(); NewPAT.Attributes = PatAttributes; NewPAT.Synchronize.TargetCount = 0; NewPAT.Synchronize.TargetPhase = &Prcb->ReverseStall; NewPAT.Synchronize.Processor = Prcb->Number; #if !defined(NT_UP) // // Collect all the (other) processors // TargetProcessors = KeActiveProcessors & ~Prcb->SetMember; if (TargetProcessors != 0) { KiIpiSendSynchronousPacket ( Prcb, TargetProcessors, KiLoadPATTarget, (PVOID) (&NewPAT), NULL, NULL ); // // Wait for all processors to be collected // KiIpiStallOnPacketTargets(TargetProcessors); // // All processors are now waiting. Raise to high level to // ensure this processor doesn't enter the debugger due to // some interrupt service routine. // KeRaiseIrql (HIGH_LEVEL, &NewIrql); // // There's no reason for any debug events now, so signal // the other processors that they can all begin the PAT update // Prcb->ReverseStall += 1; } #endif // // Update PAT // KiLoadPAT(&NewPAT); // // Release ContextSwap lock and lower to initial irql // KiUnlockContextSwap(OldIrql); MmEnablePAT(); return; } VOID KiLoadPATTarget ( IN PKIPI_CONTEXT SignalDone, IN PVOID NewPAT, IN PVOID Parameter2, IN PVOID Parameter3 ) /*++ Routine Description: Synchronize with target processors prior to PAT modification. Arguments: Context - Context which includes the PAT to load Return Value: None --*/ { PNEW_PAT Context; Context = (PNEW_PAT) NewPAT; // // Wait for all processors to be ready // KiIpiSignalPacketDoneAndStall(SignalDone, Context->Synchronize.TargetPhase); // // Update PAT // KiLoadPAT (Context); } VOID KiLoadPAT ( IN PNEW_PAT Context ) /*++ Routine Description: This function loads the PAT to all processors. Arguments: Context - Context which includes new PAT to load Return Value: PAT on all processors programmed to new values --*/ { BOOLEAN Enable; ULONG HldCr0, HldCr4, Index; // // Disable interrupts // Enable = KeDisableInterrupts(); // // Synchronize all processors // KiLockStepExecution (&Context->Synchronize); _asm { ; ; Get current CR0 ; mov eax, cr0 mov HldCr0, eax ; ; Disable caching & line fill ; and eax, not CR0_NW or eax, CR0_CD mov cr0, eax ; ; Flush caches ; ; ; wbinvd ; _emit 0Fh _emit 09h ; ; Get current cr4 ; _emit 0Fh _emit 20h _emit 0E0h ; mov eax, cr4 mov HldCr4, eax ; ; Disable global page ; and eax, not CR4_PGE _emit 0Fh _emit 22h _emit 0E0h ; mov cr4, eax ; ; Flush TLB ; mov eax, cr3 mov cr3, eax } // // Load new PAT // WRMSR (PAT_MSR, Context->Attributes.QuadPart); _asm { ; ; Flush caches. ; ; ; wbinvd ; _emit 0Fh _emit 09h ; ; Flush TLBs ; mov eax, cr3 mov cr3, eax } _asm { ; ; Restore CR4 (global page enable) ; mov eax, HldCr4 _emit 0Fh _emit 22h _emit 0E0h ; mov cr4, eax ; ; Restore CR0 (cache enable) ; mov eax, HldCr0 mov cr0, eax } // // Wait for all processors to reach the same place, // restore interrupts and return. // KiLockStepExecution (&Context->Synchronize); KeEnableInterrupts (Enable); }