text: /*++
  
  Copyright (c) Microsoft Corporation. All rights reserved. 
  
  You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
  If you do not agree to the terms, do not use the code.
  
  
  Module Name:
  
      pat.c
  
  Abstract:
  
      This module implements interfaces that set the Page Attribute
      Table. These entry points only exist on i386 machines.
  
  --*/
  
  #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;
      KIRQL       OldIrql;
      PKPRCB      Prcb;
      NEW_PAT     NewPAT;
  #if !defined(NT_UP)
      KIRQL       NewIrql;
      KAFFINITY   TargetProcessors;
  #endif
  
      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
      //
      KeAcquireSpinLock (&KiReverseStallIpiLock, &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 lock and lower to initial irql
      //
  
      KeReleaseSpinLock (&KiReverseStallIpiLock, 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;
  
      UNREFERENCED_PARAMETER (Parameter2);
      UNREFERENCED_PARAMETER (Parameter3);
  
      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;
  
      //
      // 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);
  }
  
  