/** @file Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include "CacheLibInternal.h" /** Search the memory cache type for specific memory from MTRR. @param[in] MemoryAddress the address of target memory @param[in] MemoryLength the length of target memory @param[in] ValidMtrrAddressMask the MTRR address mask @param[out] UsedMsrNum the used MSR number @param[out] UsedMemoryCacheType the cache type for the target memory @retval EFI_SUCCESS The memory is found in MTRR and cache type is returned @retval EFI_NOT_FOUND The memory is not found in MTRR **/ EFI_STATUS SearchForExactMtrr ( IN EFI_PHYSICAL_ADDRESS MemoryAddress, IN UINT64 MemoryLength, IN UINT64 ValidMtrrAddressMask, OUT UINT32 *UsedMsrNum, OUT EFI_MEMORY_CACHE_TYPE *MemoryCacheType ); /** Check if CacheType match current default setting. @param[in] MemoryCacheType input cache type to be checked. @retval TRUE MemoryCacheType is default MTRR setting. @retval FALSE MemoryCacheType is NOT default MTRR setting. **/ BOOLEAN IsDefaultType ( IN EFI_MEMORY_CACHE_TYPE MemoryCacheType ); /** Return MTRR alignment requirement for base address and size. @param[in] BaseAddress Base address. @param[in] Size Size. @retval Zero Aligned. @retval Non-Zero Not aligned. **/ UINT32 CheckMtrrAlignment ( IN UINT64 BaseAddress, IN UINT64 Size ); typedef struct { UINT32 Msr; UINT32 BaseAddress; UINT32 Length; } EFI_FIXED_MTRR; EFI_FIXED_MTRR mFixedMtrrTable[] = { { EFI_MSR_IA32_MTRR_FIX64K_00000, 0, 0x10000}, { EFI_MSR_IA32_MTRR_FIX16K_80000, 0x80000, 0x4000}, { EFI_MSR_IA32_MTRR_FIX16K_A0000, 0xA0000, 0x4000}, { EFI_MSR_IA32_MTRR_FIX4K_C0000, 0xC0000, 0x1000}, { EFI_MSR_IA32_MTRR_FIX4K_C8000, 0xC8000, 0x1000}, { EFI_MSR_IA32_MTRR_FIX4K_D0000, 0xD0000, 0x1000}, { EFI_MSR_IA32_MTRR_FIX4K_D8000, 0xD8000, 0x1000}, { EFI_MSR_IA32_MTRR_FIX4K_E0000, 0xE0000, 0x1000}, { EFI_MSR_IA32_MTRR_FIX4K_E8000, 0xE8000, 0x1000}, { EFI_MSR_IA32_MTRR_FIX4K_F0000, 0xF0000, 0x1000}, { EFI_MSR_IA32_MTRR_FIX4K_F8000, 0xF8000, 0x1000} }; /** Given the input, check if the number of MTRR is lesser. if positive or subtractive. @param[in] Input Length of Memory to program MTRR. @retval Zero do positive. @retval Non-Zero do subtractive. **/ INT8 CheckDirection ( IN UINT64 Input ) { return 0; } /** Disable cache and its mtrr. @param[out] OldMtrr To return the Old MTRR value **/ VOID EfiDisableCacheMtrr ( OUT UINT64 *OldMtrr ) { UINT64 TempQword; // // Disable Cache MTRR // *OldMtrr = AsmReadMsr64(EFI_MSR_CACHE_IA32_MTRR_DEF_TYPE); TempQword = (*OldMtrr) & ~B_EFI_MSR_GLOBAL_MTRR_ENABLE & ~B_EFI_MSR_FIXED_MTRR_ENABLE; AsmWriteMsr64(EFI_MSR_CACHE_IA32_MTRR_DEF_TYPE, TempQword); AsmDisableCache (); } /** Recover cache MTRR. @param[in] EnableMtrr Whether to enable the MTRR @param[in] OldMtrr The saved old MTRR value to restore when not to enable the MTRR **/ VOID EfiRecoverCacheMtrr ( IN BOOLEAN EnableMtrr, IN UINT64 OldMtrr ) { UINT64 TempQword; // // Enable Cache MTRR // if (EnableMtrr) { TempQword = AsmReadMsr64(EFI_MSR_CACHE_IA32_MTRR_DEF_TYPE); TempQword |= (UINT64)(B_EFI_MSR_GLOBAL_MTRR_ENABLE | B_EFI_MSR_FIXED_MTRR_ENABLE); } else { TempQword = OldMtrr; } AsmWriteMsr64 (EFI_MSR_CACHE_IA32_MTRR_DEF_TYPE, TempQword); AsmEnableCache (); } /** Programming MTRR according to Memory address, length, and type. @param[in] MtrrNumber the variable MTRR index number @param[in] MemoryAddress the address of target memory @param[in] MemoryLength the length of target memory @param[in] MemoryCacheType the cache type of target memory @param[in] ValidMtrrAddressMask the MTRR address mask **/ VOID EfiProgramMtrr ( IN UINT32 MtrrNumber, IN EFI_PHYSICAL_ADDRESS MemoryAddress, IN UINT64 MemoryLength, IN EFI_MEMORY_CACHE_TYPE MemoryCacheType, IN UINT64 ValidMtrrAddressMask ) { UINT64 TempQword; UINT64 OldMtrr; if (MemoryLength == 0) { return; } EfiDisableCacheMtrr (&OldMtrr); // // MTRR Physical Base // TempQword = (MemoryAddress & ValidMtrrAddressMask) | MemoryCacheType; AsmWriteMsr64 (MtrrNumber, TempQword); // // MTRR Physical Mask // TempQword = ~(MemoryLength - 1); AsmWriteMsr64 (MtrrNumber + 1, (TempQword & ValidMtrrAddressMask) | B_EFI_MSR_CACHE_MTRR_VALID); EfiRecoverCacheMtrr (TRUE, OldMtrr); } /** Calculate the maximum value which is a power of 2, but less the MemoryLength. @param[in] MemoryAddress Memory address. @param[in] MemoryLength The number to pass in. @return The maximum value which is align to power of 2 and less the MemoryLength **/ UINT64 Power2MaxMemory ( IN UINT64 MemoryAddress, IN UINT64 MemoryLength ) { UINT64 Result; if (MemoryLength == 0) { return EFI_INVALID_PARAMETER; } // // Compute initial power of 2 size to return // Result = GetPowerOfTwo64(MemoryLength); // // Special case base of 0 as all ranges are valid // if (MemoryAddress == 0) { return Result; } // // Loop till a value that can be mapped to this base address is found // while (CheckMtrrAlignment (MemoryAddress, Result) != 0) { // // Need to try the next smaller power of 2 // Result = RShiftU64 (Result, 1); } return Result; } /** Return MTRR alignment requirement for base address and size. @param[in] BaseAddress Base address. @param[in] Size Size. @retval Zero Aligned. @retval Non-Zero Not aligned. **/ UINT32 CheckMtrrAlignment ( IN UINT64 BaseAddress, IN UINT64 Size ) { UINT32 ShiftedBase; UINT32 ShiftedSize; // // Shift base and size right 12 bits to allow for larger memory sizes. The // MTRRs do not use the first 12 bits so this is safe for now. Only supports // up to 52 bits of physical address space. // ShiftedBase = (UINT32) RShiftU64 (BaseAddress, 12); ShiftedSize = (UINT32) RShiftU64 (Size, 12); // // Return the results to the caller of the MOD // return ShiftedBase % ShiftedSize; } /** Programs fixed MTRRs registers. @param[in] MemoryCacheType The memory type to set. @param[in] Base The base address of memory range. @param[in] Length The length of memory range. @retval RETURN_SUCCESS The cache type was updated successfully @retval RETURN_UNSUPPORTED The requested range or cache type was invalid for the fixed MTRRs. **/ EFI_STATUS ProgramFixedMtrr ( IN EFI_MEMORY_CACHE_TYPE MemoryCacheType, IN UINT64 *Base, IN UINT64 *Len ) { UINT32 MsrNum; UINT32 ByteShift; UINT64 TempQword; UINT64 OrMask; UINT64 ClearMask; TempQword = 0; OrMask = 0; ClearMask = 0; for (MsrNum = 0; MsrNum < V_EFI_FIXED_MTRR_NUMBER; MsrNum++) { if ((*Base >= mFixedMtrrTable[MsrNum].BaseAddress) && (*Base < (mFixedMtrrTable[MsrNum].BaseAddress + 8 * mFixedMtrrTable[MsrNum].Length))) { break; } } if (MsrNum == V_EFI_FIXED_MTRR_NUMBER ) { return EFI_DEVICE_ERROR; } // // We found the fixed MTRR to be programmed // for (ByteShift=0; ByteShift < 8; ByteShift++) { if ( *Base == (mFixedMtrrTable[MsrNum].BaseAddress + ByteShift * mFixedMtrrTable[MsrNum].Length)) { break; } } if (ByteShift == 8 ) { return EFI_DEVICE_ERROR; } for (; ((ByteShift<8) && (*Len >= mFixedMtrrTable[MsrNum].Length));ByteShift++) { OrMask |= LShiftU64((UINT64) MemoryCacheType, (UINT32) (ByteShift* 8)); ClearMask |= LShiftU64((UINT64) 0xFF, (UINT32) (ByteShift * 8)); *Len -= mFixedMtrrTable[MsrNum].Length; *Base += mFixedMtrrTable[MsrNum].Length; } TempQword = (AsmReadMsr64 (mFixedMtrrTable[MsrNum].Msr) & (~ClearMask)) | OrMask; AsmWriteMsr64 (mFixedMtrrTable[MsrNum].Msr, TempQword); return EFI_SUCCESS; } /** Check if there is a valid variable MTRR that overlaps the given range. @param[in] Start Base Address of the range to check. @param[in] End End address of the range to check. @retval TRUE Mtrr overlap. @retval FALSE Mtrr not overlap. **/ BOOLEAN CheckMtrrOverlap ( IN EFI_PHYSICAL_ADDRESS Start, IN EFI_PHYSICAL_ADDRESS End ) { return FALSE; } /** Given the memory range and cache type, programs the MTRRs. @param[in] MemoryAddress Base Address of Memory to program MTRR. @param[in] MemoryLength Length of Memory to program MTRR. @param[in] MemoryCacheType Cache Type. @retval EFI_SUCCESS Mtrr are set successfully. @retval EFI_LOAD_ERROR No empty MTRRs to use. @retval EFI_INVALID_PARAMETER The input parameter is not valid. @retval others An error occurs when setting MTTR. **/ EFI_STATUS EFIAPI SetCacheAttributes ( IN EFI_PHYSICAL_ADDRESS MemoryAddress, IN UINT64 MemoryLength, IN EFI_MEMORY_CACHE_TYPE MemoryCacheType ) { EFI_STATUS Status; UINT32 MsrNum, MsrNumEnd; UINT64 TempQword; UINT32 LastVariableMtrrForBios; UINT64 OldMtrr; UINT32 UsedMsrNum; EFI_MEMORY_CACHE_TYPE UsedMemoryCacheType; UINT64 ValidMtrrAddressMask; UINT32 Cpuid_RegEax; AsmCpuid (CPUID_EXTENDED_FUNCTION, &Cpuid_RegEax, NULL, NULL, NULL); if (Cpuid_RegEax >= CPUID_VIR_PHY_ADDRESS_SIZE) { AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &Cpuid_RegEax, NULL, NULL, NULL); ValidMtrrAddressMask = (LShiftU64((UINT64) 1, (Cpuid_RegEax & 0xFF)) - 1) & (~(UINT64)0x0FFF); } else { ValidMtrrAddressMask = (LShiftU64((UINT64) 1, 36) - 1) & (~(UINT64)0x0FFF); } // // Check for invalid parameter // if ((MemoryAddress & ~ValidMtrrAddressMask) != 0 || (MemoryLength & ~ValidMtrrAddressMask) != 0) { return EFI_INVALID_PARAMETER; } if (MemoryLength == 0) { return EFI_INVALID_PARAMETER; } switch (MemoryCacheType) { case EFI_CACHE_UNCACHEABLE: case EFI_CACHE_WRITECOMBINING: case EFI_CACHE_WRITETHROUGH: case EFI_CACHE_WRITEPROTECTED: case EFI_CACHE_WRITEBACK: break; default: return EFI_INVALID_PARAMETER; } // // Check if Fixed MTRR // if ((MemoryAddress + MemoryLength) <= (1 << 20)) { Status = EFI_SUCCESS; EfiDisableCacheMtrr (&OldMtrr); while ((MemoryLength > 0) && (Status == EFI_SUCCESS)) { Status = ProgramFixedMtrr (MemoryCacheType, &MemoryAddress, &MemoryLength); } EfiRecoverCacheMtrr (TRUE, OldMtrr); return Status; } // // Search if the range attribute has been set before // Status = SearchForExactMtrr( MemoryAddress, MemoryLength, ValidMtrrAddressMask, &UsedMsrNum, &UsedMemoryCacheType ); if (!EFI_ERROR(Status)) { // // Compare if it has the same type as current setting // if (UsedMemoryCacheType == MemoryCacheType) { return EFI_SUCCESS; } else { // // Different type // // // Check if the set type is the same as Default Type // if (IsDefaultType(MemoryCacheType)) { // // Clear the MTRR // AsmWriteMsr64(UsedMsrNum, 0); AsmWriteMsr64(UsedMsrNum + 1, 0); return EFI_SUCCESS; } else { // // Modify the MTRR type // EfiProgramMtrr(UsedMsrNum, MemoryAddress, MemoryLength, MemoryCacheType, ValidMtrrAddressMask ); return EFI_SUCCESS; } } } #if 0 // // @bug - Need to create memory map so that when checking for overlap we // can determine if an overlap exists based on all caching requests. // // Don't waste a variable MTRR if the caching attrib is same as default in MTRR_DEF_TYPE // if (MemoryCacheType == (AsmReadMsr64(EFI_MSR_CACHE_IA32_MTRR_DEF_TYPE) & B_EFI_MSR_CACHE_MEMORY_TYPE)) { if (!CheckMtrrOverlap (MemoryAddress, MemoryAddress+MemoryLength-1)) { return EFI_SUCCESS; } } #endif // // Find first unused MTRR // MsrNumEnd = EFI_MSR_CACHE_VARIABLE_MTRR_BASE + (2 * (UINT32)(AsmReadMsr64(EFI_MSR_IA32_MTRR_CAP) & B_EFI_MSR_IA32_MTRR_CAP_VARIABLE_SUPPORT)); for (MsrNum = EFI_MSR_CACHE_VARIABLE_MTRR_BASE; MsrNum < MsrNumEnd; MsrNum +=2) { if ((AsmReadMsr64(MsrNum+1) & B_EFI_MSR_CACHE_MTRR_VALID) == 0 ) { break; } } // // Reserve 1 MTRR pair for OS. // LastVariableMtrrForBios = MsrNumEnd - 1 - (EFI_CACHE_NUM_VAR_MTRR_PAIRS_FOR_OS * 2); if (MsrNum > LastVariableMtrrForBios) { return EFI_LOAD_ERROR; } // // Special case for 1 MB base address // if (MemoryAddress == BASE_1MB) { MemoryAddress = 0; } // // Program MTRRs // TempQword = MemoryLength; if (TempQword == Power2MaxMemory(MemoryAddress, TempQword)) { EfiProgramMtrr(MsrNum, MemoryAddress, MemoryLength, MemoryCacheType, ValidMtrrAddressMask ); } else { // // Fill in MTRRs with values. Direction can not be checked for this method // as we are using WB as the default cache type and only setting areas to UC. // do { // // Do boundary check so we don't go past last MTRR register // for BIOS use. Leave one MTRR pair for OS use. // if (MsrNum > LastVariableMtrrForBios) { return EFI_LOAD_ERROR; } // // Set next power of 2 region // MemoryLength = Power2MaxMemory(MemoryAddress, TempQword); EfiProgramMtrr(MsrNum, MemoryAddress, MemoryLength, MemoryCacheType, ValidMtrrAddressMask ); MemoryAddress += MemoryLength; TempQword -= MemoryLength; MsrNum += 2; } while (TempQword != 0); } return EFI_SUCCESS; } /** Reset all the MTRRs to a known state. @retval EFI_SUCCESS All MTRRs have been reset successfully. **/ EFI_STATUS EFIAPI ResetCacheAttributes ( VOID ) { UINT32 MsrNum, MsrNumEnd; UINT16 Index; UINT64 OldMtrr; UINT64 CacheType; BOOLEAN DisableCar; Index = 0; DisableCar = TRUE; // // Determine default cache type // CacheType = EFI_CACHE_UNCACHEABLE; // // Set default cache type // AsmWriteMsr64(EFI_MSR_CACHE_IA32_MTRR_DEF_TYPE, CacheType); // // Disable CAR // DisableCacheAsRam (DisableCar); EfiDisableCacheMtrr (&OldMtrr); // // Reset Fixed MTRRs // for (Index = 0; Index < V_EFI_FIXED_MTRR_NUMBER; Index++) { AsmWriteMsr64 (mFixedMtrrTable[Index].Msr, 0); } // // Reset Variable MTRRs // MsrNumEnd = EFI_MSR_CACHE_VARIABLE_MTRR_BASE + (2 * (UINT32)(AsmReadMsr64(EFI_MSR_IA32_MTRR_CAP) & B_EFI_MSR_IA32_MTRR_CAP_VARIABLE_SUPPORT)); for (MsrNum = EFI_MSR_CACHE_VARIABLE_MTRR_BASE; MsrNum < MsrNumEnd; MsrNum++) { AsmWriteMsr64 (MsrNum, 0); } // // Enable Fixed and Variable MTRRs // EfiRecoverCacheMtrr (TRUE, OldMtrr); return EFI_SUCCESS; } /** Search the memory cache type for specific memory from MTRR. @param[in] MemoryAddress the address of target memory @param[in] MemoryLength the length of target memory @param[in] ValidMtrrAddressMask the MTRR address mask @param[out] UsedMsrNum the used MSR number @param[out] UsedMemoryCacheType the cache type for the target memory @retval EFI_SUCCESS The memory is found in MTRR and cache type is returned @retval EFI_NOT_FOUND The memory is not found in MTRR **/ EFI_STATUS SearchForExactMtrr ( IN EFI_PHYSICAL_ADDRESS MemoryAddress, IN UINT64 MemoryLength, IN UINT64 ValidMtrrAddressMask, OUT UINT32 *UsedMsrNum, OUT EFI_MEMORY_CACHE_TYPE *UsedMemoryCacheType ) { UINT32 MsrNum, MsrNumEnd; UINT64 TempQword; if (MemoryLength == 0) { return EFI_INVALID_PARAMETER; } MsrNumEnd = EFI_MSR_CACHE_VARIABLE_MTRR_BASE + (2 * (UINT32)(AsmReadMsr64(EFI_MSR_IA32_MTRR_CAP) & B_EFI_MSR_IA32_MTRR_CAP_VARIABLE_SUPPORT)); for (MsrNum = EFI_MSR_CACHE_VARIABLE_MTRR_BASE; MsrNum < MsrNumEnd; MsrNum +=2) { TempQword = AsmReadMsr64(MsrNum+1); if ((TempQword & B_EFI_MSR_CACHE_MTRR_VALID) == 0) { continue; } if ((TempQword & ValidMtrrAddressMask) != ((~(MemoryLength - 1)) & ValidMtrrAddressMask)) { continue; } TempQword = AsmReadMsr64 (MsrNum); if ((TempQword & ValidMtrrAddressMask) != (MemoryAddress & ValidMtrrAddressMask)) { continue; } *UsedMemoryCacheType = (EFI_MEMORY_CACHE_TYPE)(TempQword & B_EFI_MSR_CACHE_MEMORY_TYPE); *UsedMsrNum = MsrNum; return EFI_SUCCESS; } return EFI_NOT_FOUND; } /** Check if CacheType match current default setting. @param[in] MemoryCacheType input cache type to be checked. @retval TRUE MemoryCacheType is default MTRR setting. @retval TRUE MemoryCacheType is NOT default MTRR setting. **/ BOOLEAN IsDefaultType ( IN EFI_MEMORY_CACHE_TYPE MemoryCacheType ) { if ((AsmReadMsr64(EFI_MSR_CACHE_IA32_MTRR_DEF_TYPE) & B_EFI_MSR_CACHE_MEMORY_TYPE) != MemoryCacheType) { return FALSE; } return TRUE; }