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