MtrrSetMemoryAttributesInMtrrSettings() is a batch-set API. When setting multiple ranges of memory attributes, the single-set API (MtrrSetMemoryAttributeInMtrrSettings and MtrrSetMemoryAttribute) may fail, but batch-set API may succeed. Add comments to recommend caller to use batch-set API when setting multiple ranges. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com> Reviewed-by: Ming Shao <ming.shao@intel.com>
		
			
				
	
	
		
			2931 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2931 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   MTRR setting library
 | |
| 
 | |
|   @par Note:
 | |
|     Most of services in this library instance are suggested to be invoked by BSP only,
 | |
|     except for MtrrSetAllMtrrs() which is used to sync BSP's MTRR setting to APs.
 | |
| 
 | |
|   Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
|   This program and the accompanying materials
 | |
|   are licensed and made available under the terms and conditions of the BSD License
 | |
|   which accompanies this distribution.  The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include <Uefi.h>
 | |
| #include <Register/Cpuid.h>
 | |
| #include <Register/Msr.h>
 | |
| 
 | |
| #include <Library/MtrrLib.h>
 | |
| #include <Library/BaseLib.h>
 | |
| #include <Library/CpuLib.h>
 | |
| #include <Library/BaseMemoryLib.h>
 | |
| #include <Library/DebugLib.h>
 | |
| 
 | |
| #define OR_SEED      0x0101010101010101ull
 | |
| #define CLEAR_SEED   0xFFFFFFFFFFFFFFFFull
 | |
| #define MAX_WEIGHT   MAX_UINT8
 | |
| #define SCRATCH_BUFFER_SIZE           (4 * SIZE_4KB)
 | |
| #define MTRR_LIB_ASSERT_ALIGNED(B, L) ASSERT ((B & ~(L - 1)) == B);
 | |
| 
 | |
| #define M(x,y) ((x) * VertexCount + (y))
 | |
| #define O(x,y) ((y) * VertexCount + (x))
 | |
| 
 | |
| //
 | |
| // Context to save and restore when MTRRs are programmed
 | |
| //
 | |
| typedef struct {
 | |
|   UINTN    Cr4;
 | |
|   BOOLEAN  InterruptState;
 | |
| } MTRR_CONTEXT;
 | |
| 
 | |
| typedef struct {
 | |
|   UINT64                 Address;
 | |
|   UINT64                 Alignment;
 | |
|   UINT64                 Length;
 | |
|   MTRR_MEMORY_CACHE_TYPE Type : 7;
 | |
| 
 | |
|   //
 | |
|   // Temprary use for calculating the best MTRR settings.
 | |
|   //
 | |
|   BOOLEAN                Visited : 1;
 | |
|   UINT8                  Weight;
 | |
|   UINT16                 Previous;
 | |
| } MTRR_LIB_ADDRESS;
 | |
| 
 | |
| //
 | |
| // This table defines the offset, base and length of the fixed MTRRs
 | |
| //
 | |
| CONST FIXED_MTRR  mMtrrLibFixedMtrrTable[] = {
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX64K_00000,
 | |
|     0,
 | |
|     SIZE_64KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX16K_80000,
 | |
|     0x80000,
 | |
|     SIZE_16KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX16K_A0000,
 | |
|     0xA0000,
 | |
|     SIZE_16KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX4K_C0000,
 | |
|     0xC0000,
 | |
|     SIZE_4KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX4K_C8000,
 | |
|     0xC8000,
 | |
|     SIZE_4KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX4K_D0000,
 | |
|     0xD0000,
 | |
|     SIZE_4KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX4K_D8000,
 | |
|     0xD8000,
 | |
|     SIZE_4KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX4K_E0000,
 | |
|     0xE0000,
 | |
|     SIZE_4KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX4K_E8000,
 | |
|     0xE8000,
 | |
|     SIZE_4KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX4K_F0000,
 | |
|     0xF0000,
 | |
|     SIZE_4KB
 | |
|   },
 | |
|   {
 | |
|     MSR_IA32_MTRR_FIX4K_F8000,
 | |
|     0xF8000,
 | |
|     SIZE_4KB
 | |
|   }
 | |
| };
 | |
| 
 | |
| //
 | |
| // Lookup table used to print MTRRs
 | |
| //
 | |
| GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mMtrrMemoryCacheTypeShortName[] = {
 | |
|   "UC",  // CacheUncacheable
 | |
|   "WC",  // CacheWriteCombining
 | |
|   "R*",  // Invalid
 | |
|   "R*",  // Invalid
 | |
|   "WT",  // CacheWriteThrough
 | |
|   "WP",  // CacheWriteProtected
 | |
|   "WB",  // CacheWriteBack
 | |
|   "R*"   // Invalid
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Worker function prints all MTRRs for debugging.
 | |
| 
 | |
|   If MtrrSetting is not NULL, print MTRR settings from input MTRR
 | |
|   settings buffer.
 | |
|   If MtrrSetting is NULL, print MTRR settings from MTRRs.
 | |
| 
 | |
|   @param  MtrrSetting    A buffer holding all MTRRs content.
 | |
| **/
 | |
| VOID
 | |
| MtrrDebugPrintAllMtrrsWorker (
 | |
|   IN MTRR_SETTINGS    *MtrrSetting
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Worker function returns the variable MTRR count for the CPU.
 | |
| 
 | |
|   @return Variable MTRR count
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| GetVariableMtrrCountWorker (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   MSR_IA32_MTRRCAP_REGISTER MtrrCap;
 | |
| 
 | |
|   MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP);
 | |
|   ASSERT (MtrrCap.Bits.VCNT <= ARRAY_SIZE (((MTRR_VARIABLE_SETTINGS *) 0)->Mtrr));
 | |
|   return MtrrCap.Bits.VCNT;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Returns the variable MTRR count for the CPU.
 | |
| 
 | |
|   @return Variable MTRR count
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| EFIAPI
 | |
| GetVariableMtrrCount (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return 0;
 | |
|   }
 | |
|   return GetVariableMtrrCountWorker ();
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Worker function returns the firmware usable variable MTRR count for the CPU.
 | |
| 
 | |
|   @return Firmware usable variable MTRR count
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| GetFirmwareVariableMtrrCountWorker (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   UINT32  VariableMtrrCount;
 | |
|   UINT32  ReservedMtrrNumber;
 | |
| 
 | |
|   VariableMtrrCount = GetVariableMtrrCountWorker ();
 | |
|   ReservedMtrrNumber = PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs);
 | |
|   if (VariableMtrrCount < ReservedMtrrNumber) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   return VariableMtrrCount - ReservedMtrrNumber;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Returns the firmware usable variable MTRR count for the CPU.
 | |
| 
 | |
|   @return Firmware usable variable MTRR count
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| EFIAPI
 | |
| GetFirmwareVariableMtrrCount (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return 0;
 | |
|   }
 | |
|   return GetFirmwareVariableMtrrCountWorker ();
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Worker function returns the default MTRR cache type for the system.
 | |
| 
 | |
|   If MtrrSetting is not NULL, returns the default MTRR cache type from input
 | |
|   MTRR settings buffer.
 | |
|   If MtrrSetting is NULL, returns the default MTRR cache type from MSR.
 | |
| 
 | |
|   @param[in]  MtrrSetting    A buffer holding all MTRRs content.
 | |
| 
 | |
|   @return  The default MTRR cache type.
 | |
| 
 | |
| **/
 | |
| MTRR_MEMORY_CACHE_TYPE
 | |
| MtrrGetDefaultMemoryTypeWorker (
 | |
|   IN MTRR_SETTINGS      *MtrrSetting
 | |
|   )
 | |
| {
 | |
|   MSR_IA32_MTRR_DEF_TYPE_REGISTER DefType;
 | |
| 
 | |
|   if (MtrrSetting == NULL) {
 | |
|     DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
 | |
|   } else {
 | |
|     DefType.Uint64 = MtrrSetting->MtrrDefType;
 | |
|   }
 | |
| 
 | |
|   return (MTRR_MEMORY_CACHE_TYPE) DefType.Bits.Type;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Returns the default MTRR cache type for the system.
 | |
| 
 | |
|   @return  The default MTRR cache type.
 | |
| 
 | |
| **/
 | |
| MTRR_MEMORY_CACHE_TYPE
 | |
| EFIAPI
 | |
| MtrrGetDefaultMemoryType (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return CacheUncacheable;
 | |
|   }
 | |
|   return MtrrGetDefaultMemoryTypeWorker (NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Preparation before programming MTRR.
 | |
| 
 | |
|   This function will do some preparation for programming MTRRs:
 | |
|   disable cache, invalid cache and disable MTRR caching functionality
 | |
| 
 | |
|   @param[out] MtrrContext  Pointer to context to save
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| MtrrLibPreMtrrChange (
 | |
|   OUT MTRR_CONTEXT  *MtrrContext
 | |
|   )
 | |
| {
 | |
|   MSR_IA32_MTRR_DEF_TYPE_REGISTER  DefType;
 | |
|   //
 | |
|   // Disable interrupts and save current interrupt state
 | |
|   //
 | |
|   MtrrContext->InterruptState = SaveAndDisableInterrupts();
 | |
| 
 | |
|   //
 | |
|   // Enter no fill cache mode, CD=1(Bit30), NW=0 (Bit29)
 | |
|   //
 | |
|   AsmDisableCache ();
 | |
| 
 | |
|   //
 | |
|   // Save original CR4 value and clear PGE flag (Bit 7)
 | |
|   //
 | |
|   MtrrContext->Cr4 = AsmReadCr4 ();
 | |
|   AsmWriteCr4 (MtrrContext->Cr4 & (~BIT7));
 | |
| 
 | |
|   //
 | |
|   // Flush all TLBs
 | |
|   //
 | |
|   CpuFlushTlb ();
 | |
| 
 | |
|   //
 | |
|   // Disable MTRRs
 | |
|   //
 | |
|   DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
 | |
|   DefType.Bits.E = 0;
 | |
|   AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, DefType.Uint64);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Cleaning up after programming MTRRs.
 | |
| 
 | |
|   This function will do some clean up after programming MTRRs:
 | |
|   Flush all TLBs,  re-enable caching, restore CR4.
 | |
| 
 | |
|   @param[in] MtrrContext  Pointer to context to restore
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| MtrrLibPostMtrrChangeEnableCache (
 | |
|   IN MTRR_CONTEXT  *MtrrContext
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // Flush all TLBs
 | |
|   //
 | |
|   CpuFlushTlb ();
 | |
| 
 | |
|   //
 | |
|   // Enable Normal Mode caching CD=NW=0, CD(Bit30), NW(Bit29)
 | |
|   //
 | |
|   AsmEnableCache ();
 | |
| 
 | |
|   //
 | |
|   // Restore original CR4 value
 | |
|   //
 | |
|   AsmWriteCr4 (MtrrContext->Cr4);
 | |
| 
 | |
|   //
 | |
|   // Restore original interrupt state
 | |
|   //
 | |
|   SetInterruptState (MtrrContext->InterruptState);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Cleaning up after programming MTRRs.
 | |
| 
 | |
|   This function will do some clean up after programming MTRRs:
 | |
|   enable MTRR caching functionality, and enable cache
 | |
| 
 | |
|   @param[in] MtrrContext  Pointer to context to restore
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| MtrrLibPostMtrrChange (
 | |
|   IN MTRR_CONTEXT  *MtrrContext
 | |
|   )
 | |
| {
 | |
|   MSR_IA32_MTRR_DEF_TYPE_REGISTER  DefType;
 | |
|   //
 | |
|   // Enable Cache MTRR
 | |
|   //
 | |
|   DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
 | |
|   DefType.Bits.E = 1;
 | |
|   DefType.Bits.FE = 1;
 | |
|   AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, DefType.Uint64);
 | |
| 
 | |
|   MtrrLibPostMtrrChangeEnableCache (MtrrContext);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Worker function gets the content in fixed MTRRs
 | |
| 
 | |
|   @param[out]  FixedSettings  A buffer to hold fixed MTRRs content.
 | |
| 
 | |
|   @retval The pointer of FixedSettings
 | |
| 
 | |
| **/
 | |
| MTRR_FIXED_SETTINGS*
 | |
| MtrrGetFixedMtrrWorker (
 | |
|   OUT MTRR_FIXED_SETTINGS         *FixedSettings
 | |
|   )
 | |
| {
 | |
|   UINT32  Index;
 | |
| 
 | |
|   for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
 | |
|       FixedSettings->Mtrr[Index] =
 | |
|         AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr);
 | |
|   }
 | |
| 
 | |
|   return FixedSettings;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This function gets the content in fixed MTRRs
 | |
| 
 | |
|   @param[out]  FixedSettings  A buffer to hold fixed MTRRs content.
 | |
| 
 | |
|   @retval The pointer of FixedSettings
 | |
| 
 | |
| **/
 | |
| MTRR_FIXED_SETTINGS*
 | |
| EFIAPI
 | |
| MtrrGetFixedMtrr (
 | |
|   OUT MTRR_FIXED_SETTINGS         *FixedSettings
 | |
|   )
 | |
| {
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return FixedSettings;
 | |
|   }
 | |
| 
 | |
|   return MtrrGetFixedMtrrWorker (FixedSettings);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Worker function will get the raw value in variable MTRRs
 | |
| 
 | |
|   If MtrrSetting is not NULL, gets the variable MTRRs raw value from input
 | |
|   MTRR settings buffer.
 | |
|   If MtrrSetting is NULL, gets the variable MTRRs raw value from MTRRs.
 | |
| 
 | |
|   @param[in]  MtrrSetting        A buffer holding all MTRRs content.
 | |
|   @param[in]  VariableMtrrCount  Number of variable MTRRs.
 | |
|   @param[out] VariableSettings   A buffer to hold variable MTRRs content.
 | |
| 
 | |
|   @return The VariableSettings input pointer
 | |
| 
 | |
| **/
 | |
| MTRR_VARIABLE_SETTINGS*
 | |
| MtrrGetVariableMtrrWorker (
 | |
|   IN  MTRR_SETTINGS           *MtrrSetting,
 | |
|   IN  UINT32                  VariableMtrrCount,
 | |
|   OUT MTRR_VARIABLE_SETTINGS  *VariableSettings
 | |
|   )
 | |
| {
 | |
|   UINT32  Index;
 | |
| 
 | |
|   ASSERT (VariableMtrrCount <= ARRAY_SIZE (VariableSettings->Mtrr));
 | |
| 
 | |
|   for (Index = 0; Index < VariableMtrrCount; Index++) {
 | |
|     if (MtrrSetting == NULL) {
 | |
|       VariableSettings->Mtrr[Index].Mask = AsmReadMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1));
 | |
|       //
 | |
|       // Skip to read the Base MSR when the Mask.V is not set.
 | |
|       //
 | |
|       if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *)&VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) {
 | |
|         VariableSettings->Mtrr[Index].Base = AsmReadMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1));
 | |
|       }
 | |
|     } else {
 | |
|       VariableSettings->Mtrr[Index].Base = MtrrSetting->Variables.Mtrr[Index].Base;
 | |
|       VariableSettings->Mtrr[Index].Mask = MtrrSetting->Variables.Mtrr[Index].Mask;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return  VariableSettings;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function will get the raw value in variable MTRRs
 | |
| 
 | |
|   @param[out]  VariableSettings   A buffer to hold variable MTRRs content.
 | |
| 
 | |
|   @return The VariableSettings input pointer
 | |
| 
 | |
| **/
 | |
| MTRR_VARIABLE_SETTINGS*
 | |
| EFIAPI
 | |
| MtrrGetVariableMtrr (
 | |
|   OUT MTRR_VARIABLE_SETTINGS         *VariableSettings
 | |
|   )
 | |
| {
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return VariableSettings;
 | |
|   }
 | |
| 
 | |
|   return MtrrGetVariableMtrrWorker (
 | |
|            NULL,
 | |
|            GetVariableMtrrCountWorker (),
 | |
|            VariableSettings
 | |
|            );
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Programs fixed MTRRs registers.
 | |
| 
 | |
|   @param[in]      Type             The memory type to set.
 | |
|   @param[in, out] Base             The base address of memory range.
 | |
|   @param[in, out] Length           The length of memory range.
 | |
|   @param[in, out] LastMsrIndex     On input, the last index of the fixed MTRR MSR to program.
 | |
|                                    On return, the current index of the fixed MTRR MSR to program.
 | |
|   @param[out]     ClearMask        The bits to clear in the fixed MTRR MSR.
 | |
|   @param[out]     OrMask           The bits to set in the fixed MTRR MSR.
 | |
| 
 | |
|   @retval RETURN_SUCCESS      The cache type was updated successfully
 | |
|   @retval RETURN_UNSUPPORTED  The requested range or cache type was invalid
 | |
|                               for the fixed MTRRs.
 | |
| 
 | |
| **/
 | |
| RETURN_STATUS
 | |
| MtrrLibProgramFixedMtrr (
 | |
|   IN     MTRR_MEMORY_CACHE_TYPE  Type,
 | |
|   IN OUT UINT64                  *Base,
 | |
|   IN OUT UINT64                  *Length,
 | |
|   IN OUT UINT32                  *LastMsrIndex,
 | |
|   OUT    UINT64                  *ClearMask,
 | |
|   OUT    UINT64                  *OrMask
 | |
|   )
 | |
| {
 | |
|   UINT32  MsrIndex;
 | |
|   UINT32  LeftByteShift;
 | |
|   UINT32  RightByteShift;
 | |
|   UINT64  SubLength;
 | |
| 
 | |
|   //
 | |
|   // Find the fixed MTRR index to be programmed
 | |
|   //
 | |
|   for (MsrIndex = *LastMsrIndex + 1; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) {
 | |
|     if ((*Base >= mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) &&
 | |
|         (*Base <
 | |
|             (
 | |
|               mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress +
 | |
|               (8 * mMtrrLibFixedMtrrTable[MsrIndex].Length)
 | |
|             )
 | |
|           )
 | |
|         ) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ASSERT (MsrIndex != ARRAY_SIZE (mMtrrLibFixedMtrrTable));
 | |
| 
 | |
|   //
 | |
|   // Find the begin offset in fixed MTRR and calculate byte offset of left shift
 | |
|   //
 | |
|   if ((((UINT32)*Base - mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) % mMtrrLibFixedMtrrTable[MsrIndex].Length) != 0) {
 | |
|     //
 | |
|     // Base address should be aligned to the begin of a certain Fixed MTRR range.
 | |
|     //
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
|   LeftByteShift = ((UINT32)*Base - mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) / mMtrrLibFixedMtrrTable[MsrIndex].Length;
 | |
|   ASSERT (LeftByteShift < 8);
 | |
| 
 | |
|   //
 | |
|   // Find the end offset in fixed MTRR and calculate byte offset of right shift
 | |
|   //
 | |
|   SubLength = mMtrrLibFixedMtrrTable[MsrIndex].Length * (8 - LeftByteShift);
 | |
|   if (*Length >= SubLength) {
 | |
|     RightByteShift = 0;
 | |
|   } else {
 | |
|     if (((UINT32)(*Length) % mMtrrLibFixedMtrrTable[MsrIndex].Length) != 0) {
 | |
|       //
 | |
|       // Length should be aligned to the end of a certain Fixed MTRR range.
 | |
|       //
 | |
|       return RETURN_UNSUPPORTED;
 | |
|     }
 | |
|     RightByteShift = 8 - LeftByteShift - (UINT32)(*Length) / mMtrrLibFixedMtrrTable[MsrIndex].Length;
 | |
|     //
 | |
|     // Update SubLength by actual length
 | |
|     //
 | |
|     SubLength = *Length;
 | |
|   }
 | |
| 
 | |
|   *ClearMask = CLEAR_SEED;
 | |
|   *OrMask    = MultU64x32 (OR_SEED, (UINT32) Type);
 | |
| 
 | |
|   if (LeftByteShift != 0) {
 | |
|     //
 | |
|     // Clear the low bits by LeftByteShift
 | |
|     //
 | |
|     *ClearMask &= LShiftU64 (*ClearMask, LeftByteShift * 8);
 | |
|     *OrMask    &= LShiftU64 (*OrMask,    LeftByteShift * 8);
 | |
|   }
 | |
| 
 | |
|   if (RightByteShift != 0) {
 | |
|     //
 | |
|     // Clear the high bits by RightByteShift
 | |
|     //
 | |
|     *ClearMask &= RShiftU64 (*ClearMask, RightByteShift * 8);
 | |
|     *OrMask    &= RShiftU64 (*OrMask,    RightByteShift * 8);
 | |
|   }
 | |
| 
 | |
|   *Length -= SubLength;
 | |
|   *Base   += SubLength;
 | |
| 
 | |
|   *LastMsrIndex    = MsrIndex;
 | |
| 
 | |
|   return RETURN_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Worker function gets the attribute of variable MTRRs.
 | |
| 
 | |
|   This function shadows the content of variable MTRRs into an
 | |
|   internal array: VariableMtrr.
 | |
| 
 | |
|   @param[in]   VariableSettings      The variable MTRR values to shadow
 | |
|   @param[in]   VariableMtrrCount     The number of variable MTRRs
 | |
|   @param[in]   MtrrValidBitsMask     The mask for the valid bit of the MTRR
 | |
|   @param[in]   MtrrValidAddressMask  The valid address mask for MTRR
 | |
|   @param[out]  VariableMtrr          The array to shadow variable MTRRs content
 | |
| 
 | |
|   @return      Number of MTRRs which has been used.
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| MtrrGetMemoryAttributeInVariableMtrrWorker (
 | |
|   IN  MTRR_VARIABLE_SETTINGS  *VariableSettings,
 | |
|   IN  UINTN                   VariableMtrrCount,
 | |
|   IN  UINT64                  MtrrValidBitsMask,
 | |
|   IN  UINT64                  MtrrValidAddressMask,
 | |
|   OUT VARIABLE_MTRR           *VariableMtrr
 | |
|   )
 | |
| {
 | |
|   UINTN   Index;
 | |
|   UINT32  UsedMtrr;
 | |
| 
 | |
|   ZeroMem (VariableMtrr, sizeof (VARIABLE_MTRR) * ARRAY_SIZE (VariableSettings->Mtrr));
 | |
|   for (Index = 0, UsedMtrr = 0; Index < VariableMtrrCount; Index++) {
 | |
|     if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *) &VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) {
 | |
|       VariableMtrr[Index].Msr         = (UINT32)Index;
 | |
|       VariableMtrr[Index].BaseAddress = (VariableSettings->Mtrr[Index].Base & MtrrValidAddressMask);
 | |
|       VariableMtrr[Index].Length      =
 | |
|         ((~(VariableSettings->Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1;
 | |
|       VariableMtrr[Index].Type        = (VariableSettings->Mtrr[Index].Base & 0x0ff);
 | |
|       VariableMtrr[Index].Valid       = TRUE;
 | |
|       VariableMtrr[Index].Used        = TRUE;
 | |
|       UsedMtrr++;
 | |
|     }
 | |
|   }
 | |
|   return UsedMtrr;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Convert variable MTRRs to a RAW MTRR_MEMORY_RANGE array.
 | |
|   One MTRR_MEMORY_RANGE element is created for each MTRR setting.
 | |
|   The routine doesn't remove the overlap or combine the near-by region.
 | |
| 
 | |
|   @param[in]   VariableSettings      The variable MTRR values to shadow
 | |
|   @param[in]   VariableMtrrCount     The number of variable MTRRs
 | |
|   @param[in]   MtrrValidBitsMask     The mask for the valid bit of the MTRR
 | |
|   @param[in]   MtrrValidAddressMask  The valid address mask for MTRR
 | |
|   @param[out]  VariableMtrr          The array to shadow variable MTRRs content
 | |
| 
 | |
|   @return      Number of MTRRs which has been used.
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| MtrrLibGetRawVariableRanges (
 | |
|   IN  MTRR_VARIABLE_SETTINGS  *VariableSettings,
 | |
|   IN  UINTN                   VariableMtrrCount,
 | |
|   IN  UINT64                  MtrrValidBitsMask,
 | |
|   IN  UINT64                  MtrrValidAddressMask,
 | |
|   OUT MTRR_MEMORY_RANGE       *VariableMtrr
 | |
|   )
 | |
| {
 | |
|   UINTN   Index;
 | |
|   UINT32  UsedMtrr;
 | |
| 
 | |
|   ZeroMem (VariableMtrr, sizeof (MTRR_MEMORY_RANGE) * ARRAY_SIZE (VariableSettings->Mtrr));
 | |
|   for (Index = 0, UsedMtrr = 0; Index < VariableMtrrCount; Index++) {
 | |
|     if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *) &VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) {
 | |
|       VariableMtrr[Index].BaseAddress = (VariableSettings->Mtrr[Index].Base & MtrrValidAddressMask);
 | |
|       VariableMtrr[Index].Length      =
 | |
|         ((~(VariableSettings->Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1;
 | |
|       VariableMtrr[Index].Type        = (MTRR_MEMORY_CACHE_TYPE)(VariableSettings->Mtrr[Index].Base & 0x0ff);
 | |
|       UsedMtrr++;
 | |
|     }
 | |
|   }
 | |
|   return UsedMtrr;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Gets the attribute of variable MTRRs.
 | |
| 
 | |
|   This function shadows the content of variable MTRRs into an
 | |
|   internal array: VariableMtrr.
 | |
| 
 | |
|   @param[in]   MtrrValidBitsMask     The mask for the valid bit of the MTRR
 | |
|   @param[in]   MtrrValidAddressMask  The valid address mask for MTRR
 | |
|   @param[out]  VariableMtrr          The array to shadow variable MTRRs content
 | |
| 
 | |
|   @return                       The return value of this parameter indicates the
 | |
|                                 number of MTRRs which has been used.
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| EFIAPI
 | |
| MtrrGetMemoryAttributeInVariableMtrr (
 | |
|   IN  UINT64                    MtrrValidBitsMask,
 | |
|   IN  UINT64                    MtrrValidAddressMask,
 | |
|   OUT VARIABLE_MTRR             *VariableMtrr
 | |
|   )
 | |
| {
 | |
|   MTRR_VARIABLE_SETTINGS  VariableSettings;
 | |
| 
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   MtrrGetVariableMtrrWorker (
 | |
|     NULL,
 | |
|     GetVariableMtrrCountWorker (),
 | |
|     &VariableSettings
 | |
|     );
 | |
| 
 | |
|   return MtrrGetMemoryAttributeInVariableMtrrWorker (
 | |
|            &VariableSettings,
 | |
|            GetFirmwareVariableMtrrCountWorker (),
 | |
|            MtrrValidBitsMask,
 | |
|            MtrrValidAddressMask,
 | |
|            VariableMtrr
 | |
|            );
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Return the biggest alignment (lowest set bit) of address.
 | |
|   The function is equivalent to: 1 << LowBitSet64 (Address).
 | |
| 
 | |
|   @param Address    The address to return the alignment.
 | |
|   @param Alignment0 The alignment to return when Address is 0.
 | |
| 
 | |
|   @return The least alignment of the Address.
 | |
| **/
 | |
| UINT64
 | |
| MtrrLibBiggestAlignment (
 | |
|   UINT64    Address,
 | |
|   UINT64    Alignment0
 | |
| )
 | |
| {
 | |
|   if (Address == 0) {
 | |
|     return Alignment0;
 | |
|   }
 | |
| 
 | |
|   return Address & ((~Address) + 1);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Return whether the left MTRR type precedes the right MTRR type.
 | |
| 
 | |
|   The MTRR type precedence rules are:
 | |
|     1. UC precedes any other type
 | |
|     2. WT precedes WB
 | |
|   For further details, please refer the IA32 Software Developer's Manual,
 | |
|   Volume 3, Section "MTRR Precedences".
 | |
| 
 | |
|   @param Left  The left MTRR type.
 | |
|   @param Right The right MTRR type.
 | |
| 
 | |
|   @retval TRUE  Left precedes Right.
 | |
|   @retval FALSE Left doesn't precede Right.
 | |
| **/
 | |
| BOOLEAN
 | |
| MtrrLibTypeLeftPrecedeRight (
 | |
|   IN MTRR_MEMORY_CACHE_TYPE  Left,
 | |
|   IN MTRR_MEMORY_CACHE_TYPE  Right
 | |
| )
 | |
| {
 | |
|   return (BOOLEAN) (Left == CacheUncacheable || (Left == CacheWriteThrough && Right == CacheWriteBack));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initializes the valid bits mask and valid address mask for MTRRs.
 | |
| 
 | |
|   This function initializes the valid bits mask and valid address mask for MTRRs.
 | |
| 
 | |
|   @param[out]  MtrrValidBitsMask     The mask for the valid bit of the MTRR
 | |
|   @param[out]  MtrrValidAddressMask  The valid address mask for the MTRR
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| MtrrLibInitializeMtrrMask (
 | |
|   OUT UINT64 *MtrrValidBitsMask,
 | |
|   OUT UINT64 *MtrrValidAddressMask
 | |
|   )
 | |
| {
 | |
|   UINT32                          MaxExtendedFunction;
 | |
|   CPUID_VIR_PHY_ADDRESS_SIZE_EAX  VirPhyAddressSize;
 | |
| 
 | |
| 
 | |
|   AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunction, NULL, NULL, NULL);
 | |
| 
 | |
|   if (MaxExtendedFunction >= CPUID_VIR_PHY_ADDRESS_SIZE) {
 | |
|     AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL);
 | |
|   } else {
 | |
|     VirPhyAddressSize.Bits.PhysicalAddressBits = 36;
 | |
|   }
 | |
| 
 | |
|   *MtrrValidBitsMask = LShiftU64 (1, VirPhyAddressSize.Bits.PhysicalAddressBits) - 1;
 | |
|   *MtrrValidAddressMask = *MtrrValidBitsMask & 0xfffffffffffff000ULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Determines the real attribute of a memory range.
 | |
| 
 | |
|   This function is to arbitrate the real attribute of the memory when
 | |
|   there are 2 MTRRs covers the same memory range. For further details,
 | |
|   please refer the IA32 Software Developer's Manual, Volume 3,
 | |
|   Section "MTRR Precedences".
 | |
| 
 | |
|   @param[in]  MtrrType1    The first kind of Memory type
 | |
|   @param[in]  MtrrType2    The second kind of memory type
 | |
| 
 | |
| **/
 | |
| MTRR_MEMORY_CACHE_TYPE
 | |
| MtrrLibPrecedence (
 | |
|   IN MTRR_MEMORY_CACHE_TYPE    MtrrType1,
 | |
|   IN MTRR_MEMORY_CACHE_TYPE    MtrrType2
 | |
|   )
 | |
| {
 | |
|   if (MtrrType1 == MtrrType2) {
 | |
|     return MtrrType1;
 | |
|   }
 | |
| 
 | |
|   ASSERT (
 | |
|     MtrrLibTypeLeftPrecedeRight (MtrrType1, MtrrType2) ||
 | |
|     MtrrLibTypeLeftPrecedeRight (MtrrType2, MtrrType1)
 | |
|   );
 | |
| 
 | |
|   if (MtrrLibTypeLeftPrecedeRight (MtrrType1, MtrrType2)) {
 | |
|     return MtrrType1;
 | |
|   } else {
 | |
|     return MtrrType2;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Worker function will get the memory cache type of the specific address.
 | |
| 
 | |
|   If MtrrSetting is not NULL, gets the memory cache type from input
 | |
|   MTRR settings buffer.
 | |
|   If MtrrSetting is NULL, gets the memory cache type from MTRRs.
 | |
| 
 | |
|   @param[in]  MtrrSetting        A buffer holding all MTRRs content.
 | |
|   @param[in]  Address            The specific address
 | |
| 
 | |
|   @return Memory cache type of the specific address
 | |
| 
 | |
| **/
 | |
| MTRR_MEMORY_CACHE_TYPE
 | |
| MtrrGetMemoryAttributeByAddressWorker (
 | |
|   IN MTRR_SETTINGS      *MtrrSetting,
 | |
|   IN PHYSICAL_ADDRESS   Address
 | |
|   )
 | |
| {
 | |
|   MSR_IA32_MTRR_DEF_TYPE_REGISTER DefType;
 | |
|   UINT64                          FixedMtrr;
 | |
|   UINTN                           Index;
 | |
|   UINTN                           SubIndex;
 | |
|   MTRR_MEMORY_CACHE_TYPE          MtrrType;
 | |
|   MTRR_MEMORY_RANGE               VariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
 | |
|   UINT64                          MtrrValidBitsMask;
 | |
|   UINT64                          MtrrValidAddressMask;
 | |
|   UINT32                          VariableMtrrCount;
 | |
|   MTRR_VARIABLE_SETTINGS          VariableSettings;
 | |
| 
 | |
|   //
 | |
|   // Check if MTRR is enabled, if not, return UC as attribute
 | |
|   //
 | |
|   if (MtrrSetting == NULL) {
 | |
|     DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
 | |
|   } else {
 | |
|     DefType.Uint64 = MtrrSetting->MtrrDefType;
 | |
|   }
 | |
| 
 | |
|   if (DefType.Bits.E == 0) {
 | |
|     return CacheUncacheable;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If address is less than 1M, then try to go through the fixed MTRR
 | |
|   //
 | |
|   if (Address < BASE_1MB) {
 | |
|     if (DefType.Bits.FE != 0) {
 | |
|       //
 | |
|       // Go through the fixed MTRR
 | |
|       //
 | |
|       for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
 | |
|         if (Address >= mMtrrLibFixedMtrrTable[Index].BaseAddress &&
 | |
|             Address < mMtrrLibFixedMtrrTable[Index].BaseAddress +
 | |
|             (mMtrrLibFixedMtrrTable[Index].Length * 8)) {
 | |
|           SubIndex =
 | |
|             ((UINTN) Address - mMtrrLibFixedMtrrTable[Index].BaseAddress) /
 | |
|             mMtrrLibFixedMtrrTable[Index].Length;
 | |
|           if (MtrrSetting == NULL) {
 | |
|             FixedMtrr = AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr);
 | |
|           } else {
 | |
|             FixedMtrr = MtrrSetting->Fixed.Mtrr[Index];
 | |
|           }
 | |
|           return (MTRR_MEMORY_CACHE_TYPE) (RShiftU64 (FixedMtrr, SubIndex * 8) & 0xFF);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   VariableMtrrCount = GetVariableMtrrCountWorker ();
 | |
|   ASSERT (VariableMtrrCount <= ARRAY_SIZE (MtrrSetting->Variables.Mtrr));
 | |
|   MtrrGetVariableMtrrWorker (MtrrSetting, VariableMtrrCount, &VariableSettings);
 | |
| 
 | |
|   MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
 | |
|   MtrrLibGetRawVariableRanges (
 | |
|     &VariableSettings,
 | |
|     VariableMtrrCount,
 | |
|     MtrrValidBitsMask,
 | |
|     MtrrValidAddressMask,
 | |
|     VariableMtrr
 | |
|   );
 | |
| 
 | |
|   //
 | |
|   // Go through the variable MTRR
 | |
|   //
 | |
|   MtrrType = CacheInvalid;
 | |
|   for (Index = 0; Index < VariableMtrrCount; Index++) {
 | |
|     if (VariableMtrr[Index].Length != 0) {
 | |
|       if (Address >= VariableMtrr[Index].BaseAddress &&
 | |
|           Address < VariableMtrr[Index].BaseAddress + VariableMtrr[Index].Length) {
 | |
|         if (MtrrType == CacheInvalid) {
 | |
|           MtrrType = (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type;
 | |
|         } else {
 | |
|           MtrrType = MtrrLibPrecedence (MtrrType, (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If there is no MTRR which covers the Address, use the default MTRR type.
 | |
|   //
 | |
|   if (MtrrType == CacheInvalid) {
 | |
|     MtrrType = (MTRR_MEMORY_CACHE_TYPE) DefType.Bits.Type;
 | |
|   }
 | |
| 
 | |
|   return MtrrType;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This function will get the memory cache type of the specific address.
 | |
| 
 | |
|   This function is mainly for debug purpose.
 | |
| 
 | |
|   @param[in]  Address   The specific address
 | |
| 
 | |
|   @return Memory cache type of the specific address
 | |
| 
 | |
| **/
 | |
| MTRR_MEMORY_CACHE_TYPE
 | |
| EFIAPI
 | |
| MtrrGetMemoryAttribute (
 | |
|   IN PHYSICAL_ADDRESS   Address
 | |
|   )
 | |
| {
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return CacheUncacheable;
 | |
|   }
 | |
| 
 | |
|   return MtrrGetMemoryAttributeByAddressWorker (NULL, Address);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Update the Ranges array to change the specified range identified by
 | |
|   BaseAddress and Length to Type.
 | |
| 
 | |
|   @param Ranges      Array holding memory type settings for all memory regions.
 | |
|   @param Capacity    The maximum count of memory ranges the array can hold.
 | |
|   @param Count       Return the new memory range count in the array.
 | |
|   @param BaseAddress The base address of the memory range to change type.
 | |
|   @param Length      The length of the memory range to change type.
 | |
|   @param Type        The new type of the specified memory range.
 | |
| 
 | |
|   @retval RETURN_SUCCESS          The type of the specified memory range is
 | |
|                                   changed successfully.
 | |
|   @retval RETURN_ALREADY_STARTED  The type of the specified memory range equals
 | |
|                                   to the desired type.
 | |
|   @retval RETURN_OUT_OF_RESOURCES The new type set causes the count of memory
 | |
|                                   range exceeds capacity.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| MtrrLibSetMemoryType (
 | |
|   IN MTRR_MEMORY_RANGE             *Ranges,
 | |
|   IN UINTN                         Capacity,
 | |
|   IN OUT UINTN                     *Count,
 | |
|   IN UINT64                        BaseAddress,
 | |
|   IN UINT64                        Length,
 | |
|   IN MTRR_MEMORY_CACHE_TYPE        Type
 | |
|   )
 | |
| {
 | |
|   UINTN                            Index;
 | |
|   UINT64                           Limit;
 | |
|   UINT64                           LengthLeft;
 | |
|   UINT64                           LengthRight;
 | |
|   UINTN                            StartIndex;
 | |
|   UINTN                            EndIndex;
 | |
|   UINTN                            DeltaCount;
 | |
| 
 | |
|   LengthRight = 0;
 | |
|   LengthLeft  = 0;
 | |
|   Limit = BaseAddress + Length;
 | |
|   StartIndex = *Count;
 | |
|   EndIndex = *Count;
 | |
|   for (Index = 0; Index < *Count; Index++) {
 | |
|     if ((StartIndex == *Count) &&
 | |
|         (Ranges[Index].BaseAddress <= BaseAddress) &&
 | |
|         (BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length)) {
 | |
|       StartIndex = Index;
 | |
|       LengthLeft = BaseAddress - Ranges[Index].BaseAddress;
 | |
|     }
 | |
| 
 | |
|     if ((EndIndex == *Count) &&
 | |
|         (Ranges[Index].BaseAddress < Limit) &&
 | |
|         (Limit <= Ranges[Index].BaseAddress + Ranges[Index].Length)) {
 | |
|       EndIndex = Index;
 | |
|       LengthRight = Ranges[Index].BaseAddress + Ranges[Index].Length - Limit;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ASSERT (StartIndex != *Count && EndIndex != *Count);
 | |
|   if (StartIndex == EndIndex && Ranges[StartIndex].Type == Type) {
 | |
|     return RETURN_ALREADY_STARTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // The type change may cause merging with previous range or next range.
 | |
|   // Update the StartIndex, EndIndex, BaseAddress, Length so that following
 | |
|   // logic doesn't need to consider merging.
 | |
|   //
 | |
|   if (StartIndex != 0) {
 | |
|     if (LengthLeft == 0 && Ranges[StartIndex - 1].Type == Type) {
 | |
|       StartIndex--;
 | |
|       Length += Ranges[StartIndex].Length;
 | |
|       BaseAddress -= Ranges[StartIndex].Length;
 | |
|     }
 | |
|   }
 | |
|   if (EndIndex != (*Count) - 1) {
 | |
|     if (LengthRight == 0 && Ranges[EndIndex + 1].Type == Type) {
 | |
|       EndIndex++;
 | |
|       Length += Ranges[EndIndex].Length;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // |- 0 -|- 1 -|- 2 -|- 3 -| StartIndex EndIndex DeltaCount  Count (Count = 4)
 | |
|   //   |++++++++++++++++++|    0          3         1=3-0-2    3
 | |
|   //   |+++++++|               0          1        -1=1-0-2    5
 | |
|   //   |+|                     0          0        -2=0-0-2    6
 | |
|   // |+++|                     0          0        -1=0-0-2+1  5
 | |
|   //
 | |
|   //
 | |
|   DeltaCount = EndIndex - StartIndex - 2;
 | |
|   if (LengthLeft == 0) {
 | |
|     DeltaCount++;
 | |
|   }
 | |
|   if (LengthRight == 0) {
 | |
|     DeltaCount++;
 | |
|   }
 | |
|   if (*Count - DeltaCount > Capacity) {
 | |
|     return RETURN_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Reserve (-DeltaCount) space
 | |
|   //
 | |
|   CopyMem (&Ranges[EndIndex + 1 - DeltaCount], &Ranges[EndIndex + 1], (*Count - EndIndex - 1) * sizeof (Ranges[0]));
 | |
|   *Count -= DeltaCount;
 | |
| 
 | |
|   if (LengthLeft != 0) {
 | |
|     Ranges[StartIndex].Length = LengthLeft;
 | |
|     StartIndex++;
 | |
|   }
 | |
|   if (LengthRight != 0) {
 | |
|     Ranges[EndIndex - DeltaCount].BaseAddress = BaseAddress + Length;
 | |
|     Ranges[EndIndex - DeltaCount].Length = LengthRight;
 | |
|     Ranges[EndIndex - DeltaCount].Type = Ranges[EndIndex].Type;
 | |
|   }
 | |
|   Ranges[StartIndex].BaseAddress = BaseAddress;
 | |
|   Ranges[StartIndex].Length = Length;
 | |
|   Ranges[StartIndex].Type = Type;
 | |
|   return RETURN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Return the number of memory types in range [BaseAddress, BaseAddress + Length).
 | |
| 
 | |
|   @param Ranges      Array holding memory type settings for all memory regions.
 | |
|   @param RangeCount  The count of memory ranges the array holds.
 | |
|   @param BaseAddress Base address.
 | |
|   @param Length      Length.
 | |
|   @param Types       Return bit mask to indicate all memory types in the specified range.
 | |
| 
 | |
|   @retval  Number of memory types.
 | |
| **/
 | |
| UINT8
 | |
| MtrrLibGetNumberOfTypes (
 | |
|   IN CONST MTRR_MEMORY_RANGE     *Ranges,
 | |
|   IN UINTN                       RangeCount,
 | |
|   IN UINT64                      BaseAddress,
 | |
|   IN UINT64                      Length,
 | |
|   IN OUT UINT8                   *Types  OPTIONAL
 | |
|   )
 | |
| {
 | |
|   UINTN                          Index;
 | |
|   UINT8                          TypeCount;
 | |
|   UINT8                          LocalTypes;
 | |
| 
 | |
|   TypeCount = 0;
 | |
|   LocalTypes = 0;
 | |
|   for (Index = 0; Index < RangeCount; Index++) {
 | |
|     if ((Ranges[Index].BaseAddress <= BaseAddress) &&
 | |
|         (BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length)
 | |
|         ) {
 | |
|       if ((LocalTypes & (1 << Ranges[Index].Type)) == 0) {
 | |
|         LocalTypes |= (UINT8)(1 << Ranges[Index].Type);
 | |
|         TypeCount++;
 | |
|       }
 | |
| 
 | |
|       if (BaseAddress + Length > Ranges[Index].BaseAddress + Ranges[Index].Length) {
 | |
|         Length -= Ranges[Index].BaseAddress + Ranges[Index].Length - BaseAddress;
 | |
|         BaseAddress = Ranges[Index].BaseAddress + Ranges[Index].Length;
 | |
|       } else {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Types != NULL) {
 | |
|     *Types = LocalTypes;
 | |
|   }
 | |
|   return TypeCount;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Calculate the least MTRR number from vertex Start to Stop and update
 | |
|   the Previous of all vertices from Start to Stop is updated to reflect
 | |
|   how the memory range is covered by MTRR.
 | |
| 
 | |
|   @param VertexCount     The count of vertices in the graph.
 | |
|   @param Vertices        Array holding all vertices.
 | |
|   @param Weight          2-dimention array holding weights between vertices.
 | |
|   @param Start           Start vertex.
 | |
|   @param Stop            Stop vertex.
 | |
|   @param IncludeOptional TRUE to count the optional weight.
 | |
| **/
 | |
| VOID
 | |
| MtrrLibCalculateLeastMtrrs (
 | |
|   IN UINT16                      VertexCount,
 | |
|   IN MTRR_LIB_ADDRESS            *Vertices,
 | |
|   IN OUT CONST UINT8             *Weight,
 | |
|   IN UINT16                      Start,
 | |
|   IN UINT16                      Stop,
 | |
|   IN BOOLEAN                     IncludeOptional
 | |
|   )
 | |
| {
 | |
|   UINT16                         Index;
 | |
|   UINT8                          MinWeight;
 | |
|   UINT16                         MinI;
 | |
|   UINT8                          Mandatory;
 | |
|   UINT8                          Optional;
 | |
| 
 | |
|   for (Index = Start; Index <= Stop; Index++) {
 | |
|     Vertices[Index].Visited = FALSE;
 | |
|     Mandatory = Weight[M(Start,Index)];
 | |
|     Vertices[Index].Weight = Mandatory;
 | |
|     if (Mandatory != MAX_WEIGHT) {
 | |
|       Optional = IncludeOptional ? Weight[O(Start, Index)] : 0;
 | |
|       Vertices[Index].Weight += Optional;
 | |
|       ASSERT (Vertices[Index].Weight >= Optional);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   MinI = Start;
 | |
|   MinWeight = 0;
 | |
|   while (!Vertices[Stop].Visited) {
 | |
|     //
 | |
|     // Update the weight from the shortest vertex to other unvisited vertices
 | |
|     //
 | |
|     for (Index = Start + 1; Index <= Stop; Index++) {
 | |
|       if (!Vertices[Index].Visited) {
 | |
|         Mandatory = Weight[M(MinI, Index)];
 | |
|         if (Mandatory != MAX_WEIGHT) {
 | |
|           Optional = IncludeOptional ? Weight[O(MinI, Index)] : 0;
 | |
|           if (MinWeight + Mandatory + Optional <= Vertices[Index].Weight) {
 | |
|             Vertices[Index].Weight   = MinWeight + Mandatory + Optional;
 | |
|             Vertices[Index].Previous = MinI; // Previous is Start based.
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Find the shortest vertex from Start
 | |
|     //
 | |
|     MinI      = VertexCount;
 | |
|     MinWeight = MAX_WEIGHT;
 | |
|     for (Index = Start + 1; Index <= Stop; Index++) {
 | |
|       if (!Vertices[Index].Visited && MinWeight > Vertices[Index].Weight) {
 | |
|         MinI      = Index;
 | |
|         MinWeight = Vertices[Index].Weight;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Mark the shortest vertex from Start as visited
 | |
|     //
 | |
|     Vertices[MinI].Visited = TRUE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Append the MTRR setting to MTRR setting array.
 | |
| 
 | |
|   @param Mtrrs        Array holding all MTRR settings.
 | |
|   @param MtrrCapacity Capacity of the MTRR array.
 | |
|   @param MtrrCount    The count of MTRR settings in array.
 | |
|   @param BaseAddress  Base address.
 | |
|   @param Length       Length.
 | |
|   @param Type         Memory type.
 | |
| 
 | |
|   @retval RETURN_SUCCESS          MTRR setting is appended to array.
 | |
|   @retval RETURN_OUT_OF_RESOURCES Array is full.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| MtrrLibAppendVariableMtrr (
 | |
|   IN OUT MTRR_MEMORY_RANGE       *Mtrrs,
 | |
|   IN     UINT32                  MtrrCapacity,
 | |
|   IN OUT UINT32                  *MtrrCount,
 | |
|   IN     UINT64                  BaseAddress,
 | |
|   IN     UINT64                  Length,
 | |
|   IN     MTRR_MEMORY_CACHE_TYPE  Type
 | |
|   )
 | |
| {
 | |
|   if (*MtrrCount == MtrrCapacity) {
 | |
|     return RETURN_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   Mtrrs[*MtrrCount].BaseAddress = BaseAddress;
 | |
|   Mtrrs[*MtrrCount].Length      = Length;
 | |
|   Mtrrs[*MtrrCount].Type        = Type;
 | |
|   (*MtrrCount)++;
 | |
|   return RETURN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Return the memory type that has the least precedence.
 | |
| 
 | |
|   @param TypeBits  Bit mask of memory type.
 | |
| 
 | |
|   @retval  Memory type that has the least precedence.
 | |
| **/
 | |
| MTRR_MEMORY_CACHE_TYPE
 | |
| MtrrLibLowestType (
 | |
|   IN      UINT8                    TypeBits
 | |
| )
 | |
| {
 | |
|   INT8                             Type;
 | |
| 
 | |
|   ASSERT (TypeBits != 0);
 | |
|   for (Type = 7; (INT8)TypeBits > 0; Type--, TypeBits <<= 1);
 | |
|   return (MTRR_MEMORY_CACHE_TYPE)Type;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Return TRUE when the Operand is exactly power of 2.
 | |
| 
 | |
|   @retval TRUE  Operand is exactly power of 2.
 | |
|   @retval FALSE Operand is not power of 2.
 | |
| **/
 | |
| BOOLEAN
 | |
| MtrrLibIsPowerOfTwo (
 | |
|   IN     UINT64                  Operand
 | |
| )
 | |
| {
 | |
|   ASSERT (Operand != 0);
 | |
|   return (BOOLEAN) ((Operand & (Operand - 1)) == 0);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Calculate the subtractive path from vertex Start to Stop.
 | |
| 
 | |
|   @param DefaultType  Default memory type.
 | |
|   @param A0           Alignment to use when base address is 0.
 | |
|   @param Ranges       Array holding memory type settings for all memory regions.
 | |
|   @param RangeCount   The count of memory ranges the array holds.
 | |
|   @param VertexCount  The count of vertices in the graph.
 | |
|   @param Vertices     Array holding all vertices.
 | |
|   @param Weight       2-dimention array holding weights between vertices.
 | |
|   @param Start        Start vertex.
 | |
|   @param Stop         Stop vertex.
 | |
|   @param Types        Type bit mask of memory range from Start to Stop.
 | |
|   @param TypeCount    Number of different memory types from Start to Stop.
 | |
|   @param Mtrrs        Array holding all MTRR settings.
 | |
|   @param MtrrCapacity Capacity of the MTRR array.
 | |
|   @param MtrrCount    The count of MTRR settings in array.
 | |
| 
 | |
|   @retval RETURN_SUCCESS          The subtractive path is calculated successfully.
 | |
|   @retval RETURN_OUT_OF_RESOURCES The MTRR setting array is full.
 | |
| 
 | |
| **/
 | |
| RETURN_STATUS
 | |
| MtrrLibCalculateSubtractivePath (
 | |
|   IN MTRR_MEMORY_CACHE_TYPE      DefaultType,
 | |
|   IN UINT64                      A0,
 | |
|   IN CONST MTRR_MEMORY_RANGE     *Ranges,
 | |
|   IN UINTN                       RangeCount,
 | |
|   IN UINT16                      VertexCount,
 | |
|   IN MTRR_LIB_ADDRESS            *Vertices,
 | |
|   IN OUT UINT8                   *Weight,
 | |
|   IN UINT16                      Start,
 | |
|   IN UINT16                      Stop,
 | |
|   IN UINT8                       Types,
 | |
|   IN UINT8                       TypeCount,
 | |
|   IN OUT MTRR_MEMORY_RANGE       *Mtrrs,       OPTIONAL
 | |
|   IN UINT32                      MtrrCapacity, OPTIONAL
 | |
|   IN OUT UINT32                  *MtrrCount    OPTIONAL
 | |
|   )
 | |
| {
 | |
|   RETURN_STATUS                  Status;
 | |
|   UINT64                         Base;
 | |
|   UINT64                         Length;
 | |
|   UINT8                          PrecedentTypes;
 | |
|   UINTN                          Index;
 | |
|   UINT64                         HBase;
 | |
|   UINT64                         HLength;
 | |
|   UINT64                         SubLength;
 | |
|   UINT16                         SubStart;
 | |
|   UINT16                         SubStop;
 | |
|   UINT16                         Cur;
 | |
|   UINT16                         Pre;
 | |
|   MTRR_MEMORY_CACHE_TYPE         LowestType;
 | |
|   MTRR_MEMORY_CACHE_TYPE         LowestPrecedentType;
 | |
| 
 | |
|   Base   = Vertices[Start].Address;
 | |
|   Length = Vertices[Stop].Address - Base;
 | |
| 
 | |
|   LowestType = MtrrLibLowestType (Types);
 | |
| 
 | |
|   //
 | |
|   // Clear the lowest type (highest bit) to get the precedent types
 | |
|   //
 | |
|   PrecedentTypes = ~(1 << LowestType) & Types;
 | |
|   LowestPrecedentType = MtrrLibLowestType (PrecedentTypes);
 | |
| 
 | |
|   if (Mtrrs == NULL) {
 | |
|     Weight[M(Start, Stop)] = ((LowestType == DefaultType) ? 0 : 1);
 | |
|     Weight[O(Start, Stop)] = ((LowestType == DefaultType) ? 1 : 0);
 | |
|   }
 | |
| 
 | |
|   // Add all high level ranges
 | |
|   HBase = MAX_UINT64;
 | |
|   HLength = 0;
 | |
|   for (Index = 0; Index < RangeCount; Index++) {
 | |
|     if (Length == 0) {
 | |
|       break;
 | |
|     }
 | |
|     if ((Base < Ranges[Index].BaseAddress) || (Ranges[Index].BaseAddress + Ranges[Index].Length <= Base)) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Base is in the Range[Index]
 | |
|     //
 | |
|     if (Base + Length > Ranges[Index].BaseAddress + Ranges[Index].Length) {
 | |
|       SubLength = Ranges[Index].BaseAddress + Ranges[Index].Length - Base;
 | |
|     } else {
 | |
|       SubLength = Length;
 | |
|     }
 | |
|     if (((1 << Ranges[Index].Type) & PrecedentTypes) != 0) {
 | |
|       //
 | |
|       // Meet a range whose types take precedence.
 | |
|       // Update the [HBase, HBase + HLength) to include the range,
 | |
|       // [HBase, HBase + HLength) may contain sub ranges with 2 different types, and both take precedence.
 | |
|       //
 | |
|       if (HBase == MAX_UINT64) {
 | |
|         HBase = Base;
 | |
|       }
 | |
|       HLength += SubLength;
 | |
|     }
 | |
| 
 | |
|     Base += SubLength;
 | |
|     Length -= SubLength;
 | |
| 
 | |
|     if (HLength == 0) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if ((Ranges[Index].Type == LowestType) || (Length == 0)) { // meet low type or end
 | |
| 
 | |
|       //
 | |
|       // Add the MTRRs for each high priority type range
 | |
|       // the range[HBase, HBase + HLength) contains only two types.
 | |
|       // We might use positive or subtractive, depending on which way uses less MTRR
 | |
|       //
 | |
|       for (SubStart = Start; SubStart <= Stop; SubStart++) {
 | |
|         if (Vertices[SubStart].Address == HBase) {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       for (SubStop = SubStart; SubStop <= Stop; SubStop++) {
 | |
|         if (Vertices[SubStop].Address == HBase + HLength) {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       ASSERT (Vertices[SubStart].Address == HBase);
 | |
|       ASSERT (Vertices[SubStop].Address == HBase + HLength);
 | |
| 
 | |
|       if ((TypeCount == 2) || (SubStart == SubStop - 1)) {
 | |
|         //
 | |
|         // add subtractive MTRRs for [HBase, HBase + HLength)
 | |
|         // [HBase, HBase + HLength) contains only one type.
 | |
|         // while - loop is to split the range to MTRR - compliant aligned range.
 | |
|         //
 | |
|         if (Mtrrs == NULL) {
 | |
|           Weight[M (Start, Stop)] += (UINT8)(SubStop - SubStart);
 | |
|         } else {
 | |
|           while (SubStart != SubStop) {
 | |
|             Status = MtrrLibAppendVariableMtrr (
 | |
|               Mtrrs, MtrrCapacity, MtrrCount,
 | |
|               Vertices[SubStart].Address, Vertices[SubStart].Length, Vertices[SubStart].Type
 | |
|             );
 | |
|             if (RETURN_ERROR (Status)) {
 | |
|               return Status;
 | |
|             }
 | |
|             SubStart++;
 | |
|           }
 | |
|         }
 | |
|       } else {
 | |
|         ASSERT (TypeCount == 3);
 | |
|         MtrrLibCalculateLeastMtrrs (VertexCount, Vertices, Weight, SubStart, SubStop, TRUE);
 | |
| 
 | |
|         if (Mtrrs == NULL) {
 | |
|           Weight[M (Start, Stop)] += Vertices[SubStop].Weight;
 | |
|         } else {
 | |
|           // When we need to collect the optimal path from SubStart to SubStop
 | |
|           while (SubStop != SubStart) {
 | |
|             Cur = SubStop;
 | |
|             Pre = Vertices[Cur].Previous;
 | |
|             SubStop = Pre;
 | |
| 
 | |
|             if (Weight[M (Pre, Cur)] + Weight[O (Pre, Cur)] != 0) {
 | |
|               Status = MtrrLibAppendVariableMtrr (
 | |
|                 Mtrrs, MtrrCapacity, MtrrCount,
 | |
|                 Vertices[Pre].Address, Vertices[Cur].Address - Vertices[Pre].Address,
 | |
|                 (Pre != Cur - 1) ? LowestPrecedentType : Vertices[Pre].Type
 | |
|               );
 | |
|               if (RETURN_ERROR (Status)) {
 | |
|                 return Status;
 | |
|               }
 | |
|             }
 | |
|             if (Pre != Cur - 1) {
 | |
|               Status = MtrrLibCalculateSubtractivePath (
 | |
|                 DefaultType, A0,
 | |
|                 Ranges, RangeCount,
 | |
|                 VertexCount, Vertices, Weight,
 | |
|                 Pre, Cur, PrecedentTypes, 2,
 | |
|                 Mtrrs, MtrrCapacity, MtrrCount
 | |
|               );
 | |
|               if (RETURN_ERROR (Status)) {
 | |
|                 return Status;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|       }
 | |
|       //
 | |
|       // Reset HBase, HLength
 | |
|       //
 | |
|       HBase = MAX_UINT64;
 | |
|       HLength = 0;
 | |
|     }
 | |
|   }
 | |
|   return RETURN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Calculate MTRR settings to cover the specified memory ranges.
 | |
| 
 | |
|   @param DefaultType  Default memory type.
 | |
|   @param A0           Alignment to use when base address is 0.
 | |
|   @param Ranges       Memory range array holding the memory type
 | |
|                       settings for all memory address.
 | |
|   @param RangeCount   Count of memory ranges.
 | |
|   @param Scratch      A temporary scratch buffer that is used to perform the calculation.
 | |
|                       This is an optional parameter that may be NULL.
 | |
|   @param ScratchSize  Pointer to the size in bytes of the scratch buffer.
 | |
|                       It may be updated to the actual required size when the calculation
 | |
|                       needs more scratch buffer.
 | |
|   @param Mtrrs        Array holding all MTRR settings.
 | |
|   @param MtrrCapacity Capacity of the MTRR array.
 | |
|   @param MtrrCount    The count of MTRR settings in array.
 | |
| 
 | |
|   @retval RETURN_SUCCESS          Variable MTRRs are allocated successfully.
 | |
|   @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.
 | |
|   @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| MtrrLibCalculateMtrrs (
 | |
|   IN MTRR_MEMORY_CACHE_TYPE  DefaultType,
 | |
|   IN UINT64                  A0,
 | |
|   IN CONST MTRR_MEMORY_RANGE *Ranges,
 | |
|   IN UINTN                   RangeCount,
 | |
|   IN VOID                    *Scratch,
 | |
|   IN OUT UINTN               *ScratchSize,
 | |
|   IN OUT MTRR_MEMORY_RANGE   *Mtrrs,
 | |
|   IN UINT32                  MtrrCapacity,
 | |
|   IN OUT UINT32              *MtrrCount
 | |
|   )
 | |
| {
 | |
|   UINT64                    Base0;
 | |
|   UINT64                    Base1;
 | |
|   UINTN                     Index;
 | |
|   UINT64                    Base;
 | |
|   UINT64                    Length;
 | |
|   UINT64                    Alignment;
 | |
|   UINT64                    SubLength;
 | |
|   MTRR_LIB_ADDRESS          *Vertices;
 | |
|   UINT8                     *Weight;
 | |
|   UINT32                    VertexIndex;
 | |
|   UINT32                    VertexCount;
 | |
|   UINTN                     RequiredScratchSize;
 | |
|   UINT8                     TypeCount;
 | |
|   UINT16                    Start;
 | |
|   UINT16                    Stop;
 | |
|   UINT8                     Type;
 | |
|   RETURN_STATUS             Status;
 | |
| 
 | |
|   Base0 = Ranges[0].BaseAddress;
 | |
|   Base1 = Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length;
 | |
|   MTRR_LIB_ASSERT_ALIGNED (Base0, Base1 - Base0);
 | |
| 
 | |
|   //
 | |
|   // Count the number of vertices.
 | |
|   //
 | |
|   Vertices = (MTRR_LIB_ADDRESS*)Scratch;
 | |
|   for (VertexIndex = 0, Index = 0; Index < RangeCount; Index++) {
 | |
|     Base = Ranges[Index].BaseAddress;
 | |
|     Length = Ranges[Index].Length;
 | |
|     while (Length != 0) {
 | |
|       Alignment = MtrrLibBiggestAlignment (Base, A0);
 | |
|       SubLength = Alignment;
 | |
|       if (SubLength > Length) {
 | |
|         SubLength = GetPowerOfTwo64 (Length);
 | |
|       }
 | |
|       if (VertexIndex < *ScratchSize / sizeof (*Vertices)) {
 | |
|         Vertices[VertexIndex].Address   = Base;
 | |
|         Vertices[VertexIndex].Alignment = Alignment;
 | |
|         Vertices[VertexIndex].Type      = Ranges[Index].Type;
 | |
|         Vertices[VertexIndex].Length    = SubLength;
 | |
|       }
 | |
|       Base   += SubLength;
 | |
|       Length -= SubLength;
 | |
|       VertexIndex++;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Vertices[VertexIndex] = Base1, so whole vertex count is (VertexIndex + 1).
 | |
|   //
 | |
|   VertexCount = VertexIndex + 1;
 | |
|   DEBUG ((
 | |
|     DEBUG_CACHE, "  Count of vertices (%016llx - %016llx) = %d\n",
 | |
|     Ranges[0].BaseAddress, Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length, VertexCount
 | |
|     ));
 | |
|   ASSERT (VertexCount < MAX_UINT16);
 | |
| 
 | |
|   RequiredScratchSize = VertexCount * sizeof (*Vertices) + VertexCount * VertexCount * sizeof (*Weight);
 | |
|   if (*ScratchSize < RequiredScratchSize) {
 | |
|     *ScratchSize = RequiredScratchSize;
 | |
|     return RETURN_BUFFER_TOO_SMALL;
 | |
|   }
 | |
|   Vertices[VertexCount - 1].Address = Base1;
 | |
| 
 | |
|   Weight = (UINT8 *) &Vertices[VertexCount];
 | |
|   for (VertexIndex = 0; VertexIndex < VertexCount; VertexIndex++) {
 | |
|     //
 | |
|     // Set optional weight between vertices and self->self to 0
 | |
|     //
 | |
|     SetMem (&Weight[M(VertexIndex, 0)], VertexIndex + 1, 0);
 | |
|     //
 | |
|     // Set mandatory weight between vertices to MAX_WEIGHT
 | |
|     //
 | |
|     SetMem (&Weight[M (VertexIndex, VertexIndex + 1)], VertexCount - VertexIndex - 1, MAX_WEIGHT);
 | |
| 
 | |
|     // Final result looks like:
 | |
|     //   00 FF FF FF
 | |
|     //   00 00 FF FF
 | |
|     //   00 00 00 FF
 | |
|     //   00 00 00 00
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set mandatory weight and optional weight for adjacent vertices
 | |
|   //
 | |
|   for (VertexIndex = 0; VertexIndex < VertexCount - 1; VertexIndex++) {
 | |
|     if (Vertices[VertexIndex].Type != DefaultType) {
 | |
|       Weight[M (VertexIndex, VertexIndex + 1)] = 1;
 | |
|       Weight[O (VertexIndex, VertexIndex + 1)] = 0;
 | |
|     } else {
 | |
|       Weight[M (VertexIndex, VertexIndex + 1)] = 0;
 | |
|       Weight[O (VertexIndex, VertexIndex + 1)] = 1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (TypeCount = 2; TypeCount <= 3; TypeCount++) {
 | |
|     for (Start = 0; Start < VertexCount; Start++) {
 | |
|       for (Stop = Start + 2; Stop < VertexCount; Stop++) {
 | |
|         ASSERT (Vertices[Stop].Address > Vertices[Start].Address);
 | |
|         Length = Vertices[Stop].Address - Vertices[Start].Address;
 | |
|         if (Length > Vertices[Start].Alignment) {
 | |
|           //
 | |
|           // Pickup a new Start when [Start, Stop) cannot be described by one MTRR.
 | |
|           //
 | |
|           break;
 | |
|         }
 | |
|         if ((Weight[M(Start, Stop)] == MAX_WEIGHT) && MtrrLibIsPowerOfTwo (Length)) {
 | |
|           if (MtrrLibGetNumberOfTypes (
 | |
|                 Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type
 | |
|                 ) == TypeCount) {
 | |
|             //
 | |
|             // Update the Weight[Start, Stop] using subtractive path.
 | |
|             //
 | |
|             MtrrLibCalculateSubtractivePath (
 | |
|               DefaultType, A0,
 | |
|               Ranges, RangeCount,
 | |
|               (UINT16)VertexCount, Vertices, Weight,
 | |
|               Start, Stop, Type, TypeCount,
 | |
|               NULL, 0, NULL
 | |
|               );
 | |
|           } else if (TypeCount == 2) {
 | |
|             //
 | |
|             // Pick up a new Start when we expect 2-type range, but 3-type range is met.
 | |
|             // Because no matter how Stop is increased, we always meet 3-type range.
 | |
|             //
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Status = RETURN_SUCCESS;
 | |
|   MtrrLibCalculateLeastMtrrs ((UINT16) VertexCount, Vertices, Weight, 0, (UINT16) VertexCount - 1, FALSE);
 | |
|   Stop = (UINT16) VertexCount - 1;
 | |
|   while (Stop != 0) {
 | |
|     Start = Vertices[Stop].Previous;
 | |
|     TypeCount = MAX_UINT8;
 | |
|     Type = 0;
 | |
|     if (Weight[M(Start, Stop)] != 0) {
 | |
|       TypeCount = MtrrLibGetNumberOfTypes (Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type);
 | |
|       Status = MtrrLibAppendVariableMtrr (
 | |
|         Mtrrs, MtrrCapacity, MtrrCount,
 | |
|         Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address,
 | |
|         MtrrLibLowestType (Type)
 | |
|         );
 | |
|       if (RETURN_ERROR (Status)) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Start != Stop - 1) {
 | |
|       //
 | |
|       // substractive path
 | |
|       //
 | |
|       if (TypeCount == MAX_UINT8) {
 | |
|         TypeCount = MtrrLibGetNumberOfTypes (
 | |
|                       Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type
 | |
|                       );
 | |
|       }
 | |
|       Status = MtrrLibCalculateSubtractivePath (
 | |
|                  DefaultType, A0,
 | |
|                  Ranges, RangeCount,
 | |
|                  (UINT16) VertexCount, Vertices, Weight, Start, Stop,
 | |
|                  Type, TypeCount,
 | |
|                  Mtrrs, MtrrCapacity, MtrrCount
 | |
|                  );
 | |
|       if (RETURN_ERROR (Status)) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     Stop = Start;
 | |
|   }
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Apply the fixed MTRR settings to memory range array.
 | |
| 
 | |
|   @param Fixed             The fixed MTRR settings.
 | |
|   @param Ranges            Return the memory range array holding memory type
 | |
|                            settings for all memory address.
 | |
|   @param RangeCapacity     The capacity of memory range array.
 | |
|   @param RangeCount        Return the count of memory range.
 | |
| 
 | |
|   @retval RETURN_SUCCESS          The memory range array is returned successfully.
 | |
|   @retval RETURN_OUT_OF_RESOURCES The count of memory ranges exceeds capacity.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| MtrrLibApplyFixedMtrrs (
 | |
|   IN     MTRR_FIXED_SETTINGS  *Fixed,
 | |
|   IN OUT MTRR_MEMORY_RANGE    *Ranges,
 | |
|   IN     UINTN                RangeCapacity,
 | |
|   IN OUT UINTN                *RangeCount
 | |
|   )
 | |
| {
 | |
|   RETURN_STATUS               Status;
 | |
|   UINTN                       MsrIndex;
 | |
|   UINTN                       Index;
 | |
|   MTRR_MEMORY_CACHE_TYPE      MemoryType;
 | |
|   UINT64                      Base;
 | |
| 
 | |
|   Base = 0;
 | |
|   for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) {
 | |
|     ASSERT (Base == mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress);
 | |
|     for (Index = 0; Index < sizeof (UINT64); Index++) {
 | |
|       MemoryType = (MTRR_MEMORY_CACHE_TYPE)((UINT8 *)(&Fixed->Mtrr[MsrIndex]))[Index];
 | |
|       Status = MtrrLibSetMemoryType (
 | |
|                  Ranges, RangeCapacity, RangeCount, Base, mMtrrLibFixedMtrrTable[MsrIndex].Length, MemoryType
 | |
|                  );
 | |
|       if (Status == RETURN_OUT_OF_RESOURCES) {
 | |
|         return Status;
 | |
|       }
 | |
|       Base += mMtrrLibFixedMtrrTable[MsrIndex].Length;
 | |
|     }
 | |
|   }
 | |
|   ASSERT (Base == BASE_1MB);
 | |
|   return RETURN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Apply the variable MTRR settings to memory range array.
 | |
| 
 | |
|   @param VariableMtrr      The variable MTRR array.
 | |
|   @param VariableMtrrCount The count of variable MTRRs.
 | |
|   @param Ranges            Return the memory range array with new MTRR settings applied.
 | |
|   @param RangeCapacity     The capacity of memory range array.
 | |
|   @param RangeCount        Return the count of memory range.
 | |
| 
 | |
|   @retval RETURN_SUCCESS          The memory range array is returned successfully.
 | |
|   @retval RETURN_OUT_OF_RESOURCES The count of memory ranges exceeds capacity.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| MtrrLibApplyVariableMtrrs (
 | |
|   IN     CONST MTRR_MEMORY_RANGE *VariableMtrr,
 | |
|   IN     UINT32                  VariableMtrrCount,
 | |
|   IN OUT MTRR_MEMORY_RANGE       *Ranges,
 | |
|   IN     UINTN                   RangeCapacity,
 | |
|   IN OUT UINTN                   *RangeCount
 | |
|   )
 | |
| {
 | |
|   RETURN_STATUS                  Status;
 | |
|   UINTN                          Index;
 | |
| 
 | |
|   //
 | |
|   // WT > WB
 | |
|   // UC > *
 | |
|   // UC > * (except WB, UC) > WB
 | |
|   //
 | |
| 
 | |
|   //
 | |
|   // 1. Set WB
 | |
|   //
 | |
|   for (Index = 0; Index < VariableMtrrCount; Index++) {
 | |
|     if ((VariableMtrr[Index].Length != 0) && (VariableMtrr[Index].Type == CacheWriteBack)) {
 | |
|       Status = MtrrLibSetMemoryType (
 | |
|         Ranges, RangeCapacity, RangeCount,
 | |
|         VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type
 | |
|       );
 | |
|       if (Status == RETURN_OUT_OF_RESOURCES) {
 | |
|         return Status;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 2. Set other types than WB or UC
 | |
|   //
 | |
|   for (Index = 0; Index < VariableMtrrCount; Index++) {
 | |
|     if ((VariableMtrr[Index].Length != 0) &&
 | |
|         (VariableMtrr[Index].Type != CacheWriteBack) && (VariableMtrr[Index].Type != CacheUncacheable)) {
 | |
|       Status = MtrrLibSetMemoryType (
 | |
|                  Ranges, RangeCapacity, RangeCount,
 | |
|                  VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type
 | |
|                  );
 | |
|       if (Status == RETURN_OUT_OF_RESOURCES) {
 | |
|         return Status;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 3. Set UC
 | |
|   //
 | |
|   for (Index = 0; Index < VariableMtrrCount; Index++) {
 | |
|     if (VariableMtrr[Index].Length != 0 && VariableMtrr[Index].Type == CacheUncacheable) {
 | |
|       Status = MtrrLibSetMemoryType (
 | |
|                  Ranges, RangeCapacity, RangeCount,
 | |
|                  VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type
 | |
|                  );
 | |
|       if (Status == RETURN_OUT_OF_RESOURCES) {
 | |
|         return Status;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return RETURN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Return the memory type bit mask that's compatible to first type in the Ranges.
 | |
| 
 | |
|   @param Ranges     Memory range array holding the memory type
 | |
|                     settings for all memory address.
 | |
|   @param RangeCount Count of memory ranges.
 | |
| 
 | |
|   @return Compatible memory type bit mask.
 | |
| **/
 | |
| UINT8
 | |
| MtrrLibGetCompatibleTypes (
 | |
|   IN CONST MTRR_MEMORY_RANGE *Ranges,
 | |
|   IN UINTN                   RangeCount
 | |
|   )
 | |
| {
 | |
|   ASSERT (RangeCount != 0);
 | |
| 
 | |
|   switch (Ranges[0].Type) {
 | |
|   case CacheWriteBack:
 | |
|   case CacheWriteThrough:
 | |
|     return (1 << CacheWriteBack) | (1 << CacheWriteThrough) | (1 << CacheUncacheable);
 | |
|     break;
 | |
| 
 | |
|   case CacheWriteCombining:
 | |
|   case CacheWriteProtected:
 | |
|     return (1 << Ranges[0].Type) | (1 << CacheUncacheable);
 | |
|     break;
 | |
| 
 | |
|   case CacheUncacheable:
 | |
|     if (RangeCount == 1) {
 | |
|       return (1 << CacheUncacheable);
 | |
|     }
 | |
|     return MtrrLibGetCompatibleTypes (&Ranges[1], RangeCount - 1);
 | |
|     break;
 | |
| 
 | |
|   case CacheInvalid:
 | |
|   default:
 | |
|     ASSERT (FALSE);
 | |
|     break;
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Overwrite the destination MTRR settings with the source MTRR settings.
 | |
|   This routine is to make sure the modification to destination MTRR settings
 | |
|   is as small as possible.
 | |
| 
 | |
|   @param DstMtrrs     Destination MTRR settings.
 | |
|   @param DstMtrrCount Count of destination MTRR settings.
 | |
|   @param SrcMtrrs     Source MTRR settings.
 | |
|   @param SrcMtrrCount Count of source MTRR settings.
 | |
|   @param Modified     Flag array to indicate which destination MTRR setting is modified.
 | |
| **/
 | |
| VOID
 | |
| MtrrLibMergeVariableMtrr (
 | |
|   MTRR_MEMORY_RANGE *DstMtrrs,
 | |
|   UINT32            DstMtrrCount,
 | |
|   MTRR_MEMORY_RANGE *SrcMtrrs,
 | |
|   UINT32            SrcMtrrCount,
 | |
|   BOOLEAN           *Modified
 | |
|   )
 | |
| {
 | |
|   UINT32          DstIndex;
 | |
|   UINT32          SrcIndex;
 | |
| 
 | |
|   ASSERT (SrcMtrrCount <= DstMtrrCount);
 | |
| 
 | |
|   for (DstIndex = 0; DstIndex < DstMtrrCount; DstIndex++) {
 | |
|     Modified[DstIndex] = FALSE;
 | |
| 
 | |
|     if (DstMtrrs[DstIndex].Length == 0) {
 | |
|       continue;
 | |
|     }
 | |
|     for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) {
 | |
|       if (DstMtrrs[DstIndex].BaseAddress == SrcMtrrs[SrcIndex].BaseAddress &&
 | |
|         DstMtrrs[DstIndex].Length == SrcMtrrs[SrcIndex].Length &&
 | |
|         DstMtrrs[DstIndex].Type == SrcMtrrs[SrcIndex].Type) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (SrcIndex == SrcMtrrCount) {
 | |
|       //
 | |
|       // Remove the one from DstMtrrs which is not in SrcMtrrs
 | |
|       //
 | |
|       DstMtrrs[DstIndex].Length = 0;
 | |
|       Modified[DstIndex] = TRUE;
 | |
|     } else {
 | |
|       //
 | |
|       // Remove the one from SrcMtrrs which is also in DstMtrrs
 | |
|       //
 | |
|       SrcMtrrs[SrcIndex].Length = 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Now valid MTRR only exists in either DstMtrrs or SrcMtrrs.
 | |
|   // Merge MTRRs from SrcMtrrs to DstMtrrs
 | |
|   //
 | |
|   DstIndex = 0;
 | |
|   for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) {
 | |
|     if (SrcMtrrs[SrcIndex].Length != 0) {
 | |
| 
 | |
|       //
 | |
|       // Find the empty slot in DstMtrrs
 | |
|       //
 | |
|       while (DstIndex < DstMtrrCount) {
 | |
|         if (DstMtrrs[DstIndex].Length == 0) {
 | |
|           break;
 | |
|         }
 | |
|         DstIndex++;
 | |
|       }
 | |
|       ASSERT (DstIndex < DstMtrrCount);
 | |
|       CopyMem (&DstMtrrs[DstIndex], &SrcMtrrs[SrcIndex], sizeof (SrcMtrrs[0]));
 | |
|       Modified[DstIndex] = TRUE;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Calculate the variable MTRR settings for all memory ranges.
 | |
| 
 | |
|   @param DefaultType          Default memory type.
 | |
|   @param A0                   Alignment to use when base address is 0.
 | |
|   @param Ranges               Memory range array holding the memory type
 | |
|                               settings for all memory address.
 | |
|   @param RangeCount           Count of memory ranges.
 | |
|   @param Scratch              Scratch buffer to be used in MTRR calculation.
 | |
|   @param ScratchSize          Pointer to the size of scratch buffer.
 | |
|   @param VariableMtrr         Array holding all MTRR settings.
 | |
|   @param VariableMtrrCapacity Capacity of the MTRR array.
 | |
|   @param VariableMtrrCount    The count of MTRR settings in array.
 | |
| 
 | |
|   @retval RETURN_SUCCESS          Variable MTRRs are allocated successfully.
 | |
|   @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.
 | |
|   @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation.
 | |
|                                   The required scratch buffer size is returned through ScratchSize.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| MtrrLibSetMemoryRanges (
 | |
|   IN MTRR_MEMORY_CACHE_TYPE DefaultType,
 | |
|   IN UINT64                 A0,
 | |
|   IN MTRR_MEMORY_RANGE      *Ranges,
 | |
|   IN UINTN                  RangeCount,
 | |
|   IN VOID                   *Scratch,
 | |
|   IN OUT UINTN              *ScratchSize,
 | |
|   OUT MTRR_MEMORY_RANGE     *VariableMtrr,
 | |
|   IN UINT32                 VariableMtrrCapacity,
 | |
|   OUT UINT32                *VariableMtrrCount
 | |
|   )
 | |
| {
 | |
|   RETURN_STATUS             Status;
 | |
|   UINT32                    Index;
 | |
|   UINT64                    Base0;
 | |
|   UINT64                    Base1;
 | |
|   UINT64                    Alignment;
 | |
|   UINT8                     CompatibleTypes;
 | |
|   UINT64                    Length;
 | |
|   UINT32                    End;
 | |
|   UINTN                     ActualScratchSize;
 | |
|   UINTN                     BiggestScratchSize;
 | |
| 
 | |
|   *VariableMtrrCount = 0;
 | |
| 
 | |
|   //
 | |
|   // Since the whole ranges need multiple calls of MtrrLibCalculateMtrrs().
 | |
|   // Each call needs different scratch buffer size.
 | |
|   // When the provided scratch buffer size is not sufficient in any call,
 | |
|   // set the GetActualScratchSize to TRUE, and following calls will only
 | |
|   // calculate the actual scratch size for the caller.
 | |
|   //
 | |
|   BiggestScratchSize = 0;
 | |
| 
 | |
|   for (Index = 0; Index < RangeCount;) {
 | |
|     Base0 = Ranges[Index].BaseAddress;
 | |
| 
 | |
|     //
 | |
|     // Full step is optimal
 | |
|     //
 | |
|     while (Index < RangeCount) {
 | |
|       ASSERT (Ranges[Index].BaseAddress == Base0);
 | |
|       Alignment = MtrrLibBiggestAlignment (Base0, A0);
 | |
|       while (Base0 + Alignment <= Ranges[Index].BaseAddress + Ranges[Index].Length) {
 | |
|         if ((BiggestScratchSize <= *ScratchSize) && (Ranges[Index].Type != DefaultType)) {
 | |
|           Status = MtrrLibAppendVariableMtrr (
 | |
|             VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,
 | |
|             Base0, Alignment, Ranges[Index].Type
 | |
|             );
 | |
|           if (RETURN_ERROR (Status)) {
 | |
|             return Status;
 | |
|           }
 | |
|         }
 | |
|         Base0 += Alignment;
 | |
|         Alignment = MtrrLibBiggestAlignment (Base0, A0);
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Remove the above range from Ranges[Index]
 | |
|       //
 | |
|       Ranges[Index].Length -= Base0 - Ranges[Index].BaseAddress;
 | |
|       Ranges[Index].BaseAddress = Base0;
 | |
|       if (Ranges[Index].Length != 0) {
 | |
|         break;
 | |
|       } else {
 | |
|         Index++;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Index == RangeCount) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Find continous ranges [Base0, Base1) which could be combined by MTRR.
 | |
|     // Per SDM, the compatible types between[B0, B1) are:
 | |
|     //   UC, *
 | |
|     //   WB, WT
 | |
|     //   UC, WB, WT
 | |
|     //
 | |
|     CompatibleTypes = MtrrLibGetCompatibleTypes (&Ranges[Index], RangeCount - Index);
 | |
| 
 | |
|     End = Index; // End points to last one that matches the CompatibleTypes.
 | |
|     while (End + 1 < RangeCount) {
 | |
|       if (((1 << Ranges[End + 1].Type) & CompatibleTypes) == 0) {
 | |
|         break;
 | |
|       }
 | |
|       End++;
 | |
|     }
 | |
|     Alignment = MtrrLibBiggestAlignment (Base0, A0);
 | |
|     Length    = GetPowerOfTwo64 (Ranges[End].BaseAddress + Ranges[End].Length - Base0);
 | |
|     Base1     = Base0 + MIN (Alignment, Length);
 | |
| 
 | |
|     //
 | |
|     // Base1 may not in Ranges[End]. Update End to the range Base1 belongs to.
 | |
|     //
 | |
|     End = Index;
 | |
|     while (End + 1 < RangeCount) {
 | |
|       if (Base1 <= Ranges[End + 1].BaseAddress) {
 | |
|         break;
 | |
|       }
 | |
|       End++;
 | |
|     }
 | |
| 
 | |
|     Length = Ranges[End].Length;
 | |
|     Ranges[End].Length = Base1 - Ranges[End].BaseAddress;
 | |
|     ActualScratchSize  = *ScratchSize;
 | |
|     Status = MtrrLibCalculateMtrrs (
 | |
|                DefaultType, A0,
 | |
|                &Ranges[Index], End + 1 - Index,
 | |
|                Scratch, &ActualScratchSize,
 | |
|                VariableMtrr, VariableMtrrCapacity, VariableMtrrCount
 | |
|                );
 | |
|     if (Status == RETURN_BUFFER_TOO_SMALL) {
 | |
|       BiggestScratchSize = MAX (BiggestScratchSize, ActualScratchSize);
 | |
|       //
 | |
|       // Ignore this error, because we need to calculate the biggest
 | |
|       // scratch buffer size.
 | |
|       //
 | |
|       Status = RETURN_SUCCESS;
 | |
|     }
 | |
|     if (RETURN_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     if (Length != Ranges[End].Length) {
 | |
|       Ranges[End].BaseAddress = Base1;
 | |
|       Ranges[End].Length = Length - Ranges[End].Length;
 | |
|       Index = End;
 | |
|     } else {
 | |
|       Index = End + 1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (*ScratchSize < BiggestScratchSize) {
 | |
|     *ScratchSize = BiggestScratchSize;
 | |
|     return RETURN_BUFFER_TOO_SMALL;
 | |
|   }
 | |
|   return RETURN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Set the below-1MB memory attribute to fixed MTRR buffer.
 | |
|   Modified flag array indicates which fixed MTRR is modified.
 | |
| 
 | |
|   @param [in, out] FixedSettings Fixed MTRR buffer.
 | |
|   @param [out]     Modified      Flag array indicating which MTRR is modified.
 | |
|   @param [in]      BaseAddress   Base address.
 | |
|   @param [in]      Length        Length.
 | |
|   @param [in]      Type          Memory type.
 | |
| 
 | |
|   @retval RETURN_SUCCESS      The memory attribute is set successfully.
 | |
|   @retval RETURN_UNSUPPORTED  The requested range or cache type was invalid
 | |
|                               for the fixed MTRRs.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| MtrrLibSetBelow1MBMemoryAttribute (
 | |
|   IN OUT MTRR_FIXED_SETTINGS     *FixedSettings,
 | |
|   OUT BOOLEAN                    *Modified,
 | |
|   IN PHYSICAL_ADDRESS            BaseAddress,
 | |
|   IN UINT64                      Length,
 | |
|   IN MTRR_MEMORY_CACHE_TYPE      Type
 | |
|   )
 | |
| {
 | |
|   RETURN_STATUS             Status;
 | |
|   UINT32                    MsrIndex;
 | |
|   UINT64                    ClearMask;
 | |
|   UINT64                    OrMask;
 | |
|   UINT64                    ClearMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];
 | |
|   UINT64                    OrMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];
 | |
|   BOOLEAN                   LocalModified[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];
 | |
| 
 | |
|   ASSERT (BaseAddress < BASE_1MB);
 | |
| 
 | |
|   SetMem (LocalModified, sizeof (LocalModified), FALSE);
 | |
| 
 | |
|   //
 | |
|   // (Value & ~0 | 0) still equals to (Value)
 | |
|   //
 | |
|   SetMem (ClearMasks, sizeof (ClearMasks), 0);
 | |
|   SetMem (OrMasks, sizeof (OrMasks), 0);
 | |
| 
 | |
|   MsrIndex = (UINT32)-1;
 | |
|   while ((BaseAddress < BASE_1MB) && (Length != 0)) {
 | |
|     Status = MtrrLibProgramFixedMtrr (Type, &BaseAddress, &Length, &MsrIndex, &ClearMask, &OrMask);
 | |
|     if (RETURN_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|     ClearMasks[MsrIndex]    = ClearMask;
 | |
|     OrMasks[MsrIndex]       = OrMask;
 | |
|     Modified[MsrIndex]      = TRUE;
 | |
|     LocalModified[MsrIndex] = TRUE;
 | |
|   }
 | |
| 
 | |
|   for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) {
 | |
|     if (LocalModified[MsrIndex]) {
 | |
|       FixedSettings->Mtrr[MsrIndex] = (FixedSettings->Mtrr[MsrIndex] & ~ClearMasks[MsrIndex]) | OrMasks[MsrIndex];
 | |
|     }
 | |
|   }
 | |
|   return RETURN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function attempts to set the attributes into MTRR setting buffer for multiple memory ranges.
 | |
| 
 | |
|   @param[in, out]  MtrrSetting  MTRR setting buffer to be set.
 | |
|   @param[in]       Scratch      A temporary scratch buffer that is used to perform the calculation.
 | |
|   @param[in, out]  ScratchSize  Pointer to the size in bytes of the scratch buffer.
 | |
|                                 It may be updated to the actual required size when the calculation
 | |
|                                 needs more scratch buffer.
 | |
|   @param[in]       Ranges       Pointer to an array of MTRR_MEMORY_RANGE.
 | |
|                                 When range overlap happens, the last one takes higher priority.
 | |
|                                 When the function returns, either all the attributes are set successfully,
 | |
|                                 or none of them is set.
 | |
|   @param[in]       RangeCount   Count of MTRR_MEMORY_RANGE.
 | |
| 
 | |
|   @retval RETURN_SUCCESS            The attributes were set for all the memory ranges.
 | |
|   @retval RETURN_INVALID_PARAMETER  Length in any range is zero.
 | |
|   @retval RETURN_UNSUPPORTED        The processor does not support one or more bytes of the
 | |
|                                     memory resource range specified by BaseAddress and Length in any range.
 | |
|   @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support for the memory resource
 | |
|                                     range specified by BaseAddress and Length in any range.
 | |
|   @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to modify the attributes of
 | |
|                                     the memory resource ranges.
 | |
|   @retval RETURN_ACCESS_DENIED      The attributes for the memory resource range specified by
 | |
|                                     BaseAddress and Length cannot be modified.
 | |
|   @retval RETURN_BUFFER_TOO_SMALL   The scratch buffer is too small for MTRR calculation.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| EFIAPI
 | |
| MtrrSetMemoryAttributesInMtrrSettings (
 | |
|   IN OUT MTRR_SETTINGS           *MtrrSetting,
 | |
|   IN     VOID                    *Scratch,
 | |
|   IN OUT UINTN                   *ScratchSize,
 | |
|   IN     CONST MTRR_MEMORY_RANGE *Ranges,
 | |
|   IN     UINTN                   RangeCount
 | |
|   )
 | |
| {
 | |
|   RETURN_STATUS             Status;
 | |
|   UINT32                    Index;
 | |
|   UINT64                    BaseAddress;
 | |
|   UINT64                    Length;
 | |
|   BOOLEAN                   Above1MbExist;
 | |
| 
 | |
|   UINT64                    MtrrValidBitsMask;
 | |
|   UINT64                    MtrrValidAddressMask;
 | |
|   MTRR_MEMORY_CACHE_TYPE    DefaultType;
 | |
|   MTRR_VARIABLE_SETTINGS    VariableSettings;
 | |
|   MTRR_MEMORY_RANGE         WorkingRanges[2 * ARRAY_SIZE (MtrrSetting->Variables.Mtrr) + 2];
 | |
|   UINTN                     WorkingRangeCount;
 | |
|   BOOLEAN                   Modified;
 | |
|   MTRR_VARIABLE_SETTING     VariableSetting;
 | |
|   UINT32                    OriginalVariableMtrrCount;
 | |
|   UINT32                    FirmwareVariableMtrrCount;
 | |
|   UINT32                    WorkingVariableMtrrCount;
 | |
|   MTRR_MEMORY_RANGE         OriginalVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
 | |
|   MTRR_MEMORY_RANGE         WorkingVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
 | |
|   BOOLEAN                   VariableSettingModified[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
 | |
| 
 | |
|   BOOLEAN                   FixedSettingsModified[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];
 | |
|   MTRR_FIXED_SETTINGS       WorkingFixedSettings;
 | |
| 
 | |
|   MTRR_CONTEXT              MtrrContext;
 | |
|   BOOLEAN                   MtrrContextValid;
 | |
| 
 | |
|   Status = RETURN_SUCCESS;
 | |
|   MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
 | |
| 
 | |
|   //
 | |
|   // TRUE indicating the accordingly Variable setting needs modificaiton in OriginalVariableMtrr.
 | |
|   //
 | |
|   SetMem (VariableSettingModified, ARRAY_SIZE (VariableSettingModified), FALSE);
 | |
|   //
 | |
|   // TRUE indicating the accordingly Fixed setting needs modification in WorkingFixedSettings.
 | |
|   //
 | |
|   SetMem (FixedSettingsModified, ARRAY_SIZE (FixedSettingsModified), FALSE);
 | |
| 
 | |
|   //
 | |
|   // TRUE indicating the caller requests to set variable MTRRs.
 | |
|   //
 | |
|   Above1MbExist             = FALSE;
 | |
|   OriginalVariableMtrrCount = 0;
 | |
| 
 | |
|   //
 | |
|   // 0. Dump the requests.
 | |
|   //
 | |
|   DEBUG_CODE (
 | |
|     DEBUG ((DEBUG_CACHE, "Mtrr: Set Mem Attribute to %a, ScratchSize = %x%a",
 | |
|             (MtrrSetting == NULL) ? "Hardware" : "Buffer", *ScratchSize,
 | |
|             (RangeCount <= 1) ? "," : "\n"
 | |
|             ));
 | |
|     for (Index = 0; Index < RangeCount; Index++) {
 | |
|       DEBUG ((DEBUG_CACHE, " %a: [%016lx, %016lx)\n",
 | |
|               mMtrrMemoryCacheTypeShortName[MIN (Ranges[Index].Type, CacheInvalid)],
 | |
|               Ranges[Index].BaseAddress, Ranges[Index].BaseAddress + Ranges[Index].Length
 | |
|               ));
 | |
|     }
 | |
|   );
 | |
| 
 | |
|   //
 | |
|   // 1. Validate the parameters.
 | |
|   //
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     Status = RETURN_UNSUPPORTED;
 | |
|     goto Exit;
 | |
|   }
 | |
| 
 | |
|   for (Index = 0; Index < RangeCount; Index++) {
 | |
|     if (Ranges[Index].Length == 0) {
 | |
|       Status = RETURN_INVALID_PARAMETER;
 | |
|       goto Exit;
 | |
|     }
 | |
|     if (((Ranges[Index].BaseAddress & ~MtrrValidAddressMask) != 0) ||
 | |
|         ((((Ranges[Index].BaseAddress + Ranges[Index].Length) & ~MtrrValidAddressMask) != 0) &&
 | |
|           (Ranges[Index].BaseAddress + Ranges[Index].Length) != MtrrValidBitsMask + 1)
 | |
|         ) {
 | |
|       //
 | |
|       // Either the BaseAddress or the Limit doesn't follow the alignment requirement.
 | |
|       // Note: It's still valid if Limit doesn't follow the alignment requirement but equals to MAX Address.
 | |
|       //
 | |
|       Status = RETURN_UNSUPPORTED;
 | |
|       goto Exit;
 | |
|     }
 | |
|     if ((Ranges[Index].Type != CacheUncacheable) &&
 | |
|         (Ranges[Index].Type != CacheWriteCombining) &&
 | |
|         (Ranges[Index].Type != CacheWriteThrough) &&
 | |
|         (Ranges[Index].Type != CacheWriteProtected) &&
 | |
|         (Ranges[Index].Type != CacheWriteBack)) {
 | |
|       Status = RETURN_INVALID_PARAMETER;
 | |
|       goto Exit;
 | |
|     }
 | |
|     if (Ranges[Index].BaseAddress + Ranges[Index].Length > BASE_1MB) {
 | |
|       Above1MbExist = TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 2. Apply the above-1MB memory attribute settings.
 | |
|   //
 | |
|   if (Above1MbExist) {
 | |
|     //
 | |
|     // 2.1. Read all variable MTRRs and convert to Ranges.
 | |
|     //
 | |
|     OriginalVariableMtrrCount = GetVariableMtrrCountWorker ();
 | |
|     MtrrGetVariableMtrrWorker (MtrrSetting, OriginalVariableMtrrCount, &VariableSettings);
 | |
|     MtrrLibGetRawVariableRanges (
 | |
|       &VariableSettings, OriginalVariableMtrrCount,
 | |
|       MtrrValidBitsMask, MtrrValidAddressMask, OriginalVariableMtrr
 | |
|       );
 | |
| 
 | |
|     DefaultType = MtrrGetDefaultMemoryTypeWorker (MtrrSetting);
 | |
|     WorkingRangeCount = 1;
 | |
|     WorkingRanges[0].BaseAddress = 0;
 | |
|     WorkingRanges[0].Length      = MtrrValidBitsMask + 1;
 | |
|     WorkingRanges[0].Type        = DefaultType;
 | |
| 
 | |
|     Status = MtrrLibApplyVariableMtrrs (
 | |
|                OriginalVariableMtrr, OriginalVariableMtrrCount,
 | |
|                WorkingRanges, ARRAY_SIZE (WorkingRanges), &WorkingRangeCount);
 | |
|     ASSERT_RETURN_ERROR (Status);
 | |
| 
 | |
|     ASSERT (OriginalVariableMtrrCount >= PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs));
 | |
|     FirmwareVariableMtrrCount = OriginalVariableMtrrCount - PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs);
 | |
|     ASSERT (WorkingRangeCount <= 2 * FirmwareVariableMtrrCount + 1);
 | |
| 
 | |
|     //
 | |
|     // 2.2. Force [0, 1M) to UC, so that it doesn't impact subtraction algorithm.
 | |
|     //
 | |
|     Status = MtrrLibSetMemoryType (
 | |
|                WorkingRanges, ARRAY_SIZE (WorkingRanges), &WorkingRangeCount,
 | |
|                0, SIZE_1MB, CacheUncacheable
 | |
|                );
 | |
|     ASSERT (Status != RETURN_OUT_OF_RESOURCES);
 | |
| 
 | |
|     //
 | |
|     // 2.3. Apply the new memory attribute settings to Ranges.
 | |
|     //
 | |
|     Modified = FALSE;
 | |
|     for (Index = 0; Index < RangeCount; Index++) {
 | |
|       BaseAddress = Ranges[Index].BaseAddress;
 | |
|       Length = Ranges[Index].Length;
 | |
|       if (BaseAddress < BASE_1MB) {
 | |
|         if (Length <= BASE_1MB - BaseAddress) {
 | |
|           continue;
 | |
|         }
 | |
|         Length -= BASE_1MB - BaseAddress;
 | |
|         BaseAddress = BASE_1MB;
 | |
|       }
 | |
|       Status = MtrrLibSetMemoryType (
 | |
|                  WorkingRanges, ARRAY_SIZE (WorkingRanges), &WorkingRangeCount,
 | |
|                  BaseAddress, Length, Ranges[Index].Type
 | |
|                  );
 | |
|       if (Status == RETURN_ALREADY_STARTED) {
 | |
|         Status = RETURN_SUCCESS;
 | |
|       } else if (Status == RETURN_OUT_OF_RESOURCES) {
 | |
|         goto Exit;
 | |
|       } else {
 | |
|         ASSERT_RETURN_ERROR (Status);
 | |
|         Modified = TRUE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Modified) {
 | |
|       //
 | |
|       // 2.4. Calculate the Variable MTRR settings based on the Ranges.
 | |
|       //      Buffer Too Small may be returned if the scratch buffer size is insufficient.
 | |
|       //
 | |
|       Status = MtrrLibSetMemoryRanges (
 | |
|                  DefaultType, LShiftU64 (1, (UINTN)HighBitSet64 (MtrrValidBitsMask)), WorkingRanges, WorkingRangeCount,
 | |
|                  Scratch, ScratchSize,
 | |
|                  WorkingVariableMtrr, FirmwareVariableMtrrCount + 1, &WorkingVariableMtrrCount
 | |
|                  );
 | |
|       if (RETURN_ERROR (Status)) {
 | |
|         goto Exit;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // 2.5. Remove the [0, 1MB) MTRR if it still exists (not merged with other range)
 | |
|       //
 | |
|       for (Index = 0; Index < WorkingVariableMtrrCount; Index++) {
 | |
|         if (WorkingVariableMtrr[Index].BaseAddress == 0 && WorkingVariableMtrr[Index].Length == SIZE_1MB) {
 | |
|           ASSERT (WorkingVariableMtrr[Index].Type == CacheUncacheable);
 | |
|           WorkingVariableMtrrCount--;
 | |
|           CopyMem (
 | |
|             &WorkingVariableMtrr[Index], &WorkingVariableMtrr[Index + 1],
 | |
|             (WorkingVariableMtrrCount - Index) * sizeof (WorkingVariableMtrr[0])
 | |
|             );
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (WorkingVariableMtrrCount > FirmwareVariableMtrrCount) {
 | |
|         Status = RETURN_OUT_OF_RESOURCES;
 | |
|         goto Exit;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // 2.6. Merge the WorkingVariableMtrr to OriginalVariableMtrr
 | |
|       //      Make sure least modification is made to OriginalVariableMtrr.
 | |
|       //
 | |
|       MtrrLibMergeVariableMtrr (
 | |
|         OriginalVariableMtrr, OriginalVariableMtrrCount,
 | |
|         WorkingVariableMtrr, WorkingVariableMtrrCount,
 | |
|         VariableSettingModified
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 3. Apply the below-1MB memory attribute settings.
 | |
|   //
 | |
|   ZeroMem (WorkingFixedSettings.Mtrr, sizeof (WorkingFixedSettings.Mtrr));
 | |
|   for (Index = 0; Index < RangeCount; Index++) {
 | |
|     if (Ranges[Index].BaseAddress >= BASE_1MB) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     Status = MtrrLibSetBelow1MBMemoryAttribute (
 | |
|                &WorkingFixedSettings, FixedSettingsModified,
 | |
|                Ranges[Index].BaseAddress, Ranges[Index].Length, Ranges[Index].Type
 | |
|                );
 | |
|     if (RETURN_ERROR (Status)) {
 | |
|       goto Exit;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   MtrrContextValid = FALSE;
 | |
|   //
 | |
|   // 4. Write fixed MTRRs that have been modified
 | |
|   //
 | |
|   for (Index = 0; Index < ARRAY_SIZE (FixedSettingsModified); Index++) {
 | |
|     if (FixedSettingsModified[Index]) {
 | |
|       if (MtrrSetting != NULL) {
 | |
|         MtrrSetting->Fixed.Mtrr[Index] = WorkingFixedSettings.Mtrr[Index];
 | |
|       } else {
 | |
|         if (!MtrrContextValid) {
 | |
|           MtrrLibPreMtrrChange (&MtrrContext);
 | |
|           MtrrContextValid = TRUE;
 | |
|         }
 | |
|         AsmWriteMsr64 (
 | |
|           mMtrrLibFixedMtrrTable[Index].Msr,
 | |
|           WorkingFixedSettings.Mtrr[Index]
 | |
|         );
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 5. Write variable MTRRs that have been modified
 | |
|   //
 | |
|   for (Index = 0; Index < OriginalVariableMtrrCount; Index++) {
 | |
|     if (VariableSettingModified[Index]) {
 | |
|       if (OriginalVariableMtrr[Index].Length != 0) {
 | |
|         VariableSetting.Base = (OriginalVariableMtrr[Index].BaseAddress & MtrrValidAddressMask)
 | |
|                              | (UINT8)OriginalVariableMtrr[Index].Type;
 | |
|         VariableSetting.Mask = ((~(OriginalVariableMtrr[Index].Length - 1)) & MtrrValidAddressMask) | BIT11;
 | |
|       } else {
 | |
|         VariableSetting.Base = 0;
 | |
|         VariableSetting.Mask = 0;
 | |
|       }
 | |
|       if (MtrrSetting != NULL) {
 | |
|         CopyMem (&MtrrSetting->Variables.Mtrr[Index], &VariableSetting, sizeof (VariableSetting));
 | |
|       } else {
 | |
|         if (!MtrrContextValid) {
 | |
|           MtrrLibPreMtrrChange (&MtrrContext);
 | |
|           MtrrContextValid = TRUE;
 | |
|         }
 | |
|         AsmWriteMsr64 (
 | |
|           MSR_IA32_MTRR_PHYSBASE0 + (Index << 1),
 | |
|           VariableSetting.Base
 | |
|         );
 | |
|         AsmWriteMsr64 (
 | |
|           MSR_IA32_MTRR_PHYSMASK0 + (Index << 1),
 | |
|           VariableSetting.Mask
 | |
|         );
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (MtrrSetting != NULL) {
 | |
|     ((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.E = 1;
 | |
|     ((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.FE = 1;
 | |
|   } else {
 | |
|     if (MtrrContextValid) {
 | |
|       MtrrLibPostMtrrChange (&MtrrContext);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| Exit:
 | |
|   DEBUG ((DEBUG_CACHE, "  Result = %r\n", Status));
 | |
|   if (!RETURN_ERROR (Status)) {
 | |
|     MtrrDebugPrintAllMtrrsWorker (MtrrSetting);
 | |
|   }
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function attempts to set the attributes into MTRR setting buffer for a memory range.
 | |
| 
 | |
|   @param[in, out]  MtrrSetting  MTRR setting buffer to be set.
 | |
|   @param[in]       BaseAddress  The physical address that is the start address
 | |
|                                 of a memory range.
 | |
|   @param[in]       Length       The size in bytes of the memory range.
 | |
|   @param[in]       Attribute    The bit mask of attributes to set for the
 | |
|                                 memory range.
 | |
| 
 | |
|   @retval RETURN_SUCCESS            The attributes were set for the memory range.
 | |
|   @retval RETURN_INVALID_PARAMETER  Length is zero.
 | |
|   @retval RETURN_UNSUPPORTED        The processor does not support one or more bytes of the
 | |
|                                     memory resource range specified by BaseAddress and Length.
 | |
|   @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support for the memory resource
 | |
|                                     range specified by BaseAddress and Length.
 | |
|   @retval RETURN_ACCESS_DENIED      The attributes for the memory resource range specified by
 | |
|                                     BaseAddress and Length cannot be modified.
 | |
|   @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to modify the attributes of
 | |
|                                     the memory resource range.
 | |
|                                     Multiple memory range attributes setting by calling this API multiple
 | |
|                                     times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean
 | |
|                                     the number of CPU MTRRs are too small to set such memory attributes.
 | |
|                                     Pass the multiple memory range attributes to one call of
 | |
|                                     MtrrSetMemoryAttributesInMtrrSettings() may succeed.
 | |
|   @retval RETURN_BUFFER_TOO_SMALL   The fixed internal scratch buffer is too small for MTRR calculation.
 | |
|                                     Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify
 | |
|                                     external scratch buffer.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| EFIAPI
 | |
| MtrrSetMemoryAttributeInMtrrSettings (
 | |
|   IN OUT MTRR_SETTINGS       *MtrrSetting,
 | |
|   IN PHYSICAL_ADDRESS        BaseAddress,
 | |
|   IN UINT64                  Length,
 | |
|   IN MTRR_MEMORY_CACHE_TYPE  Attribute
 | |
|   )
 | |
| {
 | |
|   UINT8                      Scratch[SCRATCH_BUFFER_SIZE];
 | |
|   UINTN                      ScratchSize;
 | |
|   MTRR_MEMORY_RANGE          Range;
 | |
| 
 | |
|   Range.BaseAddress = BaseAddress;
 | |
|   Range.Length      = Length;
 | |
|   Range.Type        = Attribute;
 | |
|   ScratchSize = sizeof (Scratch);
 | |
|   return MtrrSetMemoryAttributesInMtrrSettings (MtrrSetting, Scratch, &ScratchSize, &Range, 1);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function attempts to set the attributes for a memory range.
 | |
| 
 | |
|   @param[in]  BaseAddress        The physical address that is the start
 | |
|                                  address of a memory range.
 | |
|   @param[in]  Length             The size in bytes of the memory range.
 | |
|   @param[in]  Attributes         The bit mask of attributes to set for the
 | |
|                                  memory range.
 | |
| 
 | |
|   @retval RETURN_SUCCESS            The attributes were set for the memory
 | |
|                                     range.
 | |
|   @retval RETURN_INVALID_PARAMETER  Length is zero.
 | |
|   @retval RETURN_UNSUPPORTED        The processor does not support one or
 | |
|                                     more bytes of the memory resource range
 | |
|                                     specified by BaseAddress and Length.
 | |
|   @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support
 | |
|                                     for the memory resource range specified
 | |
|                                     by BaseAddress and Length.
 | |
|   @retval RETURN_ACCESS_DENIED      The attributes for the memory resource
 | |
|                                     range specified by BaseAddress and Length
 | |
|                                     cannot be modified.
 | |
|   @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to
 | |
|                                     modify the attributes of the memory
 | |
|                                     resource range.
 | |
|                                     Multiple memory range attributes setting by calling this API multiple
 | |
|                                     times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean
 | |
|                                     the number of CPU MTRRs are too small to set such memory attributes.
 | |
|                                     Pass the multiple memory range attributes to one call of
 | |
|                                     MtrrSetMemoryAttributesInMtrrSettings() may succeed.
 | |
|   @retval RETURN_BUFFER_TOO_SMALL   The fixed internal scratch buffer is too small for MTRR calculation.
 | |
|                                     Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify
 | |
|                                     external scratch buffer.
 | |
| **/
 | |
| RETURN_STATUS
 | |
| EFIAPI
 | |
| MtrrSetMemoryAttribute (
 | |
|   IN PHYSICAL_ADDRESS        BaseAddress,
 | |
|   IN UINT64                  Length,
 | |
|   IN MTRR_MEMORY_CACHE_TYPE  Attribute
 | |
|   )
 | |
| {
 | |
|   return MtrrSetMemoryAttributeInMtrrSettings (NULL, BaseAddress, Length, Attribute);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Worker function setting variable MTRRs
 | |
| 
 | |
|   @param[in]  VariableSettings   A buffer to hold variable MTRRs content.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| MtrrSetVariableMtrrWorker (
 | |
|   IN MTRR_VARIABLE_SETTINGS         *VariableSettings
 | |
|   )
 | |
| {
 | |
|   UINT32  Index;
 | |
|   UINT32  VariableMtrrCount;
 | |
| 
 | |
|   VariableMtrrCount = GetVariableMtrrCountWorker ();
 | |
|   ASSERT (VariableMtrrCount <= ARRAY_SIZE (VariableSettings->Mtrr));
 | |
| 
 | |
|   for (Index = 0; Index < VariableMtrrCount; Index++) {
 | |
|     //
 | |
|     // Mask MSR is always updated since caller might need to invalidate the MSR pair.
 | |
|     // Base MSR is skipped when Mask.V is not set.
 | |
|     //
 | |
|     AsmWriteMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1), VariableSettings->Mtrr[Index].Mask);
 | |
|     if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *)&VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) {
 | |
|       AsmWriteMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1), VariableSettings->Mtrr[Index].Base);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This function sets variable MTRRs
 | |
| 
 | |
|   @param[in]  VariableSettings   A buffer to hold variable MTRRs content.
 | |
| 
 | |
|   @return The pointer of VariableSettings
 | |
| 
 | |
| **/
 | |
| MTRR_VARIABLE_SETTINGS*
 | |
| EFIAPI
 | |
| MtrrSetVariableMtrr (
 | |
|   IN MTRR_VARIABLE_SETTINGS         *VariableSettings
 | |
|   )
 | |
| {
 | |
|   MTRR_CONTEXT  MtrrContext;
 | |
| 
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return VariableSettings;
 | |
|   }
 | |
| 
 | |
|   MtrrLibPreMtrrChange (&MtrrContext);
 | |
|   MtrrSetVariableMtrrWorker (VariableSettings);
 | |
|   MtrrLibPostMtrrChange (&MtrrContext);
 | |
|   MtrrDebugPrintAllMtrrs ();
 | |
| 
 | |
|   return  VariableSettings;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Worker function setting fixed MTRRs
 | |
| 
 | |
|   @param[in]  FixedSettings  A buffer to hold fixed MTRRs content.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| MtrrSetFixedMtrrWorker (
 | |
|   IN MTRR_FIXED_SETTINGS          *FixedSettings
 | |
|   )
 | |
| {
 | |
|   UINT32  Index;
 | |
| 
 | |
|   for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
 | |
|      AsmWriteMsr64 (
 | |
|        mMtrrLibFixedMtrrTable[Index].Msr,
 | |
|        FixedSettings->Mtrr[Index]
 | |
|        );
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This function sets fixed MTRRs
 | |
| 
 | |
|   @param[in]  FixedSettings  A buffer to hold fixed MTRRs content.
 | |
| 
 | |
|   @retval The pointer of FixedSettings
 | |
| 
 | |
| **/
 | |
| MTRR_FIXED_SETTINGS*
 | |
| EFIAPI
 | |
| MtrrSetFixedMtrr (
 | |
|   IN MTRR_FIXED_SETTINGS          *FixedSettings
 | |
|   )
 | |
| {
 | |
|   MTRR_CONTEXT  MtrrContext;
 | |
| 
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return FixedSettings;
 | |
|   }
 | |
| 
 | |
|   MtrrLibPreMtrrChange (&MtrrContext);
 | |
|   MtrrSetFixedMtrrWorker (FixedSettings);
 | |
|   MtrrLibPostMtrrChange (&MtrrContext);
 | |
|   MtrrDebugPrintAllMtrrs ();
 | |
| 
 | |
|   return FixedSettings;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This function gets the content in all MTRRs (variable and fixed)
 | |
| 
 | |
|   @param[out]  MtrrSetting  A buffer to hold all MTRRs content.
 | |
| 
 | |
|   @retval the pointer of MtrrSetting
 | |
| 
 | |
| **/
 | |
| MTRR_SETTINGS *
 | |
| EFIAPI
 | |
| MtrrGetAllMtrrs (
 | |
|   OUT MTRR_SETTINGS                *MtrrSetting
 | |
|   )
 | |
| {
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return MtrrSetting;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get fixed MTRRs
 | |
|   //
 | |
|   MtrrGetFixedMtrrWorker (&MtrrSetting->Fixed);
 | |
| 
 | |
|   //
 | |
|   // Get variable MTRRs
 | |
|   //
 | |
|   MtrrGetVariableMtrrWorker (
 | |
|     NULL,
 | |
|     GetVariableMtrrCountWorker (),
 | |
|     &MtrrSetting->Variables
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // Get MTRR_DEF_TYPE value
 | |
|   //
 | |
|   MtrrSetting->MtrrDefType = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
 | |
| 
 | |
|   return MtrrSetting;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This function sets all MTRRs (variable and fixed)
 | |
| 
 | |
|   @param[in]  MtrrSetting  A buffer holding all MTRRs content.
 | |
| 
 | |
|   @retval The pointer of MtrrSetting
 | |
| 
 | |
| **/
 | |
| MTRR_SETTINGS *
 | |
| EFIAPI
 | |
| MtrrSetAllMtrrs (
 | |
|   IN MTRR_SETTINGS                *MtrrSetting
 | |
|   )
 | |
| {
 | |
|   MTRR_CONTEXT  MtrrContext;
 | |
| 
 | |
|   if (!IsMtrrSupported ()) {
 | |
|     return MtrrSetting;
 | |
|   }
 | |
| 
 | |
|   MtrrLibPreMtrrChange (&MtrrContext);
 | |
| 
 | |
|   //
 | |
|   // Set fixed MTRRs
 | |
|   //
 | |
|   MtrrSetFixedMtrrWorker (&MtrrSetting->Fixed);
 | |
| 
 | |
|   //
 | |
|   // Set variable MTRRs
 | |
|   //
 | |
|   MtrrSetVariableMtrrWorker (&MtrrSetting->Variables);
 | |
| 
 | |
|   //
 | |
|   // Set MTRR_DEF_TYPE value
 | |
|   //
 | |
|   AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, MtrrSetting->MtrrDefType);
 | |
| 
 | |
|   MtrrLibPostMtrrChangeEnableCache (&MtrrContext);
 | |
| 
 | |
|   return MtrrSetting;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Checks if MTRR is supported.
 | |
| 
 | |
|   @retval TRUE  MTRR is supported.
 | |
|   @retval FALSE MTRR is not supported.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| IsMtrrSupported (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   CPUID_VERSION_INFO_EDX    Edx;
 | |
|   MSR_IA32_MTRRCAP_REGISTER MtrrCap;
 | |
| 
 | |
|   //
 | |
|   // Check CPUID(1).EDX[12] for MTRR capability
 | |
|   //
 | |
|   AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &Edx.Uint32);
 | |
|   if (Edx.Bits.MTRR == 0) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check number of variable MTRRs and fixed MTRRs existence.
 | |
|   // If number of variable MTRRs is zero, or fixed MTRRs do not
 | |
|   // exist, return false.
 | |
|   //
 | |
|   MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP);
 | |
|   if ((MtrrCap.Bits.VCNT == 0) || (MtrrCap.Bits.FIX == 0)) {
 | |
|     return FALSE;
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Worker function prints all MTRRs for debugging.
 | |
| 
 | |
|   If MtrrSetting is not NULL, print MTRR settings from input MTRR
 | |
|   settings buffer.
 | |
|   If MtrrSetting is NULL, print MTRR settings from MTRRs.
 | |
| 
 | |
|   @param  MtrrSetting    A buffer holding all MTRRs content.
 | |
| **/
 | |
| VOID
 | |
| MtrrDebugPrintAllMtrrsWorker (
 | |
|   IN MTRR_SETTINGS    *MtrrSetting
 | |
|   )
 | |
| {
 | |
|   DEBUG_CODE (
 | |
|     MTRR_SETTINGS     LocalMtrrs;
 | |
|     MTRR_SETTINGS     *Mtrrs;
 | |
|     UINTN             Index;
 | |
|     UINTN             RangeCount;
 | |
|     UINT64            MtrrValidBitsMask;
 | |
|     UINT64            MtrrValidAddressMask;
 | |
|     UINT32            VariableMtrrCount;
 | |
|     BOOLEAN           ContainVariableMtrr;
 | |
|     MTRR_MEMORY_RANGE Ranges[
 | |
|       ARRAY_SIZE (mMtrrLibFixedMtrrTable) * sizeof (UINT64) + 2 * ARRAY_SIZE (Mtrrs->Variables.Mtrr) + 1
 | |
|       ];
 | |
|     MTRR_MEMORY_RANGE RawVariableRanges[ARRAY_SIZE (Mtrrs->Variables.Mtrr)];
 | |
| 
 | |
|     if (!IsMtrrSupported ()) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     VariableMtrrCount = GetVariableMtrrCountWorker ();
 | |
| 
 | |
|     if (MtrrSetting != NULL) {
 | |
|       Mtrrs = MtrrSetting;
 | |
|     } else {
 | |
|       MtrrGetAllMtrrs (&LocalMtrrs);
 | |
|       Mtrrs = &LocalMtrrs;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Dump RAW MTRR contents
 | |
|     //
 | |
|     DEBUG ((DEBUG_CACHE, "MTRR Settings:\n"));
 | |
|     DEBUG ((DEBUG_CACHE, "=============\n"));
 | |
|     DEBUG ((DEBUG_CACHE, "MTRR Default Type: %016lx\n", Mtrrs->MtrrDefType));
 | |
|     for (Index = 0; Index < ARRAY_SIZE (mMtrrLibFixedMtrrTable); Index++) {
 | |
|       DEBUG ((DEBUG_CACHE, "Fixed MTRR[%02d]   : %016lx\n", Index, Mtrrs->Fixed.Mtrr[Index]));
 | |
|     }
 | |
|     ContainVariableMtrr = FALSE;
 | |
|     for (Index = 0; Index < VariableMtrrCount; Index++) {
 | |
|       if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *)&Mtrrs->Variables.Mtrr[Index].Mask)->Bits.V == 0) {
 | |
|         //
 | |
|         // If mask is not valid, then do not display range
 | |
|         //
 | |
|         continue;
 | |
|       }
 | |
|       ContainVariableMtrr = TRUE;
 | |
|       DEBUG ((DEBUG_CACHE, "Variable MTRR[%02d]: Base=%016lx Mask=%016lx\n",
 | |
|         Index,
 | |
|         Mtrrs->Variables.Mtrr[Index].Base,
 | |
|         Mtrrs->Variables.Mtrr[Index].Mask
 | |
|         ));
 | |
|     }
 | |
|     if (!ContainVariableMtrr) {
 | |
|       DEBUG ((DEBUG_CACHE, "Variable MTRR    : None.\n"));
 | |
|     }
 | |
|     DEBUG((DEBUG_CACHE, "\n"));
 | |
| 
 | |
|     //
 | |
|     // Dump MTRR setting in ranges
 | |
|     //
 | |
|     DEBUG((DEBUG_CACHE, "Memory Ranges:\n"));
 | |
|     DEBUG((DEBUG_CACHE, "====================================\n"));
 | |
|     MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
 | |
|     Ranges[0].BaseAddress = 0;
 | |
|     Ranges[0].Length      = MtrrValidBitsMask + 1;
 | |
|     Ranges[0].Type        = MtrrGetDefaultMemoryTypeWorker (Mtrrs);
 | |
|     RangeCount = 1;
 | |
| 
 | |
|     MtrrLibGetRawVariableRanges (
 | |
|       &Mtrrs->Variables, VariableMtrrCount,
 | |
|       MtrrValidBitsMask, MtrrValidAddressMask, RawVariableRanges
 | |
|       );
 | |
|     MtrrLibApplyVariableMtrrs (
 | |
|       RawVariableRanges, VariableMtrrCount,
 | |
|       Ranges, ARRAY_SIZE (Ranges), &RangeCount
 | |
|       );
 | |
| 
 | |
|     MtrrLibApplyFixedMtrrs (&Mtrrs->Fixed, Ranges, ARRAY_SIZE (Ranges), &RangeCount);
 | |
| 
 | |
|     for (Index = 0; Index < RangeCount; Index++) {
 | |
|       DEBUG ((DEBUG_CACHE, "%a:%016lx-%016lx\n",
 | |
|         mMtrrMemoryCacheTypeShortName[Ranges[Index].Type],
 | |
|         Ranges[Index].BaseAddress, Ranges[Index].BaseAddress + Ranges[Index].Length - 1
 | |
|         ));
 | |
|     }
 | |
|   );
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function prints all MTRRs for debugging.
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| MtrrDebugPrintAllMtrrs (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   MtrrDebugPrintAllMtrrsWorker (NULL);
 | |
| }
 |