blob: cf1af29936ad85846bcaf198445386ac4c2a5bb4 [file] [log] [blame]
/** @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 - 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <Base.h>
#include <Register/Cpuid.h>
#include <Register/Msr.h>
#include <Library/MtrrLib.h>
#include <Library/BaseLib.h>
#include <Library/CpuLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#define OR_SEED 0x0101010101010101ull
#define CLEAR_SEED 0xFFFFFFFFFFFFFFFFull
#define MTRR_LIB_ASSERT_ALIGNED(B, L) ASSERT ((B & ~(L - 1)) == B);
//
// Context to save and restore when MTRRs are programmed
//
typedef struct {
UINTN Cr4;
BOOLEAN InterruptState;
} MTRR_CONTEXT;
typedef struct {
UINT64 BaseAddress;
UINT64 Length;
MTRR_MEMORY_CACHE_TYPE Type;
} MEMORY_RANGE;
//
// 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 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 <= MTRR_NUMBER_OF_VARIABLE_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 <= MTRR_NUMBER_OF_VARIABLE_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;
}
/**
This function will get the raw value in variable MTRRs
@param[out] VariableSettings A buffer to hold variable MTRRs content.
@return The VariableSettings input pointer
**/
MTRR_VARIABLE_SETTINGS*
EFIAPI
MtrrGetVariableMtrr (
OUT MTRR_VARIABLE_SETTINGS *VariableSettings
)
{
if (!IsMtrrSupported ()) {
return VariableSettings;
}
return MtrrGetVariableMtrrWorker (
NULL,
GetVariableMtrrCountWorker (),
VariableSettings
);
}
/**
Programs fixed MTRRs registers.
@param[in] Type The memory type to set.
@param[in, out] Base The base address of memory range.
@param[in, out] Length The length of memory range.
@param[in, out] LastMsrNum 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] ReturnClearMask The bits to clear in the fixed MTRR MSR.
@param[out] ReturnOrMask 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 *LastMsrNum,
OUT UINT64 *ReturnClearMask,
OUT UINT64 *ReturnOrMask
)
{
UINT32 MsrNum;
UINT32 LeftByteShift;
UINT32 RightByteShift;
UINT64 OrMask;
UINT64 ClearMask;
UINT64 SubLength;
//
// Find the fixed MTRR index to be programmed
//
for (MsrNum = *LastMsrNum + 1; MsrNum < MTRR_NUMBER_OF_FIXED_MTRR; MsrNum++) {
if ((*Base >= mMtrrLibFixedMtrrTable[MsrNum].BaseAddress) &&
(*Base <
(
mMtrrLibFixedMtrrTable[MsrNum].BaseAddress +
(8 * mMtrrLibFixedMtrrTable[MsrNum].Length)
)
)
) {
break;
}
}
if (MsrNum == MTRR_NUMBER_OF_FIXED_MTRR) {
return RETURN_UNSUPPORTED;
}
//
// Find the begin offset in fixed MTRR and calculate byte offset of left shift
//
LeftByteShift = ((UINT32)*Base - mMtrrLibFixedMtrrTable[MsrNum].BaseAddress)
/ mMtrrLibFixedMtrrTable[MsrNum].Length;
if (LeftByteShift >= 8) {
return RETURN_UNSUPPORTED;
}
//
// Find the end offset in fixed MTRR and calculate byte offset of right shift
//
SubLength = mMtrrLibFixedMtrrTable[MsrNum].Length * (8 - LeftByteShift);
if (*Length >= SubLength) {
RightByteShift = 0;
} else {
RightByteShift = 8 - LeftByteShift -
(UINT32)(*Length) / mMtrrLibFixedMtrrTable[MsrNum].Length;
if ((LeftByteShift >= 8) ||
(((UINT32)(*Length) % mMtrrLibFixedMtrrTable[MsrNum].Length) != 0)
) {
return RETURN_UNSUPPORTED;
}
//
// 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;
*LastMsrNum = MsrNum;
*ReturnClearMask = ClearMask;
*ReturnOrMask = OrMask;
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) * MTRR_NUMBER_OF_VARIABLE_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;
}
/**
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 least alignment of 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
MtrrLibLeastAlignment (
UINT64 Address,
UINT64 Alignment0
)
{
if (Address == 0) {
return Alignment0;
}
return LShiftU64 (1, (UINTN) LowBitSet64 (Address));
}
/**
Return the number of required variable MTRRs to positively cover the
specified range.
@param BaseAddress Base address of the range.
@param Length Length of the range.
@param Alignment0 Alignment of 0.
@return The number of the required variable MTRRs.
**/
UINT32
MtrrLibGetPositiveMtrrNumber (
IN UINT64 BaseAddress,
IN UINT64 Length,
IN UINT64 Alignment0
)
{
UINT64 SubLength;
UINT32 MtrrNumber;
BOOLEAN UseLeastAlignment;
UseLeastAlignment = TRUE;
SubLength = 0;
//
// Calculate the alignment of the base address.
//
for (MtrrNumber = 0; Length != 0; MtrrNumber++) {
if (UseLeastAlignment) {
SubLength = MtrrLibLeastAlignment (BaseAddress, Alignment0);
if (SubLength > Length) {
//
// Set a flag when remaining length is too small
// so that MtrrLibLeastAlignment() is not called in following loops.
//
UseLeastAlignment = FALSE;
}
}
if (!UseLeastAlignment) {
SubLength = GetPowerOfTwo64 (Length);
}
BaseAddress += SubLength;
Length -= SubLength;
}
return MtrrNumber;
}
/**
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));
}
/**
Return whether the type of the specified range can precede the specified type.
@param Ranges Memory range array holding memory type settings for all
the memory address.
@param RangeCount Count of memory ranges.
@param Type Type to check precedence.
@param SubBase Base address of the specified range.
@param SubLength Length of the specified range.
@retval TRUE The type of the specified range can precede the Type.
@retval FALSE The type of the specified range cannot precede the Type.
So the subtraction is not applicable.
**/
BOOLEAN
MtrrLibSubstractable (
IN CONST MEMORY_RANGE *Ranges,
IN UINT32 RangeCount,
IN MTRR_MEMORY_CACHE_TYPE Type,
IN UINT64 SubBase,
IN UINT64 SubLength
)
{
UINT32 Index;
UINT64 Length;
// WT > WB
// UC > *
for (Index = 0; Index < RangeCount; Index++) {
if (Ranges[Index].BaseAddress <= SubBase && SubBase < Ranges[Index].BaseAddress + Ranges[Index].Length) {
if (Ranges[Index].BaseAddress + Ranges[Index].Length >= SubBase + SubLength) {
return MtrrLibTypeLeftPrecedeRight (Ranges[Index].Type, Type);
} else {
if (!MtrrLibTypeLeftPrecedeRight (Ranges[Index].Type, Type)) {
return FALSE;
}
Length = Ranges[Index].BaseAddress + Ranges[Index].Length - SubBase;
SubBase += Length;
SubLength -= Length;
}
}
}
ASSERT (FALSE);
return FALSE;
}
/**
Return the number of required variable MTRRs to cover the specified range.
The routine considers subtraction in the both side of the range to find out
the most optimal solution (which uses the least MTRRs).
@param Ranges Array holding memory type settings of all memory
address.
@param RangeCount Count of memory ranges.
@param VariableMtrr Array holding allocated variable MTRRs.
@param VariableMtrrCount Count of allocated variable MTRRs.
@param BaseAddress Base address of the specified range.
@param Length Length of the specified range.
@param Type MTRR type of the specified range.
@param Alignment0 Alignment of 0.
@param SubLeft Return the count of left subtraction.
@param SubRight Return the count of right subtraction.
@return Number of required variable MTRRs.
**/
UINT32
MtrrLibGetMtrrNumber (
IN CONST MEMORY_RANGE *Ranges,
IN UINT32 RangeCount,
IN CONST VARIABLE_MTRR *VariableMtrr,
IN UINT32 VariableMtrrCount,
IN UINT64 BaseAddress,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE Type,
IN UINT64 Alignment0,
OUT UINT32 *SubLeft, // subtractive from BaseAddress to get more aligned address, to save MTRR
OUT UINT32 *SubRight // subtractive from BaseAddress + Length, to save MTRR
)
{
UINT64 Alignment;
UINT32 LeastLeftMtrrNumber;
UINT32 MiddleMtrrNumber;
UINT32 LeastRightMtrrNumber;
UINT32 CurrentMtrrNumber;
UINT32 SubtractiveCount;
UINT32 SubtractiveMtrrNumber;
UINT32 LeastSubtractiveMtrrNumber;
UINT64 SubtractiveBaseAddress;
UINT64 SubtractiveLength;
UINT64 BaseAlignment;
UINT32 Index;
UINT64 OriginalBaseAddress;
UINT64 OriginalLength;
*SubLeft = 0;
*SubRight = 0;
LeastSubtractiveMtrrNumber = 0;
BaseAlignment = 0;
//
// Get the optimal left subtraction solution.
//
if (BaseAddress != 0) {
OriginalBaseAddress = BaseAddress;
OriginalLength = Length;
SubtractiveBaseAddress = 0;
SubtractiveLength = 0;
//
// Get the MTRR number needed without left subtraction.
//
LeastLeftMtrrNumber = MtrrLibGetPositiveMtrrNumber (BaseAddress, Length, Alignment0);
//
// Left subtraction bit by bit, to find the optimal left subtraction solution.
//
for (SubtractiveMtrrNumber = 0, SubtractiveCount = 1; BaseAddress != 0; SubtractiveCount++) {
Alignment = MtrrLibLeastAlignment (BaseAddress, Alignment0);
//
// Check whether the memory type of [BaseAddress - Alignment, BaseAddress) can override Type.
// IA32 Manual defines the following override rules:
// WT > WB
// UC > * (any)
//
if (!MtrrLibSubstractable (Ranges, RangeCount, Type, BaseAddress - Alignment, Alignment)) {
break;
}
for (Index = 0; Index < VariableMtrrCount; Index++) {
if ((VariableMtrr[Index].BaseAddress == BaseAddress - Alignment) &&
(VariableMtrr[Index].Length == Alignment)) {
break;
}
}
if (Index == VariableMtrrCount) {
//
// Increment SubtractiveMtrrNumber when [BaseAddress - Alignment, BaseAddress) is not be planed as a MTRR
//
SubtractiveMtrrNumber++;
}
BaseAddress -= Alignment;
Length += Alignment;
CurrentMtrrNumber = SubtractiveMtrrNumber + MtrrLibGetPositiveMtrrNumber (BaseAddress, Length, Alignment0);
if (CurrentMtrrNumber <= LeastLeftMtrrNumber) {
LeastLeftMtrrNumber = CurrentMtrrNumber;
LeastSubtractiveMtrrNumber = SubtractiveMtrrNumber;
*SubLeft = SubtractiveCount;
SubtractiveBaseAddress = BaseAddress;
SubtractiveLength = Length;
}
}
//
// If left subtraction is better, subtract BaseAddress to left, and enlarge Length
//
if (*SubLeft != 0) {
BaseAddress = SubtractiveBaseAddress;
Length = SubtractiveLength;
} else {
BaseAddress = OriginalBaseAddress;
Length = OriginalLength;
}
}
//
// Increment BaseAddress greedily until (BaseAddress + Alignment) exceeds (BaseAddress + Length)
//
MiddleMtrrNumber = 0;
while (Length != 0) {
BaseAlignment = MtrrLibLeastAlignment (BaseAddress, Alignment0);
if (BaseAlignment > Length) {
break;
}
BaseAddress += BaseAlignment;
Length -= BaseAlignment;
MiddleMtrrNumber++;
}
if (Length == 0) {
return LeastSubtractiveMtrrNumber + MiddleMtrrNumber;
}
//
// Get the optimal right subtraction solution.
//
//
// Get the MTRR number needed without right subtraction.
//
LeastRightMtrrNumber = MtrrLibGetPositiveMtrrNumber (BaseAddress, Length, Alignment0);
for (SubtractiveCount = 1; Length < BaseAlignment; SubtractiveCount++) {
Alignment = MtrrLibLeastAlignment (BaseAddress + Length, Alignment0);
if (!MtrrLibSubstractable (Ranges, RangeCount, Type, BaseAddress + Length, Alignment)) {
break;
}
Length += Alignment;
//
// SubtractiveCount = Number of MTRRs used for subtraction
//
CurrentMtrrNumber = SubtractiveCount + MtrrLibGetPositiveMtrrNumber (BaseAddress, Length, Alignment0);
if (CurrentMtrrNumber <= LeastRightMtrrNumber) {
LeastRightMtrrNumber = CurrentMtrrNumber;
*SubRight = SubtractiveCount;
SubtractiveLength = Length;
}
}
return LeastSubtractiveMtrrNumber + MiddleMtrrNumber + LeastRightMtrrNumber;
}
/**
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;
VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_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 <= MTRR_NUMBER_OF_VARIABLE_MTRR);
MtrrGetVariableMtrrWorker (MtrrSetting, VariableMtrrCount, &VariableSettings);
MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
MtrrGetMemoryAttributeInVariableMtrrWorker (
&VariableSettings,
VariableMtrrCount,
MtrrValidBitsMask,
MtrrValidAddressMask,
VariableMtrr
);
//
// Go through the variable MTRR
//
MtrrType = CacheInvalid;
for (Index = 0; Index < VariableMtrrCount; Index++) {
if (VariableMtrr[Index].Valid) {
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);
}
/**
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 Index1;
UINTN VariableMtrrCount;
UINT64 Base;
UINT64 Limit;
UINT64 MtrrBase;
UINT64 MtrrLimit;
UINT64 RangeBase;
UINT64 RangeLimit;
UINT64 NoRangeBase;
UINT64 NoRangeLimit;
UINT32 RegEax;
UINTN MemoryType;
UINTN PreviousMemoryType;
BOOLEAN Found;
if (!IsMtrrSupported ()) {
return;
}
DEBUG((DEBUG_CACHE, "MTRR Settings\n"));
DEBUG((DEBUG_CACHE, "=============\n"));
if (MtrrSetting != NULL) {
Mtrrs = MtrrSetting;
} else {
MtrrGetAllMtrrs (&LocalMtrrs);
Mtrrs = &LocalMtrrs;
}
DEBUG((DEBUG_CACHE, "MTRR Default Type: %016lx\n", Mtrrs->MtrrDefType));
for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
DEBUG((DEBUG_CACHE, "Fixed MTRR[%02d] : %016lx\n", Index, Mtrrs->Fixed.Mtrr[Index]));
}
VariableMtrrCount = GetVariableMtrrCount ();
for (Index = 0; Index < VariableMtrrCount; Index++) {
DEBUG((DEBUG_CACHE, "Variable MTRR[%02d]: Base=%016lx Mask=%016lx\n",
Index,
Mtrrs->Variables.Mtrr[Index].Base,
Mtrrs->Variables.Mtrr[Index].Mask
));
}
DEBUG((DEBUG_CACHE, "\n"));
DEBUG((DEBUG_CACHE, "MTRR Ranges\n"));
DEBUG((DEBUG_CACHE, "====================================\n"));
Base = 0;
PreviousMemoryType = MTRR_CACHE_INVALID_TYPE;
for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
Base = mMtrrLibFixedMtrrTable[Index].BaseAddress;
for (Index1 = 0; Index1 < 8; Index1++) {
MemoryType = (UINTN)(RShiftU64 (Mtrrs->Fixed.Mtrr[Index], Index1 * 8) & 0xff);
if (MemoryType > CacheWriteBack) {
MemoryType = MTRR_CACHE_INVALID_TYPE;
}
if (MemoryType != PreviousMemoryType) {
if (PreviousMemoryType != MTRR_CACHE_INVALID_TYPE) {
DEBUG((DEBUG_CACHE, "%016lx\n", Base - 1));
}
PreviousMemoryType = MemoryType;
DEBUG((DEBUG_CACHE, "%a:%016lx-", mMtrrMemoryCacheTypeShortName[MemoryType], Base));
}
Base += mMtrrLibFixedMtrrTable[Index].Length;
}
}
DEBUG((DEBUG_CACHE, "%016lx\n", Base - 1));
VariableMtrrCount = GetVariableMtrrCount ();
Limit = BIT36 - 1;
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
if (RegEax >= 0x80000008) {
AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
Limit = LShiftU64 (1, RegEax & 0xff) - 1;
}
Base = BASE_1MB;
PreviousMemoryType = MTRR_CACHE_INVALID_TYPE;
do {
MemoryType = MtrrGetMemoryAttributeByAddressWorker (Mtrrs, Base);
if (MemoryType > CacheWriteBack) {
MemoryType = MTRR_CACHE_INVALID_TYPE;
}
if (MemoryType != PreviousMemoryType) {
if (PreviousMemoryType != MTRR_CACHE_INVALID_TYPE) {
DEBUG((DEBUG_CACHE, "%016lx\n", Base - 1));
}
PreviousMemoryType = MemoryType;
DEBUG((DEBUG_CACHE, "%a:%016lx-", mMtrrMemoryCacheTypeShortName[MemoryType], Base));
}
RangeBase = BASE_1MB;
NoRangeBase = BASE_1MB;
RangeLimit = Limit;
NoRangeLimit = Limit;
for (Index = 0, Found = FALSE; Index < VariableMtrrCount; Index++) {
if ((Mtrrs->Variables.Mtrr[Index].Mask & BIT11) == 0) {
//
// If mask is not valid, then do not display range
//
continue;
}
MtrrBase = (Mtrrs->Variables.Mtrr[Index].Base & (~(SIZE_4KB - 1)));
MtrrLimit = MtrrBase + ((~(Mtrrs->Variables.Mtrr[Index].Mask & (~(SIZE_4KB - 1)))) & Limit);
if (Base >= MtrrBase && Base < MtrrLimit) {
Found = TRUE;
}
if (Base >= MtrrBase && MtrrBase > RangeBase) {
RangeBase = MtrrBase;
}
if (Base > MtrrLimit && MtrrLimit > RangeBase) {
RangeBase = MtrrLimit + 1;
}
if (Base < MtrrBase && MtrrBase < RangeLimit) {
RangeLimit = MtrrBase - 1;
}
if (Base < MtrrLimit && MtrrLimit <= RangeLimit) {
RangeLimit = MtrrLimit;
}
if (Base > MtrrLimit && NoRangeBase < MtrrLimit) {
NoRangeBase = MtrrLimit + 1;
}
if (Base < MtrrBase && NoRangeLimit > MtrrBase) {
NoRangeLimit = MtrrBase - 1;
}
}
if (Found) {
Base = RangeLimit + 1;
} else {
Base = NoRangeLimit + 1;
}
} while (Base < Limit);
DEBUG((DEBUG_CACHE, "%016lx\n\n", Base - 1));
);
}
/**
This function prints all MTRRs for debugging.
**/
VOID
EFIAPI
MtrrDebugPrintAllMtrrs (
VOID
)
{
MtrrDebugPrintAllMtrrsWorker (NULL);
}
/**
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_OUT_OF_RESOURCES The new type set causes the count of memory
range exceeds capacity.
**/
RETURN_STATUS
MtrrLibSetMemoryType (
IN MEMORY_RANGE *Ranges,
IN UINT32 Capacity,
IN OUT UINT32 *Count,
IN UINT64 BaseAddress,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE Type
)
{
UINT32 Index;
UINT64 Limit;
UINT64 LengthLeft;
UINT64 LengthRight;
UINT32 StartIndex;
UINT32 EndIndex;
UINT32 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_SUCCESS;
}
//
// 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;
}
/**
Allocate one or more variable MTRR to cover the range identified by
BaseAddress and Length.
@param Ranges Memory range array holding the memory type
settings for all memory address.
@param RangeCount Count of memory ranges.
@param VariableMtrr Variable MTRR array.
@param VariableMtrrCapacity Capacity of variable MTRR array.
@param VariableMtrrCount Count of variable MTRR.
@param BaseAddress Base address of the memory range.
@param Length Length of the memory range.
@param Type MTRR type of the memory range.
@param Alignment0 Alignment of 0.
@retval RETURN_SUCCESS Variable MTRRs are allocated successfully.
@retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.
**/
RETURN_STATUS
MtrrLibSetMemoryAttributeInVariableMtrr (
IN CONST MEMORY_RANGE *Ranges,
IN UINT32 RangeCount,
IN OUT VARIABLE_MTRR *VariableMtrr,
IN UINT32 VariableMtrrCapacity,
IN OUT UINT32 *VariableMtrrCount,
IN UINT64 BaseAddress,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE Type,
IN UINT64 Alignment0
);
/**
Allocate one or more variable MTRR to cover the range identified by
BaseAddress and Length.
The routine recursively calls MtrrLibSetMemoryAttributeInVariableMtrr()
to allocate variable MTRRs when the range contains several sub-ranges
with different attributes.
@param Ranges Memory range array holding the memory type
settings for all memory address.
@param RangeCount Count of memory ranges.
@param VariableMtrr Variable MTRR array.
@param VariableMtrrCapacity Capacity of variable MTRR array.
@param VariableMtrrCount Count of variable MTRR.
@param BaseAddress Base address of the memory range.
@param Length Length of the memory range.
@param Type MTRR type of the range.
If it's CacheInvalid, the memory range may
contains several sub-ranges with different
attributes.
@param Alignment0 Alignment of 0.
@retval RETURN_SUCCESS Variable MTRRs are allocated successfully.
@retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.
**/
RETURN_STATUS
MtrrLibAddVariableMtrr (
IN CONST MEMORY_RANGE *Ranges,
IN UINT32 RangeCount,
IN OUT VARIABLE_MTRR *VariableMtrr,
IN UINT32 VariableMtrrCapacity,
IN OUT UINT32 *VariableMtrrCount,
IN PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE Type,
IN UINT64 Alignment0
)
{
RETURN_STATUS Status;
UINT32 Index;
UINT64 SubLength;
MTRR_LIB_ASSERT_ALIGNED (BaseAddress, Length);
if (Type == CacheInvalid) {
ASSERT (Ranges != NULL);
for (Index = 0; Index < RangeCount; Index++) {
if (Ranges[Index].BaseAddress <= BaseAddress && BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length) {
//
// Because the Length may not be aligned to BaseAddress, below code calls
// MtrrLibSetMemoryAttributeInVariableMtrr() instead of itself.
// MtrrLibSetMemoryAttributeInVariableMtrr() splits the range to several
// aligned ranges.
//
if (Ranges[Index].BaseAddress + Ranges[Index].Length >= BaseAddress + Length) {
return MtrrLibSetMemoryAttributeInVariableMtrr (
Ranges, RangeCount, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,
BaseAddress, Length, Ranges[Index].Type, Alignment0
);
} else {
SubLength = Ranges[Index].BaseAddress + Ranges[Index].Length - BaseAddress;
Status = MtrrLibSetMemoryAttributeInVariableMtrr (
Ranges, RangeCount, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,
BaseAddress, SubLength, Ranges[Index].Type, Alignment0
);
if (RETURN_ERROR (Status)) {
return Status;
}
BaseAddress += SubLength;
Length -= SubLength;
}
}
}
//
// Because memory ranges cover all the memory addresses, it's impossible to be here.
//
ASSERT (FALSE);
return RETURN_DEVICE_ERROR;
} else {
for (Index = 0; Index < *VariableMtrrCount; Index++) {
if (VariableMtrr[Index].BaseAddress == BaseAddress && VariableMtrr[Index].Length == Length) {
ASSERT (VariableMtrr[Index].Type == Type);
break;
}
}
if (Index == *VariableMtrrCount) {
if (*VariableMtrrCount == VariableMtrrCapacity) {
return RETURN_OUT_OF_RESOURCES;
}
VariableMtrr[Index].BaseAddress = BaseAddress;
VariableMtrr[Index].Length = Length;
VariableMtrr[Index].Type = Type;
VariableMtrr[Index].Valid = TRUE;
VariableMtrr[Index].Used = TRUE;
(*VariableMtrrCount)++;
}
return RETURN_SUCCESS;
}
}
/**
Allocate one or more variable MTRR to cover the range identified by
BaseAddress and Length.
@param Ranges Memory range array holding the memory type
settings for all memory address.
@param RangeCount Count of memory ranges.
@param VariableMtrr Variable MTRR array.
@param VariableMtrrCapacity Capacity of variable MTRR array.
@param VariableMtrrCount Count of variable MTRR.
@param BaseAddress Base address of the memory range.
@param Length Length of the memory range.
@param Type MTRR type of the memory range.
@param Alignment0 Alignment of 0.
@retval RETURN_SUCCESS Variable MTRRs are allocated successfully.
@retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.
**/
RETURN_STATUS
MtrrLibSetMemoryAttributeInVariableMtrr (
IN CONST MEMORY_RANGE *Ranges,
IN UINT32 RangeCount,
IN OUT VARIABLE_MTRR *VariableMtrr,
IN UINT32 VariableMtrrCapacity,
IN OUT UINT32 *VariableMtrrCount,
IN UINT64 BaseAddress,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE Type,
IN UINT64 Alignment0
)
{
UINT64 Alignment;
UINT32 MtrrNumber;
UINT32 SubtractiveLeft;
UINT32 SubtractiveRight;
BOOLEAN UseLeastAlignment;
Alignment = 0;
MtrrNumber = MtrrLibGetMtrrNumber (Ranges, RangeCount, VariableMtrr, *VariableMtrrCount,
BaseAddress, Length, Type, Alignment0, &SubtractiveLeft, &SubtractiveRight);
if (MtrrNumber + *VariableMtrrCount > VariableMtrrCapacity) {
return RETURN_OUT_OF_RESOURCES;
}
while (SubtractiveLeft-- != 0) {
Alignment = MtrrLibLeastAlignment (BaseAddress, Alignment0);
ASSERT (Alignment <= Length);
MtrrLibAddVariableMtrr (Ranges, RangeCount, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,
BaseAddress - Alignment, Alignment, CacheInvalid, Alignment0);
BaseAddress -= Alignment;
Length += Alignment;
}
while (Length != 0) {
Alignment = MtrrLibLeastAlignment (BaseAddress, Alignment0);
if (Alignment > Length) {
break;
}
MtrrLibAddVariableMtrr (NULL, 0, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,
BaseAddress, Alignment, Type, Alignment0);
BaseAddress += Alignment;
Length -= Alignment;
}
while (SubtractiveRight-- != 0) {
Alignment = MtrrLibLeastAlignment (BaseAddress + Length, Alignment0);
MtrrLibAddVariableMtrr (Ranges, RangeCount, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,
BaseAddress + Length, Alignment, CacheInvalid, Alignment0);
Length += Alignment;
}
UseLeastAlignment = TRUE;
while (Length != 0) {
if (UseLeastAlignment) {
Alignment = MtrrLibLeastAlignment (BaseAddress, Alignment0);
if (Alignment > Length) {
UseLeastAlignment = FALSE;
}
}
if (!UseLeastAlignment) {
Alignment = GetPowerOfTwo64 (Length);
}
MtrrLibAddVariableMtrr (NULL, 0, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,
BaseAddress, Alignment, Type, Alignment0);
BaseAddress += Alignment;
Length -= Alignment;
}
return RETURN_SUCCESS;
}
/**
Return an array of memory ranges holding memory type settings for all memory
address.
@param DefaultType The default memory type.
@param TotalLength The total length of the memory.
@param VariableMtrr The variable MTRR array.
@param VariableMtrrCount The count of variable MTRRs.
@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
MtrrLibGetMemoryTypes (
IN MTRR_MEMORY_CACHE_TYPE DefaultType,
IN UINT64 TotalLength,
IN CONST VARIABLE_MTRR *VariableMtrr,
IN UINT32 VariableMtrrCount,
OUT MEMORY_RANGE *Ranges,
IN UINT32 RangeCapacity,
OUT UINT32 *RangeCount
)
{
RETURN_STATUS Status;
UINTN Index;
//
// WT > WB
// UC > *
// UC > * (except WB, UC) > WB
//
//
// 0. Set whole range as DefaultType
//
*RangeCount = 1;
Ranges[0].BaseAddress = 0;
Ranges[0].Length = TotalLength;
Ranges[0].Type = DefaultType;
//
// 1. Set WB
//
for (Index = 0; Index < VariableMtrrCount; Index++) {
if (VariableMtrr[Index].Valid && VariableMtrr[Index].Type == CacheWriteBack) {
Status = MtrrLibSetMemoryType (
Ranges, RangeCapacity, RangeCount,
VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type
);
if (RETURN_ERROR (Status)) {
return Status;
}
}
}
//
// 2. Set other types than WB or UC
//
for (Index = 0; Index < VariableMtrrCount; Index++) {
if (VariableMtrr[Index].Valid && VariableMtrr[Index].Type != CacheWriteBack && VariableMtrr[Index].Type != CacheUncacheable) {
Status = MtrrLibSetMemoryType (
Ranges, RangeCapacity, RangeCount,
VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type
);
if (RETURN_ERROR (Status)) {
return Status;
}
}
}
//
// 3. Set UC
//
for (Index = 0; Index < VariableMtrrCount; Index++) {
if (VariableMtrr[Index].Valid && VariableMtrr[Index].Type == CacheUncacheable) {
Status = MtrrLibSetMemoryType (
Ranges, RangeCapacity, RangeCount,
VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type
);
if (RETURN_ERROR (Status)) {
return Status;
}
}
}
return RETURN_SUCCESS;
}
/**
Worker function attempts to set the attributes for a memory range.
If MtrrSetting is not NULL, set the attributes into the input MTRR
settings buffer.
If MtrrSetting is NULL, set the attributes into MTRRs registers.
@param[in, out] MtrrSetting A buffer holding all MTRRs content.
@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] Type The MTRR type 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 MTRR type is not support for the
memory resource range specified
by BaseAddress and Length.
@retval RETURN_OUT_OF_RESOURCES There are not enough system resources to
modify the attributes of the memory
resource range.
**/
RETURN_STATUS
MtrrSetMemoryAttributeWorker (
IN OUT MTRR_SETTINGS *MtrrSetting,
IN PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE Type
)
{
RETURN_STATUS Status;
UINT32 Index;
UINT32 WorkingIndex;
//
// N variable MTRRs can maximumly separate (2N + 1) Ranges, plus 1 range for [0, 1M).
//
MEMORY_RANGE Ranges[MTRR_NUMBER_OF_VARIABLE_MTRR * 2 + 2];
UINT32 RangeCount;
UINT64 MtrrValidBitsMask;
UINT64 MtrrValidAddressMask;
UINT64 Alignment0;
MTRR_CONTEXT MtrrContext;
BOOLEAN MtrrContextValid;
MTRR_MEMORY_CACHE_TYPE DefaultType;
UINT32 MsrIndex;
UINT64 ClearMask;
UINT64 OrMask;
UINT64 NewValue;
BOOLEAN FixedSettingsValid[MTRR_NUMBER_OF_FIXED_MTRR];
BOOLEAN FixedSettingsModified[MTRR_NUMBER_OF_FIXED_MTRR];
MTRR_FIXED_SETTINGS WorkingFixedSettings;
UINT32 FirmwareVariableMtrrCount;
MTRR_VARIABLE_SETTINGS *VariableSettings;
MTRR_VARIABLE_SETTINGS OriginalVariableSettings;
UINT32 OriginalVariableMtrrCount;
VARIABLE_MTRR OriginalVariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR];
UINT32 WorkingVariableMtrrCount;
VARIABLE_MTRR WorkingVariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR];
BOOLEAN VariableSettingModified[MTRR_NUMBER_OF_VARIABLE_MTRR];
UINTN FreeVariableMtrrCount;
if (Length == 0) {
return RETURN_INVALID_PARAMETER;
}
MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
if (((BaseAddress & ~MtrrValidAddressMask) != 0) || (Length & ~MtrrValidAddressMask) != 0) {
return RETURN_UNSUPPORTED;
}
OriginalVariableMtrrCount = 0;
VariableSettings = NULL;
ZeroMem (&WorkingFixedSettings, sizeof (WorkingFixedSettings));
for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
FixedSettingsValid[Index] = FALSE;
FixedSettingsModified[Index] = FALSE;
}
//
// Check if Fixed MTRR
//
if (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;
}
if (MtrrSetting != NULL) {
MtrrSetting->Fixed.Mtrr[MsrIndex] = (MtrrSetting->Fixed.Mtrr[MsrIndex] & ~ClearMask) | OrMask;
((MSR_IA32_MTRR_DEF_TYPE_REGISTER *) &MtrrSetting->MtrrDefType)->Bits.FE = 1;
} else {
if (!FixedSettingsValid[MsrIndex]) {
WorkingFixedSettings.Mtrr[MsrIndex] = AsmReadMsr64 (mMtrrLibFixedMtrrTable[MsrIndex].Msr);
FixedSettingsValid[MsrIndex] = TRUE;
}
NewValue = (WorkingFixedSettings.Mtrr[MsrIndex] & ~ClearMask) | OrMask;
if (WorkingFixedSettings.Mtrr[MsrIndex] != NewValue) {
WorkingFixedSettings.Mtrr[MsrIndex] = NewValue;
FixedSettingsModified[MsrIndex] = TRUE;
}
}
}
if (Length == 0) {
//
// A Length of 0 can only make sense for fixed MTTR ranges.
// Since we just handled the fixed MTRRs, we can skip the
// variable MTRR section.
//
goto Done;
}
}
//
// Read the default MTRR type
//
DefaultType = MtrrGetDefaultMemoryTypeWorker (MtrrSetting);
//
// Read all variable MTRRs and convert to Ranges.
//
OriginalVariableMtrrCount = GetVariableMtrrCountWorker ();
if (MtrrSetting == NULL) {
ZeroMem (&OriginalVariableSettings, sizeof (OriginalVariableSettings));
MtrrGetVariableMtrrWorker (NULL, OriginalVariableMtrrCount, &OriginalVariableSettings);
VariableSettings = &OriginalVariableSettings;
} else {
VariableSettings = &MtrrSetting->Variables;
}
MtrrGetMemoryAttributeInVariableMtrrWorker (VariableSettings, OriginalVariableMtrrCount, MtrrValidBitsMask, MtrrValidAddressMask, OriginalVariableMtrr);
Status = MtrrLibGetMemoryTypes (
DefaultType, MtrrValidBitsMask + 1, OriginalVariableMtrr, OriginalVariableMtrrCount,
Ranges, 2 * OriginalVariableMtrrCount + 1, &RangeCount
);
ASSERT (Status == RETURN_SUCCESS);
FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCountWorker ();
ASSERT (RangeCount <= 2 * FirmwareVariableMtrrCount + 1);
//
// Force [0, 1M) to UC, so that it doesn't impact left subtraction algorithm.
//
Status = MtrrLibSetMemoryType (Ranges, 2 * FirmwareVariableMtrrCount + 2, &RangeCount, 0, SIZE_1MB, CacheUncacheable);
ASSERT (Status == RETURN_SUCCESS);
//
// Apply Type to [BaseAddress, BaseAddress + Length)
//
Status = MtrrLibSetMemoryType (Ranges, 2 * FirmwareVariableMtrrCount + 2, &RangeCount, BaseAddress, Length, Type);
if (RETURN_ERROR (Status)) {
return Status;
}
Alignment0 = LShiftU64 (1, (UINTN) HighBitSet64 (MtrrValidBitsMask));
WorkingVariableMtrrCount = 0;
ZeroMem (&WorkingVariableMtrr, sizeof (WorkingVariableMtrr));
for (Index = 0; Index < RangeCount; Index++) {
if (Ranges[Index].Type != DefaultType) {
//
// Maximum allowed MTRR count is (FirmwareVariableMtrrCount + 1)
// Because potentially the range [0, 1MB) is not merged, but can be ignored because fixed MTRR covers that
//
Status = MtrrLibSetMemoryAttributeInVariableMtrr (
Ranges, RangeCount,
WorkingVariableMtrr, FirmwareVariableMtrrCount + 1, &WorkingVariableMtrrCount,
Ranges[Index].BaseAddress, Ranges[Index].Length,
Ranges[Index].Type, Alignment0
);
if (RETURN_ERROR (Status)) {
return Status;
}
}
}
//
// Remove the [0, 1MB) MTRR if it still exists (not merged with other range)
//
if (WorkingVariableMtrr[0].BaseAddress == 0 && WorkingVariableMtrr[0].Length == SIZE_1MB) {
ASSERT (WorkingVariableMtrr[0].Type == CacheUncacheable);
WorkingVariableMtrrCount--;
CopyMem (&WorkingVariableMtrr[0], &WorkingVariableMtrr[1], WorkingVariableMtrrCount * sizeof (VARIABLE_MTRR));
}
if (WorkingVariableMtrrCount > FirmwareVariableMtrrCount) {
return RETURN_OUT_OF_RESOURCES;
}
for (Index = 0; Index < OriginalVariableMtrrCount; Index++) {
VariableSettingModified[Index] = FALSE;
if (!OriginalVariableMtrr[Index].Valid) {
continue;
}
for (WorkingIndex = 0; WorkingIndex < WorkingVariableMtrrCount; WorkingIndex++) {
if (OriginalVariableMtrr[Index].BaseAddress == WorkingVariableMtrr[WorkingIndex].BaseAddress &&
OriginalVariableMtrr[Index].Length == WorkingVariableMtrr[WorkingIndex].Length &&
OriginalVariableMtrr[Index].Type == WorkingVariableMtrr[WorkingIndex].Type) {
break;
}
}
if (WorkingIndex == WorkingVariableMtrrCount) {
//
// Remove the one from OriginalVariableMtrr which is not in WorkingVariableMtrr
//
OriginalVariableMtrr[Index].Valid = FALSE;
VariableSettingModified[Index] = TRUE;
} else {
//
// Remove the one from WorkingVariableMtrr which is also in OriginalVariableMtrr
//
WorkingVariableMtrr[WorkingIndex].Valid = FALSE;
}
//
// The above two operations cause that valid MTRR only exists in either OriginalVariableMtrr or WorkingVariableMtrr.
//
}
//
// Merge remaining MTRRs from WorkingVariableMtrr to OriginalVariableMtrr
//
for (FreeVariableMtrrCount = 0, WorkingIndex = 0, Index = 0; Index < OriginalVariableMtrrCount; Index++) {
if (!OriginalVariableMtrr[Index].Valid) {
for (; WorkingIndex < WorkingVariableMtrrCount; WorkingIndex++) {
if (WorkingVariableMtrr[WorkingIndex].Valid) {
break;
}
}
if (WorkingIndex == WorkingVariableMtrrCount) {
FreeVariableMtrrCount++;
} else {
CopyMem (&OriginalVariableMtrr[Index], &WorkingVariableMtrr[WorkingIndex], sizeof (VARIABLE_MTRR));
VariableSettingModified[Index] = TRUE;
WorkingIndex++;
}
}
}
ASSERT (OriginalVariableMtrrCount - FreeVariableMtrrCount <= FirmwareVariableMtrrCount);
//
// Move MTRRs after the FirmwareVariableMtrrCount position to beginning
//
if (FirmwareVariableMtrrCount < OriginalVariableMtrrCount) {
WorkingIndex = FirmwareVariableMtrrCount;
for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {
if (!OriginalVariableMtrr[Index].Valid) {
//
// Found an empty MTRR in WorkingIndex position
//
for (; WorkingIndex < OriginalVariableMtrrCount; WorkingIndex++) {
if (OriginalVariableMtrr[WorkingIndex].Valid) {
break;
}
}
if (WorkingIndex != OriginalVariableMtrrCount) {
CopyMem (&OriginalVariableMtrr[Index], &OriginalVariableMtrr[WorkingIndex], sizeof (VARIABLE_MTRR));
VariableSettingModified[Index] = TRUE;
VariableSettingModified[WorkingIndex] = TRUE;
OriginalVariableMtrr[WorkingIndex].Valid = FALSE;
}
}
}
}
//
// Convert OriginalVariableMtrr to VariableSettings
// NOTE: MTRR from FirmwareVariableMtrr to OriginalVariableMtrr need to update as well.
//
for (Index = 0; Index < OriginalVariableMtrrCount; Index++) {
if (VariableSettingModified[Index]) {
if (OriginalVariableMtrr[Index].Valid) {
VariableSettings->Mtrr[Index].Base = (OriginalVariableMtrr[Index].BaseAddress & MtrrValidAddressMask) | (UINT8) OriginalVariableMtrr[Index].Type;
VariableSettings->Mtrr[Index].Mask = ((~(OriginalVariableMtrr[Index].Length - 1)) & MtrrValidAddressMask) | BIT11;
} else {
VariableSettings->Mtrr[Index].Base = 0;
VariableSettings->Mtrr[Index].Mask = 0;
}
}
}
Done:
if (MtrrSetting != NULL) {
((MSR_IA32_MTRR_DEF_TYPE_REGISTER *) &MtrrSetting->MtrrDefType)->Bits.E = 1;
return RETURN_SUCCESS;
}
MtrrContextValid = FALSE;
//
// Write fixed MTRRs that have been modified
//
for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
if (FixedSettingsModified[Index]) {
if (!MtrrContextValid) {
MtrrLibPreMtrrChange (&MtrrContext);
MtrrContextValid = TRUE;
}
AsmWriteMsr64 (
mMtrrLibFixedMtrrTable[Index].Msr,
WorkingFixedSettings.Mtrr[Index]
);
}
}
//
// Write variable MTRRs
// When only fixed MTRRs were changed, below loop doesn't run
// because OriginalVariableMtrrCount equals to 0.
//
for (Index = 0; Index < OriginalVariableMtrrCount; Index++) {
if (VariableSettingModified[Index]) {
if (!MtrrContextValid) {
MtrrLibPreMtrrChange (&MtrrContext);
MtrrContextValid = TRUE;
}
AsmWriteMsr64 (
MSR_IA32_MTRR_PHYSBASE0 + (Index << 1),
VariableSettings->Mtrr[Index].Base
);
AsmWriteMsr64 (
MSR_IA32_MTRR_PHYSMASK0 + (Index << 1),
VariableSettings->Mtrr[Index].Mask
);
}
}
if (MtrrContextValid) {
MtrrLibPostMtrrChange (&MtrrContext);
}
return RETURN_SUCCESS;
}
/**
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.
**/
RETURN_STATUS
EFIAPI
MtrrSetMemoryAttribute (
IN PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE Attribute
)
{
RETURN_STATUS Status;
if (!IsMtrrSupported ()) {
return RETURN_UNSUPPORTED;
}
Status = MtrrSetMemoryAttributeWorker (NULL, BaseAddress, Length, Attribute);
DEBUG ((DEBUG_CACHE, "MtrrSetMemoryAttribute() %a: [%016lx, %016lx) - %r\n",
mMtrrMemoryCacheTypeShortName[Attribute], BaseAddress, BaseAddress + Length, Status));
if (!RETURN_ERROR (Status)) {
MtrrDebugPrintAllMtrrsWorker (NULL);
}
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.
**/
RETURN_STATUS
EFIAPI
MtrrSetMemoryAttributeInMtrrSettings (
IN OUT MTRR_SETTINGS *MtrrSetting,
IN PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE Attribute
)
{
RETURN_STATUS Status;
Status = MtrrSetMemoryAttributeWorker (MtrrSetting, BaseAddress, Length, Attribute);
DEBUG((DEBUG_CACHE, "MtrrSetMemoryAttributeMtrrSettings(%p) %a: [%016lx, %016lx) - %r\n",
MtrrSetting, mMtrrMemoryCacheTypeShortName[Attribute], BaseAddress, BaseAddress + Length, Status));
if (!RETURN_ERROR (Status)) {
MtrrDebugPrintAllMtrrsWorker (MtrrSetting);
}
return Status;
}
/**
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 <= MTRR_NUMBER_OF_VARIABLE_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
);
}
}
/**
This function sets variable MTRRs
@param[in] VariableSettings A buffer to hold variable MTRRs content.
@return The pointer of VariableSettings
**/
MTRR_VARIABLE_SETTINGS*
EFIAPI
MtrrSetVariableMtrr (
IN MTRR_VARIABLE_SETTINGS *VariableSettings
)
{
MTRR_CONTEXT MtrrContext;
if (!IsMtrrSupported ()) {
return VariableSettings;
}
MtrrLibPreMtrrChange (&MtrrContext);
MtrrSetVariableMtrrWorker (VariableSettings);
MtrrLibPostMtrrChange (&MtrrContext);
MtrrDebugPrintAllMtrrs ();
return VariableSettings;
}
/**
Worker function setting fixed MTRRs
@param[in] FixedSettings A buffer to hold fixed MTRRs content.
**/
VOID
MtrrSetFixedMtrrWorker (
IN MTRR_FIXED_SETTINGS *FixedSettings
)
{
UINT32 Index;
for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
AsmWriteMsr64 (
mMtrrLibFixedMtrrTable[Index].Msr,
FixedSettings->Mtrr[Index]
);
}
}
/**
This function sets fixed MTRRs
@param[in] FixedSettings A buffer to hold fixed MTRRs content.
@retval The pointer of FixedSettings
**/
MTRR_FIXED_SETTINGS*
EFIAPI
MtrrSetFixedMtrr (
IN MTRR_FIXED_SETTINGS *FixedSettings
)
{
MTRR_CONTEXT MtrrContext;
if (!IsMtrrSupported ()) {
return FixedSettings;
}
MtrrLibPreMtrrChange (&MtrrContext);
MtrrSetFixedMtrrWorker (FixedSettings);
MtrrLibPostMtrrChange (&MtrrContext);
MtrrDebugPrintAllMtrrs ();
return FixedSettings;
}
/**
This function gets the content in all MTRRs (variable and fixed)
@param[out] MtrrSetting A buffer to hold all MTRRs content.
@retval the pointer of MtrrSetting
**/
MTRR_SETTINGS *
EFIAPI
MtrrGetAllMtrrs (
OUT MTRR_SETTINGS *MtrrSetting
)
{
if (!IsMtrrSupported ()) {
return MtrrSetting;
}
//
// Get fixed MTRRs
//
MtrrGetFixedMtrrWorker (&MtrrSetting->Fixed);
//
// Get variable MTRRs
//
MtrrGetVariableMtrrWorker (
NULL,
GetVariableMtrrCountWorker (),
&MtrrSetting->Variables
);
//
// Get MTRR_DEF_TYPE value
//
MtrrSetting->MtrrDefType = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
return MtrrSetting;
}
/**
This function sets all MTRRs (variable and fixed)
@param[in] MtrrSetting A buffer holding all MTRRs content.
@retval The pointer of MtrrSetting
**/
MTRR_SETTINGS *
EFIAPI
MtrrSetAllMtrrs (
IN MTRR_SETTINGS *MtrrSetting
)
{
MTRR_CONTEXT MtrrContext;
if (!IsMtrrSupported ()) {
return MtrrSetting;
}
MtrrLibPreMtrrChange (&MtrrContext);
//
// Set fixed MTRRs
//
MtrrSetFixedMtrrWorker (&MtrrSetting->Fixed);
//
// Set variable MTRRs
//
MtrrSetVariableMtrrWorker (&MtrrSetting->Variables);
//
// Set MTRR_DEF_TYPE value
//
AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, MtrrSetting->MtrrDefType);
MtrrLibPostMtrrChangeEnableCache (&MtrrContext);
return MtrrSetting;
}
/**
Checks if MTRR is supported.
@retval TRUE MTRR is supported.
@retval FALSE MTRR is not supported.
**/
BOOLEAN
EFIAPI
IsMtrrSupported (
VOID
)
{
CPUID_VERSION_INFO_EDX Edx;
MSR_IA32_MTRRCAP_REGISTER MtrrCap;
//
// Check CPUID(1).EDX[12] for MTRR capability
//
AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &Edx.Uint32);
if (Edx.Bits.MTRR == 0) {
return FALSE;
}
//
// Check number of variable MTRRs and fixed MTRRs existence.
// If number of variable MTRRs is zero, or fixed MTRRs do not
// exist, return false.
//
MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP);
if ((MtrrCap.Bits.VCNT == 0) || (MtrrCap.Bits.FIX == 0)) {
return FALSE;
}
return TRUE;
}