audk/UefiCpuPkg/Library/MtrrLib/MtrrLib.c

2150 lines
58 KiB
C

/** @file
MTRR setting library
@par Note:
Most of services in this library instance are suggested to be invoked by BSP only,
except for MtrrSetAllMtrrs() which is used to sync BSP's MTRR setting to APs.
Copyright (c) 2008 - 2016, 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 <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
//
// Context to save and restore when MTRRs are programmed
//
typedef struct {
UINTN Cr4;
BOOLEAN InterruptState;
} MTRR_CONTEXT;
//
// This table defines the offset, base and length of the fixed MTRRs
//
CONST FIXED_MTRR mMtrrLibFixedMtrrTable[] = {
{
MTRR_LIB_IA32_MTRR_FIX64K_00000,
0,
SIZE_64KB
},
{
MTRR_LIB_IA32_MTRR_FIX16K_80000,
0x80000,
SIZE_16KB
},
{
MTRR_LIB_IA32_MTRR_FIX16K_A0000,
0xA0000,
SIZE_16KB
},
{
MTRR_LIB_IA32_MTRR_FIX4K_C0000,
0xC0000,
SIZE_4KB
},
{
MTRR_LIB_IA32_MTRR_FIX4K_C8000,
0xC8000,
SIZE_4KB
},
{
MTRR_LIB_IA32_MTRR_FIX4K_D0000,
0xD0000,
SIZE_4KB
},
{
MTRR_LIB_IA32_MTRR_FIX4K_D8000,
0xD8000,
SIZE_4KB
},
{
MTRR_LIB_IA32_MTRR_FIX4K_E0000,
0xE0000,
SIZE_4KB
},
{
MTRR_LIB_IA32_MTRR_FIX4K_E8000,
0xE8000,
SIZE_4KB
},
{
MTRR_LIB_IA32_MTRR_FIX4K_F0000,
0xF0000,
SIZE_4KB
},
{
MTRR_LIB_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
)
{
UINT32 VariableMtrrCount;
VariableMtrrCount = (UINT32)(AsmReadMsr64 (MTRR_LIB_IA32_MTRR_CAP) & MTRR_LIB_IA32_MTRR_CAP_VCNT_MASK);
ASSERT (VariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR);
return VariableMtrrCount;
}
/**
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
)
{
if (MtrrSetting == NULL) {
return (MTRR_MEMORY_CACHE_TYPE) (AsmReadMsr64 (MTRR_LIB_IA32_MTRR_DEF_TYPE) & 0x7);
} else {
return (MTRR_MEMORY_CACHE_TYPE) (MtrrSetting->MtrrDefType & 0x7);
}
}
/**
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
PreMtrrChange (
OUT MTRR_CONTEXT *MtrrContext
)
{
//
// 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
//
AsmMsrBitFieldWrite64 (MTRR_LIB_IA32_MTRR_DEF_TYPE, 10, 11, 0);
}
/**
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
PostMtrrChangeEnableCache (
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
PostMtrrChange (
IN MTRR_CONTEXT *MtrrContext
)
{
//
// Enable Cache MTRR
//
AsmMsrBitFieldWrite64 (MTRR_LIB_IA32_MTRR_DEF_TYPE, 10, 11, 3);
PostMtrrChangeEnableCache (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 (MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1));
VariableSettings->Mtrr[Index].Mask =
AsmReadMsr64 (MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1) + 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] MemoryCacheType 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
ProgramFixedMtrr (
IN UINT64 MemoryCacheType,
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)MemoryCacheType);
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] FirmwareVariableMtrrCount The number of variable MTRRs available to firmware
@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
MtrrGetMemoryAttributeInVariableMtrrWorker (
IN MTRR_VARIABLE_SETTINGS *VariableSettings,
IN UINTN FirmwareVariableMtrrCount,
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 < FirmwareVariableMtrrCount; Index++) {
if ((VariableSettings->Mtrr[Index].Mask & MTRR_LIB_CACHE_MTRR_ENABLED) != 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
);
}
/**
Checks overlap between given memory range and MTRRs.
@param[in] FirmwareVariableMtrrCount The number of variable MTRRs available
to firmware.
@param[in] Start The start address of memory range.
@param[in] End The end address of memory range.
@param[in] VariableMtrr The array to shadow variable MTRRs content
@retval TRUE Overlap exists.
@retval FALSE No overlap.
**/
BOOLEAN
CheckMemoryAttributeOverlap (
IN UINTN FirmwareVariableMtrrCount,
IN PHYSICAL_ADDRESS Start,
IN PHYSICAL_ADDRESS End,
IN VARIABLE_MTRR *VariableMtrr
)
{
UINT32 Index;
for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {
if (
VariableMtrr[Index].Valid &&
!(
(Start > (VariableMtrr[Index].BaseAddress +
VariableMtrr[Index].Length - 1)
) ||
(End < VariableMtrr[Index].BaseAddress)
)
) {
return TRUE;
}
}
return FALSE;
}
/**
Marks a variable MTRR as non-valid.
@param[in] Index The index of the array VariableMtrr to be invalidated
@param[in] VariableMtrr The array to shadow variable MTRRs content
@param[out] UsedMtrr The number of MTRRs which has already been used
**/
VOID
InvalidateShadowMtrr (
IN UINTN Index,
IN VARIABLE_MTRR *VariableMtrr,
OUT UINT32 *UsedMtrr
)
{
VariableMtrr[Index].Valid = FALSE;
*UsedMtrr = *UsedMtrr - 1;
}
/**
Combines memory attributes.
If overlap exists between given memory range and MTRRs, try to combine them.
@param[in] FirmwareVariableMtrrCount The number of variable MTRRs
available to firmware.
@param[in] Attributes 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] VariableMtrr The array to shadow variable MTRRs content
@param[in, out] UsedMtrr The number of MTRRs which has already been used
@param[out] OverwriteExistingMtrr Returns whether an existing MTRR was used
@retval EFI_SUCCESS Memory region successfully combined.
@retval EFI_ACCESS_DENIED Memory region cannot be combined.
**/
RETURN_STATUS
CombineMemoryAttribute (
IN UINT32 FirmwareVariableMtrrCount,
IN UINT64 Attributes,
IN OUT UINT64 *Base,
IN OUT UINT64 *Length,
IN VARIABLE_MTRR *VariableMtrr,
IN OUT UINT32 *UsedMtrr,
OUT BOOLEAN *OverwriteExistingMtrr
)
{
UINT32 Index;
UINT64 CombineStart;
UINT64 CombineEnd;
UINT64 MtrrEnd;
UINT64 EndAddress;
BOOLEAN CoveredByExistingMtrr;
*OverwriteExistingMtrr = FALSE;
CoveredByExistingMtrr = FALSE;
EndAddress = *Base +*Length - 1;
for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {
MtrrEnd = VariableMtrr[Index].BaseAddress + VariableMtrr[Index].Length - 1;
if (
!VariableMtrr[Index].Valid ||
(
*Base > (MtrrEnd) ||
(EndAddress < VariableMtrr[Index].BaseAddress)
)
) {
continue;
}
//
// Combine same attribute MTRR range
//
if (Attributes == VariableMtrr[Index].Type) {
//
// if the MTRR range contain the request range, set a flag, then continue to
// invalidate any MTRR of the same request range with higher priority cache type.
//
if (VariableMtrr[Index].BaseAddress <= *Base && MtrrEnd >= EndAddress) {
CoveredByExistingMtrr = TRUE;
continue;
}
//
// invalid this MTRR, and program the combine range
//
CombineStart =
(*Base) < VariableMtrr[Index].BaseAddress ?
(*Base) :
VariableMtrr[Index].BaseAddress;
CombineEnd = EndAddress > MtrrEnd ? EndAddress : MtrrEnd;
//
// Record the MTRR usage status in VariableMtrr array.
//
InvalidateShadowMtrr (Index, VariableMtrr, UsedMtrr);
*Base = CombineStart;
*Length = CombineEnd - CombineStart + 1;
EndAddress = CombineEnd;
*OverwriteExistingMtrr = TRUE;
continue;
} else {
//
// The cache type is different, but the range is convered by one MTRR
//
if (VariableMtrr[Index].BaseAddress == *Base && MtrrEnd == EndAddress) {
InvalidateShadowMtrr (Index, VariableMtrr, UsedMtrr);
continue;
}
}
if ((Attributes== MTRR_CACHE_WRITE_THROUGH &&
VariableMtrr[Index].Type == MTRR_CACHE_WRITE_BACK) ||
(Attributes == MTRR_CACHE_WRITE_BACK &&
VariableMtrr[Index].Type == MTRR_CACHE_WRITE_THROUGH) ||
(Attributes == MTRR_CACHE_UNCACHEABLE) ||
(VariableMtrr[Index].Type == MTRR_CACHE_UNCACHEABLE)
) {
*OverwriteExistingMtrr = TRUE;
continue;
}
//
// Other type memory overlap is invalid
//
return RETURN_ACCESS_DENIED;
}
if (CoveredByExistingMtrr) {
*Length = 0;
}
return RETURN_SUCCESS;
}
/**
Calculates the maximum value which is a power of 2, but less the MemoryLength.
@param[in] MemoryLength The number to pass in.
@return The maximum value which is align to power of 2 and less the MemoryLength
**/
UINT64
Power2MaxMemory (
IN UINT64 MemoryLength
)
{
UINT64 Result;
if (RShiftU64 (MemoryLength, 32) != 0) {
Result = LShiftU64 (
(UINT64) GetPowerOfTwo32 (
(UINT32) RShiftU64 (MemoryLength, 32)
),
32
);
} else {
Result = (UINT64) GetPowerOfTwo32 ((UINT32) MemoryLength);
}
return Result;
}
/**
Determines the MTRR numbers used to program a memory range.
This function first checks the alignment of the base address.
If the alignment of the base address <= Length, cover the memory range
(BaseAddress, alignment) by a MTRR, then BaseAddress += alignment and
Length -= alignment. Repeat the step until alignment > Length.
Then this function determines which direction of programming the variable
MTRRs for the remaining length will use fewer MTRRs.
@param[in] BaseAddress Length of Memory to program MTRR
@param[in] Length Length of Memory to program MTRR
@param[in] MtrrNumber Pointer to the number of necessary MTRRs
@retval TRUE Positive direction is better.
FALSE Negative direction is better.
**/
BOOLEAN
GetMtrrNumberAndDirection (
IN UINT64 BaseAddress,
IN UINT64 Length,
IN UINTN *MtrrNumber
)
{
UINT64 TempQword;
UINT64 Alignment;
UINT32 Positive;
UINT32 Subtractive;
*MtrrNumber = 0;
if (BaseAddress != 0) {
do {
//
// Calculate the alignment of the base address.
//
Alignment = LShiftU64 (1, (UINTN)LowBitSet64 (BaseAddress));
if (Alignment > Length) {
break;
}
(*MtrrNumber)++;
BaseAddress += Alignment;
Length -= Alignment;
} while (TRUE);
if (Length == 0) {
return TRUE;
}
}
TempQword = Length;
Positive = 0;
Subtractive = 0;
do {
TempQword -= Power2MaxMemory (TempQword);
Positive++;
} while (TempQword != 0);
TempQword = Power2MaxMemory (LShiftU64 (Length, 1)) - Length;
Subtractive++;
do {
TempQword -= Power2MaxMemory (TempQword);
Subtractive++;
} while (TempQword != 0);
if (Positive <= Subtractive) {
*MtrrNumber += Positive;
return TRUE;
} else {
*MtrrNumber += Subtractive;
return FALSE;
}
}
/**
Invalid variable MTRRs according to the value in the shadow array.
This function programs MTRRs according to the values specified
in the shadow array.
@param[in, out] VariableSettings Variable MTRR settings
@param[in] VariableMtrrCount Number of variable MTRRs
@param[in, out] VariableMtrr Shadow of variable MTRR contents
**/
VOID
InvalidateMtrr (
IN OUT MTRR_VARIABLE_SETTINGS *VariableSettings,
IN UINTN VariableMtrrCount,
IN OUT VARIABLE_MTRR *VariableMtrr
)
{
UINTN Index;
for (Index = 0; Index < VariableMtrrCount; Index++) {
if (!VariableMtrr[Index].Valid && VariableMtrr[Index].Used) {
VariableSettings->Mtrr[Index].Base = 0;
VariableSettings->Mtrr[Index].Mask = 0;
VariableMtrr[Index].Used = FALSE;
}
}
}
/**
Programs variable MTRRs
This function programs variable MTRRs
@param[in, out] VariableSettings Variable MTRR settings.
@param[in] MtrrNumber Index of MTRR to program.
@param[in] BaseAddress Base address of memory region.
@param[in] Length Length of memory region.
@param[in] MemoryCacheType Memory type to set.
@param[in] MtrrValidAddressMask The valid address mask for MTRR
**/
VOID
ProgramVariableMtrr (
IN OUT MTRR_VARIABLE_SETTINGS *VariableSettings,
IN UINTN MtrrNumber,
IN PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN UINT64 MemoryCacheType,
IN UINT64 MtrrValidAddressMask
)
{
UINT64 TempQword;
//
// MTRR Physical Base
//
TempQword = (BaseAddress & MtrrValidAddressMask) | MemoryCacheType;
VariableSettings->Mtrr[MtrrNumber].Base = TempQword;
//
// MTRR Physical Mask
//
TempQword = ~(Length - 1);
VariableSettings->Mtrr[MtrrNumber].Mask = (TempQword & MtrrValidAddressMask) | MTRR_LIB_CACHE_MTRR_ENABLED;
}
/**
Converts the Memory attribute value to MTRR_MEMORY_CACHE_TYPE.
If MtrrSetting is not NULL, gets the default memory attribute from input
MTRR settings buffer.
If MtrrSetting is NULL, gets the default memory attribute from MSR.
@param[in] MtrrSetting A buffer holding all MTRRs content.
@param[in] MtrrType MTRR memory type
@return The enum item in MTRR_MEMORY_CACHE_TYPE
**/
MTRR_MEMORY_CACHE_TYPE
GetMemoryCacheTypeFromMtrrType (
IN MTRR_SETTINGS *MtrrSetting,
IN UINT64 MtrrType
)
{
switch (MtrrType) {
case MTRR_CACHE_UNCACHEABLE:
return CacheUncacheable;
case MTRR_CACHE_WRITE_COMBINING:
return CacheWriteCombining;
case MTRR_CACHE_WRITE_THROUGH:
return CacheWriteThrough;
case MTRR_CACHE_WRITE_PROTECTED:
return CacheWriteProtected;
case MTRR_CACHE_WRITE_BACK:
return CacheWriteBack;
default:
//
// MtrrType is MTRR_CACHE_INVALID_TYPE, that means
// no MTRR covers the range
//
return MtrrGetDefaultMemoryTypeWorker (MtrrSetting);
}
}
/**
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 RegEax;
UINT8 PhysicalAddressBits;
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
if (RegEax >= 0x80000008) {
AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
PhysicalAddressBits = (UINT8) RegEax;
*MtrrValidBitsMask = LShiftU64 (1, PhysicalAddressBits) - 1;
*MtrrValidAddressMask = *MtrrValidBitsMask & 0xfffffffffffff000ULL;
} else {
*MtrrValidBitsMask = MTRR_LIB_MSR_VALID_MASK;
*MtrrValidAddressMask = MTRR_LIB_CACHE_VALID_ADDRESS;
}
}
/**
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 10.11.4.1.
@param[in] MtrrType1 The first kind of Memory type
@param[in] MtrrType2 The second kind of memory type
**/
UINT64
MtrrPrecedence (
IN UINT64 MtrrType1,
IN UINT64 MtrrType2
)
{
UINT64 MtrrType;
MtrrType = MTRR_CACHE_INVALID_TYPE;
switch (MtrrType1) {
case MTRR_CACHE_UNCACHEABLE:
MtrrType = MTRR_CACHE_UNCACHEABLE;
break;
case MTRR_CACHE_WRITE_COMBINING:
if (
MtrrType2==MTRR_CACHE_WRITE_COMBINING ||
MtrrType2==MTRR_CACHE_UNCACHEABLE
) {
MtrrType = MtrrType2;
}
break;
case MTRR_CACHE_WRITE_THROUGH:
if (
MtrrType2==MTRR_CACHE_WRITE_THROUGH ||
MtrrType2==MTRR_CACHE_WRITE_BACK
) {
MtrrType = MTRR_CACHE_WRITE_THROUGH;
} else if(MtrrType2==MTRR_CACHE_UNCACHEABLE) {
MtrrType = MTRR_CACHE_UNCACHEABLE;
}
break;
case MTRR_CACHE_WRITE_PROTECTED:
if (MtrrType2 == MTRR_CACHE_WRITE_PROTECTED ||
MtrrType2 == MTRR_CACHE_UNCACHEABLE) {
MtrrType = MtrrType2;
}
break;
case MTRR_CACHE_WRITE_BACK:
if (
MtrrType2== MTRR_CACHE_UNCACHEABLE ||
MtrrType2==MTRR_CACHE_WRITE_THROUGH ||
MtrrType2== MTRR_CACHE_WRITE_BACK
) {
MtrrType = MtrrType2;
}
break;
case MTRR_CACHE_INVALID_TYPE:
MtrrType = MtrrType2;
break;
default:
break;
}
if (MtrrType2 == MTRR_CACHE_INVALID_TYPE) {
MtrrType = MtrrType1;
}
return MtrrType;
}
/**
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
)
{
UINT64 TempQword;
UINTN Index;
UINTN SubIndex;
UINT64 MtrrType;
UINT64 TempMtrrType;
MTRR_MEMORY_CACHE_TYPE CacheType;
VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR];
UINT64 MtrrValidBitsMask;
UINT64 MtrrValidAddressMask;
UINTN VariableMtrrCount;
MTRR_VARIABLE_SETTINGS VariableSettings;
//
// Check if MTRR is enabled, if not, return UC as attribute
//
if (MtrrSetting == NULL) {
TempQword = AsmReadMsr64 (MTRR_LIB_IA32_MTRR_DEF_TYPE);
} else {
TempQword = MtrrSetting->MtrrDefType;
}
MtrrType = MTRR_CACHE_INVALID_TYPE;
if ((TempQword & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
return CacheUncacheable;
}
//
// If address is less than 1M, then try to go through the fixed MTRR
//
if (Address < BASE_1MB) {
if ((TempQword & MTRR_LIB_CACHE_FIXED_MTRR_ENABLED) != 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) {
TempQword = AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr);
} else {
TempQword = MtrrSetting->Fixed.Mtrr[Index];
}
MtrrType = RShiftU64 (TempQword, SubIndex * 8) & 0xFF;
return GetMemoryCacheTypeFromMtrrType (MtrrSetting, MtrrType);
}
}
}
}
MtrrLibInitializeMtrrMask(&MtrrValidBitsMask, &MtrrValidAddressMask);
MtrrGetVariableMtrrWorker (
MtrrSetting,
GetVariableMtrrCountWorker (),
&VariableSettings
);
MtrrGetMemoryAttributeInVariableMtrrWorker (
&VariableSettings,
GetFirmwareVariableMtrrCountWorker (),
MtrrValidBitsMask,
MtrrValidAddressMask,
VariableMtrr
);
//
// Go through the variable MTRR
//
VariableMtrrCount = GetVariableMtrrCountWorker ();
ASSERT (VariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR);
for (Index = 0; Index < VariableMtrrCount; Index++) {
if (VariableMtrr[Index].Valid) {
if (Address >= VariableMtrr[Index].BaseAddress &&
Address < VariableMtrr[Index].BaseAddress+VariableMtrr[Index].Length) {
TempMtrrType = VariableMtrr[Index].Type;
MtrrType = MtrrPrecedence (MtrrType, TempMtrrType);
}
}
}
CacheType = GetMemoryCacheTypeFromMtrrType (MtrrSetting, MtrrType);
return CacheType;
}
/**
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 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);
}
/**
Worker function attempts to set the attributes for a memory range.
If MtrrSettings is not NULL, set the attributes into the input MTRR
settings buffer.
If MtrrSettings 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 region.
@param[in] Length The size in bytes of the memory region.
@param[in] Attribute The bit mask of attributes to set for the
memory region.
@retval RETURN_SUCCESS The attributes were set for the memory
region.
@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
MtrrSetMemoryAttributeWorker (
IN OUT MTRR_SETTINGS *MtrrSetting,
IN PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE Attribute
)
{
UINT64 TempQword;
RETURN_STATUS Status;
UINT64 MemoryType;
UINT64 Alignment;
BOOLEAN OverLap;
BOOLEAN Positive;
UINT32 MsrNum;
UINTN MtrrNumber;
VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR];
UINT32 UsedMtrr;
UINT64 MtrrValidBitsMask;
UINT64 MtrrValidAddressMask;
BOOLEAN OverwriteExistingMtrr;
UINT32 FirmwareVariableMtrrCount;
MTRR_CONTEXT MtrrContext;
BOOLEAN MtrrContextValid;
BOOLEAN FixedSettingsValid[MTRR_NUMBER_OF_FIXED_MTRR];
BOOLEAN FixedSettingsModified[MTRR_NUMBER_OF_FIXED_MTRR];
MTRR_FIXED_SETTINGS WorkingFixedSettings;
UINT32 VariableMtrrCount;
MTRR_VARIABLE_SETTINGS OriginalVariableSettings;
BOOLEAN ProgramVariableSettings;
MTRR_VARIABLE_SETTINGS WorkingVariableSettings;
UINT32 Index;
UINT64 ClearMask;
UINT64 OrMask;
UINT64 NewValue;
MTRR_VARIABLE_SETTINGS *VariableSettings;
MtrrContextValid = FALSE;
VariableMtrrCount = 0;
ZeroMem (&WorkingFixedSettings, sizeof (WorkingFixedSettings));
for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
FixedSettingsValid[Index] = FALSE;
FixedSettingsModified[Index] = FALSE;
}
ProgramVariableSettings = FALSE;
if (!IsMtrrSupported ()) {
Status = RETURN_UNSUPPORTED;
goto Done;
}
MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
TempQword = 0;
MemoryType = (UINT64)Attribute;
OverwriteExistingMtrr = FALSE;
//
// Check for an invalid parameter
//
if (Length == 0) {
Status = RETURN_INVALID_PARAMETER;
goto Done;
}
if (
(BaseAddress & ~MtrrValidAddressMask) != 0 ||
(Length & ~MtrrValidAddressMask) != 0
) {
Status = RETURN_UNSUPPORTED;
goto Done;
}
//
// Check if Fixed MTRR
//
Status = RETURN_SUCCESS;
if (BaseAddress < BASE_1MB) {
MsrNum = (UINT32)-1;
while ((BaseAddress < BASE_1MB) && (Length > 0) && Status == RETURN_SUCCESS) {
Status = ProgramFixedMtrr (MemoryType, &BaseAddress, &Length, &MsrNum, &ClearMask, &OrMask);
if (RETURN_ERROR (Status)) {
goto Done;
}
if (MtrrSetting != NULL) {
MtrrSetting->Fixed.Mtrr[MsrNum] = (MtrrSetting->Fixed.Mtrr[MsrNum] & ~ClearMask) | OrMask;
MtrrSetting->MtrrDefType |= MTRR_LIB_CACHE_FIXED_MTRR_ENABLED;
} else {
if (!FixedSettingsValid[MsrNum]) {
WorkingFixedSettings.Mtrr[MsrNum] = AsmReadMsr64 (mMtrrLibFixedMtrrTable[MsrNum].Msr);
FixedSettingsValid[MsrNum] = TRUE;
}
NewValue = (WorkingFixedSettings.Mtrr[MsrNum] & ~ClearMask) | OrMask;
if (WorkingFixedSettings.Mtrr[MsrNum] != NewValue) {
WorkingFixedSettings.Mtrr[MsrNum] = NewValue;
FixedSettingsModified[MsrNum] = 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;
}
}
//
// Since memory ranges below 1MB will be overridden by the fixed MTRRs,
// we can set the base to 0 to save variable MTRRs.
//
if (BaseAddress == BASE_1MB) {
BaseAddress = 0;
Length += SIZE_1MB;
}
//
// Read all variable MTRRs
//
VariableMtrrCount = GetVariableMtrrCountWorker ();
FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCountWorker ();
if (MtrrSetting != NULL) {
VariableSettings = &MtrrSetting->Variables;
} else {
MtrrGetVariableMtrrWorker (NULL, VariableMtrrCount, &OriginalVariableSettings);
CopyMem (&WorkingVariableSettings, &OriginalVariableSettings, sizeof (WorkingVariableSettings));
ProgramVariableSettings = TRUE;
VariableSettings = &WorkingVariableSettings;
}
//
// Check for overlap
//
UsedMtrr = MtrrGetMemoryAttributeInVariableMtrrWorker (
VariableSettings,
FirmwareVariableMtrrCount,
MtrrValidBitsMask,
MtrrValidAddressMask,
VariableMtrr
);
OverLap = CheckMemoryAttributeOverlap (
FirmwareVariableMtrrCount,
BaseAddress,
BaseAddress + Length - 1,
VariableMtrr
);
if (OverLap) {
Status = CombineMemoryAttribute (
FirmwareVariableMtrrCount,
MemoryType,
&BaseAddress,
&Length,
VariableMtrr,
&UsedMtrr,
&OverwriteExistingMtrr
);
if (RETURN_ERROR (Status)) {
goto Done;
}
if (Length == 0) {
//
// Combined successfully, invalidate the now-unused MTRRs
//
InvalidateMtrr (VariableSettings, VariableMtrrCount, VariableMtrr);
Status = RETURN_SUCCESS;
goto Done;
}
}
//
// The memory type is the same with the type specified by
// MTRR_LIB_IA32_MTRR_DEF_TYPE.
//
if ((!OverwriteExistingMtrr) && (Attribute == MtrrGetDefaultMemoryTypeWorker (MtrrSetting))) {
//
// Invalidate the now-unused MTRRs
//
InvalidateMtrr (VariableSettings, VariableMtrrCount, VariableMtrr);
goto Done;
}
Positive = GetMtrrNumberAndDirection (BaseAddress, Length, &MtrrNumber);
if ((UsedMtrr + MtrrNumber) > FirmwareVariableMtrrCount) {
Status = RETURN_OUT_OF_RESOURCES;
goto Done;
}
//
// Invalidate the now-unused MTRRs
//
InvalidateMtrr (VariableSettings, VariableMtrrCount, VariableMtrr);
//
// Find first unused MTRR
//
for (MsrNum = 0; MsrNum < VariableMtrrCount; MsrNum++) {
if ((VariableSettings->Mtrr[MsrNum].Mask & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
break;
}
}
if (BaseAddress != 0) {
do {
//
// Calculate the alignment of the base address.
//
Alignment = LShiftU64 (1, (UINTN)LowBitSet64 (BaseAddress));
if (Alignment > Length) {
break;
}
//
// Find unused MTRR
//
for (; MsrNum < VariableMtrrCount; MsrNum++) {
if ((VariableSettings->Mtrr[MsrNum].Mask & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
break;
}
}
ProgramVariableMtrr (
VariableSettings,
MsrNum,
BaseAddress,
Alignment,
MemoryType,
MtrrValidAddressMask
);
BaseAddress += Alignment;
Length -= Alignment;
} while (TRUE);
if (Length == 0) {
goto Done;
}
}
TempQword = Length;
if (!Positive) {
Length = Power2MaxMemory (LShiftU64 (TempQword, 1));
//
// Find unused MTRR
//
for (; MsrNum < VariableMtrrCount; MsrNum++) {
if ((VariableSettings->Mtrr[MsrNum].Mask & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
break;
}
}
ProgramVariableMtrr (
VariableSettings,
MsrNum,
BaseAddress,
Length,
MemoryType,
MtrrValidAddressMask
);
BaseAddress += Length;
TempQword = Length - TempQword;
MemoryType = MTRR_CACHE_UNCACHEABLE;
}
do {
//
// Find unused MTRR
//
for (; MsrNum < VariableMtrrCount; MsrNum++) {
if ((VariableSettings->Mtrr[MsrNum].Mask & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
break;
}
}
Length = Power2MaxMemory (TempQword);
if (!Positive) {
BaseAddress -= Length;
}
ProgramVariableMtrr (
VariableSettings,
MsrNum,
BaseAddress,
Length,
MemoryType,
MtrrValidAddressMask
);
if (Positive) {
BaseAddress += Length;
}
TempQword -= Length;
} while (TempQword > 0);
Done:
//
// Write fixed MTRRs that have been modified
//
for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
if (FixedSettingsModified[Index]) {
if (!MtrrContextValid) {
PreMtrrChange (&MtrrContext);
MtrrContextValid = TRUE;
}
AsmWriteMsr64 (
mMtrrLibFixedMtrrTable[Index].Msr,
WorkingFixedSettings.Mtrr[Index]
);
}
}
//
// Write variable MTRRs
//
if (ProgramVariableSettings) {
for (Index = 0; Index < VariableMtrrCount; Index++) {
if (WorkingVariableSettings.Mtrr[Index].Base != OriginalVariableSettings.Mtrr[Index].Base ||
WorkingVariableSettings.Mtrr[Index].Mask != OriginalVariableSettings.Mtrr[Index].Mask ) {
if (!MtrrContextValid) {
PreMtrrChange (&MtrrContext);
MtrrContextValid = TRUE;
}
AsmWriteMsr64 (
MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1),
WorkingVariableSettings.Mtrr[Index].Base
);
AsmWriteMsr64 (
MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1) + 1,
WorkingVariableSettings.Mtrr[Index].Mask
);
}
}
}
if (MtrrContextValid) {
PostMtrrChange (&MtrrContext);
}
DEBUG((DEBUG_CACHE, " Status = %r\n", Status));
if (!RETURN_ERROR (Status)) {
if (MtrrSetting != NULL) {
MtrrSetting->MtrrDefType |= MTRR_LIB_CACHE_MTRR_ENABLED;
}
MtrrDebugPrintAllMtrrsWorker (MtrrSetting);
}
return Status;
}
/**
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 region.
@param[in] Length The size in bytes of the memory region.
@param[in] Attributes The bit mask of attributes to set for the
memory region.
@retval RETURN_SUCCESS The attributes were set for the memory
region.
@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
)
{
DEBUG((DEBUG_CACHE, "MtrrSetMemoryAttribute() %a:%016lx-%016lx\n", mMtrrMemoryCacheTypeShortName[Attribute], BaseAddress, Length));
return MtrrSetMemoryAttributeWorker (
NULL,
BaseAddress,
Length,
Attribute
);
}
/**
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 region.
@param[in] Length The size in bytes of the memory region.
@param[in] Attribute The bit mask of attributes to set for the
memory region.
@retval RETURN_SUCCESS The attributes were set for the memory region.
@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
)
{
DEBUG((DEBUG_CACHE, "MtrrSetMemoryAttributeMtrrSettings(%p) %a:%016lx-%016lx\n", MtrrSetting, mMtrrMemoryCacheTypeShortName[Attribute], BaseAddress, Length));
return MtrrSetMemoryAttributeWorker (
MtrrSetting,
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 <= MTRR_NUMBER_OF_VARIABLE_MTRR);
for (Index = 0; Index < VariableMtrrCount; Index++) {
AsmWriteMsr64 (
MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1),
VariableSettings->Mtrr[Index].Base
);
AsmWriteMsr64 (
MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1) + 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;
}
PreMtrrChange (&MtrrContext);
MtrrSetVariableMtrrWorker (VariableSettings);
PostMtrrChange (&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;
}
PreMtrrChange (&MtrrContext);
MtrrSetFixedMtrrWorker (FixedSettings);
PostMtrrChange (&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 (MTRR_LIB_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;
}
PreMtrrChange (&MtrrContext);
//
// Set fixed MTRRs
//
MtrrSetFixedMtrrWorker (&MtrrSetting->Fixed);
//
// Set variable MTRRs
//
MtrrSetVariableMtrrWorker (&MtrrSetting->Variables);
//
// Set MTRR_DEF_TYPE value
//
AsmWriteMsr64 (MTRR_LIB_IA32_MTRR_DEF_TYPE, MtrrSetting->MtrrDefType);
PostMtrrChangeEnableCache (&MtrrContext);
return MtrrSetting;
}
/**
Checks if MTRR is supported.
@retval TRUE MTRR is supported.
@retval FALSE MTRR is not supported.
**/
BOOLEAN
EFIAPI
IsMtrrSupported (
VOID
)
{
UINT32 RegEdx;
UINT64 MtrrCap;
//
// Check CPUID(1).EDX[12] for MTRR capability
//
AsmCpuid (1, NULL, NULL, NULL, &RegEdx);
if (BitFieldRead32 (RegEdx, 12, 12) == 0) {
return FALSE;
}
//
// Check IA32_MTRRCAP.[0..7] for number of variable MTRRs and IA32_MTRRCAP[8] for
// fixed MTRRs existence. If number of variable MTRRs is zero, or fixed MTRRs do not
// exist, return false.
//
MtrrCap = AsmReadMsr64 (MTRR_LIB_IA32_MTRR_CAP);
if ((BitFieldRead64 (MtrrCap, 0, 7) == 0) || (BitFieldRead64 (MtrrCap, 8, 8) == 0)) {
return FALSE;
}
return TRUE;
}