/** @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 - 2023, 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; | |
MSR_IA32_MTRR_DEF_TYPE_REGISTER DefType; | |
} 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 | |
); | |
/** | |
Return whether MTRR is supported. | |
@param[out] FixedMtrrSupported Return whether fixed MTRR is supported. | |
@param[out] VariableMtrrCount Return the max number of variable MTRRs. | |
@retval TRUE MTRR is supported when either fixed MTRR is supported or max number | |
of variable MTRRs is not 0. | |
@retval FALSE MTRR is not supported when both fixed MTRR is not supported and max | |
number of variable MTRRs is 0. | |
**/ | |
BOOLEAN | |
MtrrLibIsMtrrSupported ( | |
OUT BOOLEAN *FixedMtrrSupported OPTIONAL, | |
OUT UINT32 *VariableMtrrCount OPTIONAL | |
) | |
{ | |
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) { | |
if (FixedMtrrSupported != NULL) { | |
*FixedMtrrSupported = FALSE; | |
} | |
if (VariableMtrrCount != NULL) { | |
*VariableMtrrCount = 0; | |
} | |
return FALSE; | |
} | |
// | |
// Check the number of variable MTRRs and determine whether fixed MTRRs exist. | |
// If the count of variable MTRRs is zero and there are no fixed MTRRs, | |
// then return false | |
// | |
MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP); | |
ASSERT (MtrrCap.Bits.VCNT <= ARRAY_SIZE (((MTRR_VARIABLE_SETTINGS *)0)->Mtrr)); | |
if (FixedMtrrSupported != NULL) { | |
*FixedMtrrSupported = (BOOLEAN)(MtrrCap.Bits.FIX == 1); | |
} | |
if (VariableMtrrCount != NULL) { | |
*VariableMtrrCount = MtrrCap.Bits.VCNT; | |
} | |
if ((MtrrCap.Bits.VCNT == 0) && (MtrrCap.Bits.FIX == 0)) { | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/** | |
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 CONST 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 (); | |
// | |
// Save current MTRR default type and disable MTRRs | |
// | |
MtrrContext->DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE); | |
DefType.Uint64 = MtrrContext->DefType.Uint64; | |
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 | |
) | |
{ | |
// | |
// Enable Cache MTRR | |
// Note: It's possible that MTRR was not enabled earlier. | |
// But it will be enabled here unconditionally. | |
// | |
MtrrContext->DefType.Bits.E = 1; | |
AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, MtrrContext->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 | |
) | |
{ | |
BOOLEAN FixedMtrrSupported; | |
MtrrLibIsMtrrSupported (&FixedMtrrSupported, NULL); | |
if (!FixedMtrrSupported) { | |
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 CONST 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; | |
UINT32 MaxFunction; | |
CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_ECX ExtendedFeatureFlagsEcx; | |
MSR_IA32_TME_ACTIVATE_REGISTER TmeActivate; | |
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; | |
} | |
// | |
// CPUID enumeration of MAX_PA is unaffected by TME-MK activation and will continue | |
// to report the maximum physical address bits available for software to use, | |
// irrespective of the number of KeyID bits. | |
// So, we need to check if TME is enabled and adjust the PA size accordingly. | |
// | |
AsmCpuid (CPUID_SIGNATURE, &MaxFunction, NULL, NULL, NULL); | |
if (MaxFunction >= CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS) { | |
AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, 0, NULL, NULL, &ExtendedFeatureFlagsEcx.Uint32, NULL); | |
if (ExtendedFeatureFlagsEcx.Bits.TME_EN == 1) { | |
TmeActivate.Uint64 = AsmReadMsr64 (MSR_IA32_TME_ACTIVATE); | |
if (TmeActivate.Bits.TmeEnable == 1) { | |
VirPhyAddressSize.Bits.PhysicalAddressBits -= TmeActivate.Bits.MkTmeKeyidBits; | |
} | |
} | |
} | |
*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; | |
ASSERT (Length != 0); | |
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; | |
} | |
/** | |
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) && IS_POW2 (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 CONST 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 VariableMtrrNeeded; | |
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 FixedMtrrMemoryLimit; | |
BOOLEAN FixedMtrrSupported; | |
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. | |
// | |
VariableMtrrNeeded = FALSE; | |
OriginalVariableMtrrCount = 0; | |
// | |
// 0. Dump the requests. | |
// | |
DEBUG_CODE_BEGIN (); | |
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 | |
)); | |
} | |
DEBUG_CODE_END (); | |
// | |
// 1. Validate the parameters. | |
// | |
if (!MtrrLibIsMtrrSupported (&FixedMtrrSupported, &OriginalVariableMtrrCount)) { | |
Status = RETURN_UNSUPPORTED; | |
goto Exit; | |
} | |
FixedMtrrMemoryLimit = FixedMtrrSupported ? BASE_1MB : 0; | |
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 > FixedMtrrMemoryLimit) { | |
VariableMtrrNeeded = TRUE; | |
} | |
} | |
// | |
// 2. Apply the above-1MB memory attribute settings. | |
// | |
if (VariableMtrrNeeded) { | |
// | |
// 2.1. Read all variable MTRRs and convert to Ranges. | |
// | |
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. | |
// | |
if (FixedMtrrMemoryLimit != 0) { | |
Status = MtrrLibSetMemoryType ( | |
WorkingRanges, | |
ARRAY_SIZE (WorkingRanges), | |
&WorkingRangeCount, | |
0, | |
FixedMtrrMemoryLimit, | |
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 < FixedMtrrMemoryLimit) { | |
if (Length <= FixedMtrrMemoryLimit - BaseAddress) { | |
continue; | |
} | |
Length -= FixedMtrrMemoryLimit - BaseAddress; | |
BaseAddress = FixedMtrrMemoryLimit; | |
} | |
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 == FixedMtrrMemoryLimit)) { | |
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 >= FixedMtrrMemoryLimit) { | |
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) { | |
// | |
// Fixed MTRR is modified indicating fixed MTRR should be enabled in the end of MTRR programming. | |
// | |
((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.FE = 1; | |
MtrrSetting->Fixed.Mtrr[Index] = (MtrrSetting->Fixed.Mtrr[Index] & ~ClearMasks[Index]) | OrMasks[Index]; | |
} else { | |
if (!MtrrContextValid) { | |
MtrrLibPreMtrrChange (&MtrrContext); | |
// | |
// Fixed MTRR is modified indicating fixed MTRR should be enabled in the end of MTRR programming. | |
// | |
MtrrContext.DefType.Bits.FE = 1; | |
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) { | |
// | |
// Enable MTRR unconditionally | |
// | |
((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.E = 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 | |
) | |
{ | |
BOOLEAN FixedMtrrSupported; | |
UINT32 VariableMtrrCount; | |
MSR_IA32_MTRR_DEF_TYPE_REGISTER *MtrrDefType; | |
ZeroMem (MtrrSetting, sizeof (*MtrrSetting)); | |
MtrrDefType = (MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType; | |
if (!MtrrLibIsMtrrSupported (&FixedMtrrSupported, &VariableMtrrCount)) { | |
return MtrrSetting; | |
} | |
// | |
// Get MTRR_DEF_TYPE value | |
// | |
MtrrDefType->Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE); | |
// | |
// Enabling the Fixed MTRR bit when unsupported is not allowed. | |
// | |
ASSERT (FixedMtrrSupported || (MtrrDefType->Bits.FE == 0)); | |
// | |
// Get fixed MTRRs | |
// | |
if (MtrrDefType->Bits.FE == 1) { | |
MtrrGetFixedMtrrWorker (&MtrrSetting->Fixed); | |
} | |
// | |
// Get variable MTRRs | |
// | |
MtrrGetVariableMtrrWorker ( | |
NULL, | |
VariableMtrrCount, | |
&MtrrSetting->Variables | |
); | |
return MtrrSetting; | |
} | |
/** | |
This function sets all MTRRs includes Variable and Fixed. | |
The behavior of this function is to program everything in MtrrSetting to hardware. | |
MTRRs might not be enabled because the enable bit is clear in MtrrSetting->MtrrDefType. | |
@param[in] MtrrSetting A buffer holding all MTRRs content. | |
@retval The pointer of MtrrSetting | |
**/ | |
MTRR_SETTINGS * | |
EFIAPI | |
MtrrSetAllMtrrs ( | |
IN MTRR_SETTINGS *MtrrSetting | |
) | |
{ | |
BOOLEAN FixedMtrrSupported; | |
MSR_IA32_MTRR_DEF_TYPE_REGISTER *MtrrDefType; | |
MTRR_CONTEXT MtrrContext; | |
MtrrDefType = (MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType; | |
if (!MtrrLibIsMtrrSupported (&FixedMtrrSupported, NULL)) { | |
return MtrrSetting; | |
} | |
MtrrLibPreMtrrChange (&MtrrContext); | |
// | |
// Enabling the Fixed MTRR bit when unsupported is not allowed. | |
// | |
ASSERT (FixedMtrrSupported || (MtrrDefType->Bits.FE == 0)); | |
// | |
// If the hardware supports Fixed MTRR, it is sufficient | |
// to set MTRRs regardless of whether Fixed MTRR bit is enabled. | |
// | |
if (FixedMtrrSupported) { | |
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 | |
) | |
{ | |
return MtrrLibIsMtrrSupported (NULL, NULL); | |
} | |
/** | |
This function returns a Ranges array containing the memory cache types | |
of all memory addresses. | |
@param[in] MtrrSetting MTRR setting buffer to parse. | |
@param[out] Ranges Pointer to an array of MTRR_MEMORY_RANGE. | |
@param[in,out] RangeCount Count of MTRR_MEMORY_RANGE. | |
On input, the maximum entries the Ranges can hold. | |
On output, the actual entries that the function returns. | |
@retval RETURN_INVALID_PARAMETER RangeCount is NULL. | |
@retval RETURN_INVALID_PARAMETER *RangeCount is not 0 but Ranges is NULL. | |
@retval RETURN_BUFFER_TOO_SMALL *RangeCount is too small. | |
@retval RETURN_SUCCESS Ranges are successfully returned. | |
**/ | |
RETURN_STATUS | |
EFIAPI | |
MtrrGetMemoryAttributesInMtrrSettings ( | |
IN CONST MTRR_SETTINGS *MtrrSetting OPTIONAL, | |
OUT MTRR_MEMORY_RANGE *Ranges, | |
IN OUT UINTN *RangeCount | |
) | |
{ | |
RETURN_STATUS Status; | |
MTRR_SETTINGS LocalMtrrs; | |
CONST MTRR_SETTINGS *Mtrrs; | |
MSR_IA32_MTRR_DEF_TYPE_REGISTER *MtrrDefType; | |
UINTN LocalRangeCount; | |
UINT64 MtrrValidBitsMask; | |
UINT64 MtrrValidAddressMask; | |
UINT32 VariableMtrrCount; | |
MTRR_MEMORY_RANGE RawVariableRanges[ARRAY_SIZE (Mtrrs->Variables.Mtrr)]; | |
MTRR_MEMORY_RANGE LocalRanges[ | |
ARRAY_SIZE (mMtrrLibFixedMtrrTable) * sizeof (UINT64) + 2 * ARRAY_SIZE (Mtrrs->Variables.Mtrr) + 1 | |
]; | |
if (RangeCount == NULL) { | |
return RETURN_INVALID_PARAMETER; | |
} | |
if ((*RangeCount != 0) && (Ranges == NULL)) { | |
return RETURN_INVALID_PARAMETER; | |
} | |
if (MtrrSetting != NULL) { | |
Mtrrs = MtrrSetting; | |
} else { | |
MtrrGetAllMtrrs (&LocalMtrrs); | |
Mtrrs = &LocalMtrrs; | |
} | |
MtrrDefType = (MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&Mtrrs->MtrrDefType; | |
LocalRangeCount = 1; | |
MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask); | |
LocalRanges[0].BaseAddress = 0; | |
LocalRanges[0].Length = MtrrValidBitsMask + 1; | |
if (MtrrDefType->Bits.E == 0) { | |
LocalRanges[0].Type = CacheUncacheable; | |
} else { | |
LocalRanges[0].Type = MtrrGetDefaultMemoryTypeWorker (Mtrrs); | |
VariableMtrrCount = GetVariableMtrrCountWorker (); | |
ASSERT (VariableMtrrCount <= ARRAY_SIZE (MtrrSetting->Variables.Mtrr)); | |
MtrrLibGetRawVariableRanges ( | |
&Mtrrs->Variables, | |
VariableMtrrCount, | |
MtrrValidBitsMask, | |
MtrrValidAddressMask, | |
RawVariableRanges | |
); | |
Status = MtrrLibApplyVariableMtrrs ( | |
RawVariableRanges, | |
VariableMtrrCount, | |
LocalRanges, | |
ARRAY_SIZE (LocalRanges), | |
&LocalRangeCount | |
); | |
ASSERT_RETURN_ERROR (Status); | |
if (MtrrDefType->Bits.FE == 1) { | |
MtrrLibApplyFixedMtrrs (&Mtrrs->Fixed, LocalRanges, ARRAY_SIZE (LocalRanges), &LocalRangeCount); | |
} | |
} | |
if (*RangeCount < LocalRangeCount) { | |
*RangeCount = LocalRangeCount; | |
return RETURN_BUFFER_TOO_SMALL; | |
} | |
CopyMem (Ranges, LocalRanges, LocalRangeCount * sizeof (LocalRanges[0])); | |
*RangeCount = LocalRangeCount; | |
return RETURN_SUCCESS; | |
} | |
/** | |
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_BEGIN (); | |
UINT32 Index; | |
MTRR_SETTINGS LocalMtrrs; | |
MTRR_SETTINGS *Mtrrs; | |
RETURN_STATUS Status; | |
UINTN RangeCount; | |
BOOLEAN ContainVariableMtrr; | |
MTRR_MEMORY_RANGE Ranges[ | |
ARRAY_SIZE (mMtrrLibFixedMtrrTable) * sizeof (UINT64) + 2 * ARRAY_SIZE (Mtrrs->Variables.Mtrr) + 1 | |
]; | |
if (MtrrSetting != NULL) { | |
Mtrrs = MtrrSetting; | |
} else { | |
MtrrGetAllMtrrs (&LocalMtrrs); | |
Mtrrs = &LocalMtrrs; | |
} | |
RangeCount = ARRAY_SIZE (Ranges); | |
Status = MtrrGetMemoryAttributesInMtrrSettings (Mtrrs, Ranges, &RangeCount); | |
if (RETURN_ERROR (Status)) { | |
DEBUG ((DEBUG_CACHE, "MTRR is not enabled.\n")); | |
return; | |
} | |
// | |
// 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 < ARRAY_SIZE (Mtrrs->Variables.Mtrr); 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")); | |
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 | |
)); | |
} | |
DEBUG_CODE_END (); | |
} | |
/** | |
This function prints all MTRRs for debugging. | |
**/ | |
VOID | |
EFIAPI | |
MtrrDebugPrintAllMtrrs ( | |
VOID | |
) | |
{ | |
MtrrDebugPrintAllMtrrsWorker (NULL); | |
} |