| /** @file | |
| MTRR setting library | |
| @par Note: | |
| Most of services in this library instance are suggested to be invoked by BSP only, | |
| except for MtrrSetAllMtrrs() which is used to sync BSP's MTRR setting to APs. | |
| Copyright (c) 2008 - 2017, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include <Uefi.h> | |
| #include <Register/Cpuid.h> | |
| #include <Register/Msr.h> | |
| #include <Library/MtrrLib.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/CpuLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #define OR_SEED 0x0101010101010101ull | |
| #define CLEAR_SEED 0xFFFFFFFFFFFFFFFFull | |
| #define 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) * VectorCount + (y)) | |
| #define O(x,y) ((y) * VectorCount + (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; | |
| UINT8 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].Mask = AsmReadMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1)); | |
| // | |
| // Skip to read the Base MSR when the Mask.V is not set. | |
| // | |
| if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *)&VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) { | |
| VariableSettings->Mtrr[Index].Base = AsmReadMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (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 vector Start to Stop and update | |
| the Previous of all vectors from Start to Stop is updated to reflect | |
| how the memory range is covered by MTRR. | |
| @param VectorCount The count of vectors in the graph. | |
| @param Vector Array holding all vectors. | |
| @param Weight 2-dimention array holding weights between vectors. | |
| @param Start Start vector. | |
| @param Stop Stop vector. | |
| @param IncludeOptional TRUE to count the optional weight. | |
| **/ | |
| VOID | |
| MtrrLibCalculateLeastMtrrs ( | |
| IN UINT16 VectorCount, | |
| IN MTRR_LIB_ADDRESS *Vector, | |
| 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++) { | |
| Vector[Index].Visited = FALSE; | |
| Vector[Index].Previous = VectorCount; | |
| Mandatory = Weight[M(Start,Index)]; | |
| Vector[Index].Weight = Mandatory; | |
| if (Mandatory != MAX_WEIGHT) { | |
| Optional = IncludeOptional ? Weight[O(Start, Index)] : 0; | |
| Vector[Index].Weight += Optional; | |
| ASSERT (Vector[Index].Weight >= Optional); | |
| } | |
| } | |
| MinI = Start; | |
| MinWeight = 0; | |
| while (!Vector[Stop].Visited) { | |
| // | |
| // Update the weight from the shortest vector to other unvisited vectors | |
| // | |
| for (Index = Start + 1; Index <= Stop; Index++) { | |
| if (!Vector[Index].Visited) { | |
| Mandatory = Weight[M(MinI, Index)]; | |
| if (Mandatory != MAX_WEIGHT) { | |
| Optional = IncludeOptional ? Weight[O(MinI, Index)] : 0; | |
| if (MinWeight + Mandatory + Optional <= Vector[Index].Weight) { | |
| Vector[Index].Weight = MinWeight + Mandatory + Optional; | |
| Vector[Index].Previous = MinI; // Previous is Start based. | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // Find the shortest vector from Start | |
| // | |
| MinI = VectorCount; | |
| MinWeight = MAX_WEIGHT; | |
| for (Index = Start + 1; Index <= Stop; Index++) { | |
| if (!Vector[Index].Visited && MinWeight > Vector[Index].Weight) { | |
| MinI = Index; | |
| MinWeight = Vector[Index].Weight; | |
| } | |
| } | |
| // | |
| // Mark the shortest vector from Start as visited | |
| // | |
| Vector[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 vector 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 VectorCount The count of vectors in the graph. | |
| @param Vector Array holding all vectors. | |
| @param Weight 2-dimention array holding weights between vectors. | |
| @param Start Start vector. | |
| @param Stop Stop vector. | |
| @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 VectorCount, | |
| IN MTRR_LIB_ADDRESS *Vector, | |
| 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 = Vector[Start].Address; | |
| Length = Vector[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 (Vector[SubStart].Address == HBase) { | |
| break; | |
| } | |
| } | |
| for (SubStop = SubStart; SubStop <= Stop; SubStop++) { | |
| if (Vector[SubStop].Address == HBase + HLength) { | |
| break; | |
| } | |
| } | |
| ASSERT (Vector[SubStart].Address == HBase); | |
| ASSERT (Vector[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, | |
| Vector[SubStart].Address, Vector[SubStart].Length, (MTRR_MEMORY_CACHE_TYPE) Vector[SubStart].Type | |
| ); | |
| if (RETURN_ERROR (Status)) { | |
| return Status; | |
| } | |
| SubStart++; | |
| } | |
| } | |
| } else { | |
| ASSERT (TypeCount == 3); | |
| MtrrLibCalculateLeastMtrrs (VectorCount, Vector, Weight, SubStart, SubStop, TRUE); | |
| if (Mtrrs == NULL) { | |
| Weight[M (Start, Stop)] += Vector[SubStop].Weight; | |
| } else { | |
| // When we need to collect the optimal path from SubStart to SubStop | |
| while (SubStop != SubStart) { | |
| Cur = SubStop; | |
| Pre = Vector[Cur].Previous; | |
| SubStop = Pre; | |
| if (Weight[M (Pre, Cur)] != 0) { | |
| Status = MtrrLibAppendVariableMtrr ( | |
| Mtrrs, MtrrCapacity, MtrrCount, | |
| Vector[Pre].Address, Vector[Cur].Address - Vector[Pre].Address, LowestPrecedentType | |
| ); | |
| if (RETURN_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| if (Pre != Cur - 1) { | |
| Status = MtrrLibCalculateSubtractivePath ( | |
| DefaultType, A0, | |
| Ranges, RangeCount, | |
| VectorCount, Vector, 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 *Vector; | |
| UINT8 *Weight; | |
| UINT32 VectorIndex; | |
| UINT32 VectorCount; | |
| 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 vectors. | |
| // | |
| Vector = (MTRR_LIB_ADDRESS*)Scratch; | |
| for (VectorIndex = 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 (VectorIndex < *ScratchSize / sizeof (*Vector)) { | |
| Vector[VectorIndex].Address = Base; | |
| Vector[VectorIndex].Alignment = Alignment; | |
| Vector[VectorIndex].Type = Ranges[Index].Type; | |
| Vector[VectorIndex].Length = SubLength; | |
| } | |
| Base += SubLength; | |
| Length -= SubLength; | |
| VectorIndex++; | |
| } | |
| } | |
| // | |
| // Vector[VectorIndex] = Base1, so whole vector count is (VectorIndex + 1). | |
| // | |
| VectorCount = VectorIndex + 1; | |
| DEBUG (( | |
| DEBUG_CACHE, "VectorCount (%016lx - %016lx) = %d\n", | |
| Ranges[0].BaseAddress, Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length, VectorCount | |
| )); | |
| ASSERT (VectorCount < MAX_UINT16); | |
| RequiredScratchSize = VectorCount * sizeof (*Vector) + VectorCount * VectorCount * sizeof (*Weight); | |
| if (*ScratchSize < RequiredScratchSize) { | |
| *ScratchSize = RequiredScratchSize; | |
| return RETURN_BUFFER_TOO_SMALL; | |
| } | |
| Vector[VectorCount - 1].Address = Base1; | |
| Weight = (UINT8 *) &Vector[VectorCount]; | |
| // | |
| // Set mandatory weight between any vector to max | |
| // Set optional weight and between any vector and self->self to 0 | |
| // E.g.: | |
| // 00 FF FF FF | |
| // 00 00 FF FF | |
| // 00 00 00 FF | |
| // 00 00 00 00 | |
| // | |
| for (VectorIndex = 0; VectorIndex < VectorCount; VectorIndex++) { | |
| SetMem (&Weight[M(VectorIndex, 0)], VectorIndex + 1, 0); | |
| if (VectorIndex != VectorCount - 1) { | |
| Weight[M (VectorIndex, VectorIndex + 1)] = (DefaultType == Vector[VectorIndex].Type) ? 0 : 1; | |
| SetMem (&Weight[M (VectorIndex, VectorIndex + 2)], VectorCount - VectorIndex - 2, MAX_WEIGHT); | |
| } | |
| } | |
| for (TypeCount = 2; TypeCount <= 3; TypeCount++) { | |
| for (Start = 0; Start < VectorCount; Start++) { | |
| for (Stop = Start + 2; Stop < VectorCount; Stop++) { | |
| ASSERT (Vector[Stop].Address > Vector[Start].Address); | |
| Length = Vector[Stop].Address - Vector[Start].Address; | |
| if (Length > Vector[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, Vector[Start].Address, Vector[Stop].Address - Vector[Start].Address, &Type | |
| ) == TypeCount) { | |
| // | |
| // Update the Weight[Start, Stop] using subtractive path. | |
| // | |
| MtrrLibCalculateSubtractivePath ( | |
| DefaultType, A0, | |
| Ranges, RangeCount, | |
| (UINT16)VectorCount, Vector, 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) VectorCount, Vector, Weight, 0, (UINT16) VectorCount - 1, FALSE); | |
| Stop = (UINT16) VectorCount - 1; | |
| while (Stop != 0) { | |
| Start = Vector[Stop].Previous; | |
| TypeCount = MAX_UINT8; | |
| Type = 0; | |
| if (Weight[M(Start, Stop)] != 0) { | |
| TypeCount = MtrrLibGetNumberOfTypes (Ranges, RangeCount, Vector[Start].Address, Vector[Stop].Address - Vector[Start].Address, &Type); | |
| Status = MtrrLibAppendVariableMtrr ( | |
| Mtrrs, MtrrCapacity, MtrrCount, | |
| Vector[Start].Address, Vector[Stop].Address - Vector[Start].Address, | |
| MtrrLibLowestType (Type) | |
| ); | |
| if (RETURN_ERROR (Status)) { | |
| break; | |
| } | |
| } | |
| if (Start != Stop - 1) { | |
| // | |
| // substractive path | |
| // | |
| if (TypeCount == MAX_UINT8) { | |
| TypeCount = MtrrLibGetNumberOfTypes ( | |
| Ranges, RangeCount, Vector[Start].Address, Vector[Stop].Address - Vector[Start].Address, &Type | |
| ); | |
| } | |
| Status = MtrrLibCalculateSubtractivePath ( | |
| DefaultType, A0, | |
| Ranges, RangeCount, | |
| (UINT16) VectorCount, Vector, 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] FixedSettings Fixed MTRR buffer. | |
| @param [out] Modified Flag array indicating which MTRR is modified. | |
| @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 MTRR_FIXED_SETTINGS *FixedSettings, | |
| OUT BOOLEAN *Modified, | |
| IN PHYSICAL_ADDRESS BaseAddress, | |
| IN UINT64 Length, | |
| IN MTRR_MEMORY_CACHE_TYPE Type | |
| ) | |
| { | |
| RETURN_STATUS Status; | |
| UINT32 MsrIndex; | |
| UINT64 ClearMask; | |
| UINT64 OrMask; | |
| UINT64 ClearMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)]; | |
| UINT64 OrMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)]; | |
| BOOLEAN LocalModified[ARRAY_SIZE (mMtrrLibFixedMtrrTable)]; | |
| ASSERT (BaseAddress < BASE_1MB); | |
| SetMem (LocalModified, sizeof (LocalModified), FALSE); | |
| // | |
| // (Value & ~0 | 0) still equals to (Value) | |
| // | |
| SetMem (ClearMasks, sizeof (ClearMasks), 0); | |
| SetMem (OrMasks, sizeof (OrMasks), 0); | |
| 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] = ClearMask; | |
| OrMasks[MsrIndex] = OrMask; | |
| Modified[MsrIndex] = TRUE; | |
| LocalModified[MsrIndex] = TRUE; | |
| } | |
| for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) { | |
| if (LocalModified[MsrIndex]) { | |
| FixedSettings->Mtrr[MsrIndex] = (FixedSettings->Mtrr[MsrIndex] & ~ClearMasks[MsrIndex]) | OrMasks[MsrIndex]; | |
| } | |
| } | |
| 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)]; | |
| BOOLEAN FixedSettingsModified[ARRAY_SIZE (mMtrrLibFixedMtrrTable)]; | |
| MTRR_FIXED_SETTINGS WorkingFixedSettings; | |
| MTRR_CONTEXT MtrrContext; | |
| BOOLEAN MtrrContextValid; | |
| MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask); | |
| // | |
| // TRUE indicating the accordingly Variable setting needs modificaiton in OriginalVariableMtrr. | |
| // | |
| SetMem (VariableSettingModified, ARRAY_SIZE (VariableSettingModified), FALSE); | |
| // | |
| // TRUE indicating the accordingly Fixed setting needs modification in WorkingFixedSettings. | |
| // | |
| SetMem (FixedSettingsModified, ARRAY_SIZE (FixedSettingsModified), FALSE); | |
| // | |
| // TRUE indicating the caller requests to set variable MTRRs. | |
| // | |
| Above1MbExist = FALSE; | |
| OriginalVariableMtrrCount = 0; | |
| // | |
| // 1. Validate the parameters. | |
| // | |
| for (Index = 0; Index < RangeCount; Index++) { | |
| if (Ranges[Index].Length == 0) { | |
| return RETURN_INVALID_PARAMETER; | |
| } | |
| if (((Ranges[Index].BaseAddress & ~MtrrValidAddressMask) != 0) || | |
| ((Ranges[Index].Length & ~MtrrValidAddressMask) != 0) | |
| ) { | |
| return RETURN_UNSUPPORTED; | |
| } | |
| if ((Ranges[Index].Type != CacheUncacheable) && | |
| (Ranges[Index].Type != CacheWriteCombining) && | |
| (Ranges[Index].Type != CacheWriteThrough) && | |
| (Ranges[Index].Type != CacheWriteProtected) && | |
| (Ranges[Index].Type != CacheWriteBack)) { | |
| return RETURN_INVALID_PARAMETER; | |
| } | |
| 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) { | |
| return Status; | |
| } 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)) { | |
| return Status; | |
| } | |
| // | |
| // 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) { | |
| return RETURN_OUT_OF_RESOURCES; | |
| } | |
| // | |
| // 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. | |
| // | |
| ZeroMem (WorkingFixedSettings.Mtrr, sizeof (WorkingFixedSettings.Mtrr)); | |
| for (Index = 0; Index < RangeCount; Index++) { | |
| if (Ranges[Index].BaseAddress >= BASE_1MB) { | |
| continue; | |
| } | |
| Status = MtrrLibSetBelow1MBMemoryAttribute ( | |
| &WorkingFixedSettings, FixedSettingsModified, | |
| Ranges[Index].BaseAddress, Ranges[Index].Length, Ranges[Index].Type | |
| ); | |
| if (RETURN_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| MtrrContextValid = FALSE; | |
| // | |
| // 4. Write fixed MTRRs that have been modified | |
| // | |
| for (Index = 0; Index < ARRAY_SIZE (FixedSettingsModified); Index++) { | |
| if (FixedSettingsModified[Index]) { | |
| if (MtrrSetting != NULL) { | |
| MtrrSetting->Fixed.Mtrr[Index] = WorkingFixedSettings.Mtrr[Index]; | |
| } else { | |
| if (!MtrrContextValid) { | |
| MtrrLibPreMtrrChange (&MtrrContext); | |
| MtrrContextValid = TRUE; | |
| } | |
| AsmWriteMsr64 ( | |
| mMtrrLibFixedMtrrTable[Index].Msr, | |
| WorkingFixedSettings.Mtrr[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); | |
| } | |
| } | |
| return RETURN_SUCCESS; | |
| } | |
| /** | |
| 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 | |
| ) | |
| { | |
| RETURN_STATUS Status; | |
| UINT8 Scratch[SCRATCH_BUFFER_SIZE]; | |
| UINTN ScratchSize; | |
| MTRR_MEMORY_RANGE Range; | |
| if (!IsMtrrSupported ()) { | |
| return RETURN_UNSUPPORTED; | |
| } | |
| Range.BaseAddress = BaseAddress; | |
| Range.Length = Length; | |
| Range.Type = Attribute; | |
| ScratchSize = sizeof (Scratch); | |
| Status = MtrrSetMemoryAttributesInMtrrSettings (MtrrSetting, Scratch, &ScratchSize, &Range, 1); | |
| DEBUG ((DEBUG_CACHE, "MtrrSetMemoryAttribute(MtrrSettings = %p) %a: [%016lx, %016lx) - %r\n", | |
| MtrrSetting, | |
| mMtrrMemoryCacheTypeShortName[Attribute], BaseAddress, BaseAddress + Length, Status)); | |
| if (!RETURN_ERROR (Status)) { | |
| 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 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++) { | |
| // | |
| // Mask MSR is always updated since caller might need to invalidate the MSR pair. | |
| // Base MSR is skipped when Mask.V is not set. | |
| // | |
| AsmWriteMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1), VariableSettings->Mtrr[Index].Mask); | |
| if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *)&VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) { | |
| AsmWriteMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1), VariableSettings->Mtrr[Index].Base); | |
| } | |
| } | |
| } | |
| /** | |
| 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; | |
| 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])); | |
| } | |
| for (Index = 0; Index < VariableMtrrCount; Index++) { | |
| if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *)&Mtrrs->Variables.Mtrr[Index].Mask)->Bits.V == 0) { | |
| // | |
| // If mask is not valid, then do not display range | |
| // | |
| continue; | |
| } | |
| 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")); | |
| // | |
| // Dump MTRR setting in ranges | |
| // | |
| DEBUG((DEBUG_CACHE, "MTRR 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); | |
| } |