summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.c2815
1 files changed, 2815 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.c
new file mode 100644
index 00000000..85ae1700
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.c
@@ -0,0 +1,2815 @@
+/** @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 - 2020, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Register/Intel/Cpuid.h>
+#include <Register/Intel/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].Base =
+ AsmReadMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1));
+ VariableSettings->Mtrr[Index].Mask =
+ AsmReadMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1));
+ } else {
+ VariableSettings->Mtrr[Index].Base = MtrrSetting->Variables.Mtrr[Index].Base;
+ VariableSettings->Mtrr[Index].Mask = MtrrSetting->Variables.Mtrr[Index].Mask;
+ }
+ }
+
+ return 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] ClearMasks The bits (when set) to clear in the fixed MTRR MSR.
+ @param [in, out] OrMasks The bits to set in the fixed MTRR MSR.
+ @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 UINT64 *ClearMasks,
+ IN OUT UINT64 *OrMasks,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN MTRR_MEMORY_CACHE_TYPE Type
+ )
+{
+ RETURN_STATUS Status;
+ UINT32 MsrIndex;
+ UINT64 ClearMask;
+ UINT64 OrMask;
+
+ ASSERT (BaseAddress < BASE_1MB);
+
+ 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] = ClearMasks[MsrIndex] | ClearMask;
+ OrMasks[MsrIndex] = (OrMasks[MsrIndex] & ~ClearMask) | OrMask;
+ }
+ 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)];
+
+ UINT64 ClearMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];
+ UINT64 OrMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];
+
+ 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 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.
+ //
+ // (Value & ~0 | 0) still equals to (Value)
+ //
+ ZeroMem (ClearMasks, sizeof (ClearMasks));
+ ZeroMem (OrMasks, sizeof (OrMasks));
+ for (Index = 0; Index < RangeCount; Index++) {
+ if (Ranges[Index].BaseAddress >= BASE_1MB) {
+ continue;
+ }
+
+ Status = MtrrLibSetBelow1MBMemoryAttribute (
+ ClearMasks, OrMasks,
+ 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 (ClearMasks); Index++) {
+ if (ClearMasks[Index] != 0) {
+ if (MtrrSetting != NULL) {
+ MtrrSetting->Fixed.Mtrr[Index] = (MtrrSetting->Fixed.Mtrr[Index] & ~ClearMasks[Index]) | OrMasks[Index];
+ } else {
+ if (!MtrrContextValid) {
+ MtrrLibPreMtrrChange (&MtrrContext);
+ MtrrContextValid = TRUE;
+ }
+ AsmMsrAndThenOr64 (mMtrrLibFixedMtrrTable[Index].Msr, ~ClearMasks[Index], OrMasks[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++) {
+ AsmWriteMsr64 (
+ MSR_IA32_MTRR_PHYSBASE0 + (Index << 1),
+ VariableSettings->Mtrr[Index].Base
+ );
+ AsmWriteMsr64 (
+ MSR_IA32_MTRR_PHYSMASK0 + (Index << 1),
+ VariableSettings->Mtrr[Index].Mask
+ );
+ }
+}
+
+/**
+ 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 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 ((Mtrrs->Variables.Mtrr[Index].Mask & BIT11) == 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);
+}