/** Returns the firmware usable variable MTRR count for the CPU. @return Firmware usable variable MTRR count **/ UINT32 GetFirmwareVariableMtrrCount ( VOID ) { UINT32 VariableMtrrCount; VariableMtrrCount = GetVariableMtrrCount (); if (VariableMtrrCount < RESERVED_FIRMWARE_VARIABLE_MTRR_NUMBER) { return 0; } return VariableMtrrCount - RESERVED_FIRMWARE_VARIABLE_MTRR_NUMBER; }
/** Get the attribute of variable MTRRs. This function shadows the content of variable MTRRs into an internal array: VariableMtrr. @param MtrrValidBitsMask The mask for the valid bit of the MTRR @param MtrrValidAddressMask The valid address mask for MTRR @param VariableMtrr The array to shadow variable MTRRs content @return The return value of this paramter indicates the number of MTRRs which has been used. **/ UINT32 EFIAPI MtrrGetMemoryAttributeInVariableMtrr ( IN UINT64 MtrrValidBitsMask, IN UINT64 MtrrValidAddressMask, OUT VARIABLE_MTRR *VariableMtrr ) { UINTN Index; UINT32 MsrNum; UINT32 UsedMtrr; UINT32 FirmwareVariableMtrrCount; UINT32 VariableMtrrEnd; if (!IsMtrrSupported ()) { return 0; } FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount (); VariableMtrrEnd = MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (2 * GetVariableMtrrCount ()) - 1; ZeroMem (VariableMtrr, sizeof (VARIABLE_MTRR) * MTRR_NUMBER_OF_VARIABLE_MTRR); UsedMtrr = 0; for (MsrNum = MTRR_LIB_IA32_VARIABLE_MTRR_BASE, Index = 0; ( (MsrNum < VariableMtrrEnd) && (Index < FirmwareVariableMtrrCount) ); MsrNum += 2 ) { if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) != 0) { VariableMtrr[Index].Msr = MsrNum; VariableMtrr[Index].BaseAddress = (AsmReadMsr64 (MsrNum) & MtrrValidAddressMask); VariableMtrr[Index].Length = ((~(AsmReadMsr64 (MsrNum + 1) & MtrrValidAddressMask) ) & MtrrValidBitsMask ) + 1; VariableMtrr[Index].Type = (AsmReadMsr64 (MsrNum) & 0x0ff); VariableMtrr[Index].Valid = TRUE; VariableMtrr[Index].Used = TRUE; UsedMtrr = UsedMtrr + 1; Index++; } } return UsedMtrr; }
/** Invalid variable MTRRs according to the value in the shadow array. This function programs MTRRs according to the values specified in the shadow array. @param VariableMtrr The array to shadow variable MTRRs content **/ VOID InvalidateMtrr ( IN VARIABLE_MTRR *VariableMtrr ) { UINTN Index; UINTN VariableMtrrCount; MTRR_CONTEXT MtrrContext; PreMtrrChange (&MtrrContext); Index = 0; VariableMtrrCount = GetVariableMtrrCount (); while (Index < VariableMtrrCount) { if (!VariableMtrr[Index].Valid && VariableMtrr[Index].Used) { AsmWriteMsr64 (VariableMtrr[Index].Msr, 0); AsmWriteMsr64 (VariableMtrr[Index].Msr + 1, 0); VariableMtrr[Index].Used = FALSE; } Index ++; } PostMtrrChange (&MtrrContext); }
/** Worker function setting variable MTRRs @param VariableSettings A buffer to hold variable MTRRs content. **/ VOID MtrrSetVariableMtrrWorker ( IN MTRR_VARIABLE_SETTINGS *VariableSettings ) { UINT32 Index; UINT32 VariableMtrrCount; VariableMtrrCount = GetVariableMtrrCount (); ASSERT (VariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR); for (Index = 0; Index < VariableMtrrCount; Index++) { AsmWriteMsr64 ( MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1), VariableSettings->Mtrr[Index].Base ); AsmWriteMsr64 ( MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1) + 1, VariableSettings->Mtrr[Index].Mask ); } }
/** Invalid variable MTRRs according to the value in the shadow array. This function programs MTRRs according to the values specified in the shadow array. @param VariableMtrr The array to shadow variable MTRRs content **/ STATIC VOID InvalidateMtrr ( IN VARIABLE_MTRR *VariableMtrr ) { UINTN Index; UINTN Cr4; UINTN VariableMtrrCount; Cr4 = PreMtrrChange (); Index = 0; VariableMtrrCount = GetVariableMtrrCount (); while (Index < VariableMtrrCount) { if (VariableMtrr[Index].Valid == FALSE && VariableMtrr[Index].Used == TRUE ) { AsmWriteMsr64 (VariableMtrr[Index].Msr, 0); AsmWriteMsr64 (VariableMtrr[Index].Msr + 1, 0); VariableMtrr[Index].Used = FALSE; } Index ++; } PostMtrrChange (Cr4); }
EFIAPI MtrrGetVariableMtrr ( OUT MTRR_VARIABLE_SETTINGS *VariableSettings ) { UINT32 Index; UINT32 VariableMtrrCount; if (!IsMtrrSupported ()) { return VariableSettings; } VariableMtrrCount = GetVariableMtrrCount (); ASSERT (VariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR); for (Index = 0; Index < VariableMtrrCount; Index++) { VariableSettings->Mtrr[Index].Base = AsmReadMsr64 (MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1)); VariableSettings->Mtrr[Index].Mask = AsmReadMsr64 (MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (Index << 1) + 1); } return VariableSettings; }
/** This function attempts to set the attributes for a memory range. @param BaseAddress The physical address that is the start address of a memory region. @param Length The size in bytes of the memory region. @param Attributes The bit mask of attributes to set for the memory region. @retval RETURN_SUCCESS The attributes were set for the memory region. @retval RETURN_INVALID_PARAMETER Length is zero. @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory resource range specified by BaseAddress and Length. @retval RETURN_UNSUPPORTED The bit mask of attributes is not support for the memory resource range specified by BaseAddress and Length. @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by BaseAddress and Length cannot be modified. @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of the memory resource range. **/ RETURN_STATUS EFIAPI MtrrSetMemoryAttribute ( IN PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN MTRR_MEMORY_CACHE_TYPE Attribute ) { UINT64 TempQword; RETURN_STATUS Status; UINT64 MemoryType; UINT64 Alignment; BOOLEAN OverLap; BOOLEAN Positive; UINT32 MsrNum; UINTN MtrrNumber; VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR]; UINT32 UsedMtrr; UINT64 MtrrValidBitsMask; UINT64 MtrrValidAddressMask; BOOLEAN OverwriteExistingMtrr; UINT32 FirmwareVariableMtrrCount; UINT32 VariableMtrrEnd; MTRR_CONTEXT MtrrContext; DEBUG((DEBUG_CACHE, "MtrrSetMemoryAttribute() %a:%016lx-%016lx\n", mMtrrMemoryCacheTypeShortName[Attribute], BaseAddress, Length)); if (!IsMtrrSupported ()) { Status = RETURN_UNSUPPORTED; goto Done; } FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount (); VariableMtrrEnd = MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (2 * GetVariableMtrrCount ()) - 1; MtrrLibInitializeMtrrMask(&MtrrValidBitsMask, &MtrrValidAddressMask); TempQword = 0; MemoryType = (UINT64)Attribute; OverwriteExistingMtrr = FALSE; // // Check for an invalid parameter // if (Length == 0) { Status = RETURN_INVALID_PARAMETER; goto Done; } if ( (BaseAddress & ~MtrrValidAddressMask) != 0 || (Length & ~MtrrValidAddressMask) != 0 ) { Status = RETURN_UNSUPPORTED; goto Done; } // // Check if Fixed MTRR // Status = RETURN_SUCCESS; while ((BaseAddress < BASE_1MB) && (Length > 0) && Status == RETURN_SUCCESS) { PreMtrrChange (&MtrrContext); Status = ProgramFixedMtrr (MemoryType, &BaseAddress, &Length); PostMtrrChange (&MtrrContext); if (RETURN_ERROR (Status)) { goto Done; } } if (Length == 0) { // // A Length of 0 can only make sense for fixed MTTR ranges. // Since we just handled the fixed MTRRs, we can skip the // variable MTRR section. // goto Done; } // // Since memory ranges below 1MB will be overridden by the fixed MTRRs, // we can set the base to 0 to save variable MTRRs. // if (BaseAddress == BASE_1MB) { BaseAddress = 0; Length += SIZE_1MB; } // // Check for overlap // UsedMtrr = MtrrGetMemoryAttributeInVariableMtrr (MtrrValidBitsMask, MtrrValidAddressMask, VariableMtrr); OverLap = CheckMemoryAttributeOverlap (BaseAddress, BaseAddress + Length - 1, VariableMtrr); if (OverLap) { Status = CombineMemoryAttribute (MemoryType, &BaseAddress, &Length, VariableMtrr, &UsedMtrr, &OverwriteExistingMtrr); if (RETURN_ERROR (Status)) { goto Done; } if (Length == 0) { // // Combined successfully, invalidate the now-unused MTRRs // InvalidateMtrr(VariableMtrr); Status = RETURN_SUCCESS; goto Done; } } // // The memory type is the same with the type specified by // MTRR_LIB_IA32_MTRR_DEF_TYPE. // if ((!OverwriteExistingMtrr) && (Attribute == MtrrGetDefaultMemoryType ())) { // // Invalidate the now-unused MTRRs // InvalidateMtrr(VariableMtrr); goto Done; } Positive = GetMtrrNumberAndDirection (BaseAddress, Length, &MtrrNumber); if ((UsedMtrr + MtrrNumber) > FirmwareVariableMtrrCount) { Status = RETURN_OUT_OF_RESOURCES; goto Done; } // // Invalidate the now-unused MTRRs // InvalidateMtrr(VariableMtrr); // // Find first unused MTRR // for (MsrNum = MTRR_LIB_IA32_VARIABLE_MTRR_BASE; MsrNum < VariableMtrrEnd; MsrNum += 2 ) { if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) { break; } } if (BaseAddress != 0) { do { // // Calculate the alignment of the base address. // Alignment = LShiftU64 (1, (UINTN)LowBitSet64 (BaseAddress)); if (Alignment > Length) { break; } // // Find unused MTRR // for (; MsrNum < VariableMtrrEnd; MsrNum += 2) { if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) { break; } } ProgramVariableMtrr ( MsrNum, BaseAddress, Alignment, MemoryType, MtrrValidAddressMask ); BaseAddress += Alignment; Length -= Alignment; } while (TRUE); if (Length == 0) { goto Done; } } TempQword = Length; if (!Positive) { Length = Power2MaxMemory (LShiftU64 (TempQword, 1)); // // Find unused MTRR // for (; MsrNum < VariableMtrrEnd; MsrNum += 2) { if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) { break; } } ProgramVariableMtrr ( MsrNum, BaseAddress, Length, MemoryType, MtrrValidAddressMask ); BaseAddress += Length; TempQword = Length - TempQword; MemoryType = MTRR_CACHE_UNCACHEABLE; } do { // // Find unused MTRR // for (; MsrNum < VariableMtrrEnd; MsrNum += 2) { if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) { break; } } Length = Power2MaxMemory (TempQword); if (!Positive) { BaseAddress -= Length; } ProgramVariableMtrr ( MsrNum, BaseAddress, Length, MemoryType, MtrrValidAddressMask ); if (Positive) { BaseAddress += Length; } TempQword -= Length; } while (TempQword > 0); Done: DEBUG((DEBUG_CACHE, " Status = %r\n", Status)); if (!RETURN_ERROR (Status)) { MtrrDebugPrintAllMtrrs (); } return Status; }
/** This function will get the memory cache type of the specific address. This function is mainly for debug purpose. @param Address The specific address @return Memory cache type of the sepcific address **/ MTRR_MEMORY_CACHE_TYPE EFIAPI MtrrGetMemoryAttribute ( IN PHYSICAL_ADDRESS Address ) { UINT64 TempQword; UINTN Index; UINTN SubIndex; UINT64 MtrrType; UINT64 TempMtrrType; MTRR_MEMORY_CACHE_TYPE CacheType; VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR]; UINT64 MtrrValidBitsMask; UINT64 MtrrValidAddressMask; UINTN VariableMtrrCount; if (!IsMtrrSupported ()) { return CacheUncacheable; } // // Check if MTRR is enabled, if not, return UC as attribute // TempQword = AsmReadMsr64 (MTRR_LIB_IA32_MTRR_DEF_TYPE); MtrrType = MTRR_CACHE_INVALID_TYPE; if ((TempQword & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) { return CacheUncacheable; } // // If address is less than 1M, then try to go through the fixed MTRR // if (Address < BASE_1MB) { if ((TempQword & MTRR_LIB_CACHE_FIXED_MTRR_ENABLED) != 0) { // // Go through the fixed MTRR // for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) { if (Address >= mMtrrLibFixedMtrrTable[Index].BaseAddress && Address < ( mMtrrLibFixedMtrrTable[Index].BaseAddress + (mMtrrLibFixedMtrrTable[Index].Length * 8) ) ) { SubIndex = ((UINTN)Address - mMtrrLibFixedMtrrTable[Index].BaseAddress) / mMtrrLibFixedMtrrTable[Index].Length; TempQword = AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr); MtrrType = RShiftU64 (TempQword, SubIndex * 8) & 0xFF; return GetMemoryCacheTypeFromMtrrType (MtrrType); } } } } MtrrLibInitializeMtrrMask(&MtrrValidBitsMask, &MtrrValidAddressMask); MtrrGetMemoryAttributeInVariableMtrr( MtrrValidBitsMask, MtrrValidAddressMask, VariableMtrr ); // // Go through the variable MTRR // VariableMtrrCount = GetVariableMtrrCount (); ASSERT (VariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR); for (Index = 0; Index < VariableMtrrCount; Index++) { if (VariableMtrr[Index].Valid) { if (Address >= VariableMtrr[Index].BaseAddress && Address < VariableMtrr[Index].BaseAddress+VariableMtrr[Index].Length) { TempMtrrType = VariableMtrr[Index].Type; MtrrType = MtrrPrecedence (MtrrType, TempMtrrType); } } } CacheType = GetMemoryCacheTypeFromMtrrType (MtrrType); return CacheType; }
/** This function attempts to set the attributes for a memory range. @param BaseAddress The physical address that is the start address of a memory region. @param Length The size in bytes of the memory region. @param Attributes The bit mask of attributes to set for the memory region. @retval RETURN_SUCCESS The attributes were set for the memory region. @retval RETURN_INVALID_PARAMETER Length is zero. @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory resource range specified by BaseAddress and Length. @retval RETURN_UNSUPPORTED The bit mask of attributes is not support for the memory resource range specified by BaseAddress and Length. @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by BaseAddress and Length cannot be modified. @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of the memory resource range. **/ RETURN_STATUS EFIAPI MtrrSetMemoryAttribute ( IN PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN MTRR_MEMORY_CACHE_TYPE Attribute ) { UINT64 TempQword; RETURN_STATUS Status; UINT64 MemoryType; UINT64 Remainder; BOOLEAN OverLap; BOOLEAN Positive; UINT32 MsrNum; UINTN MtrrNumber; VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR]; UINT32 UsedMtrr; UINT64 MtrrValidBitsMask; UINT64 MtrrValidAddressMask; UINTN Cr4; BOOLEAN OverwriteExistingMtrr; UINT32 FirmwareVariableMtrrCount; UINT32 VariableMtrrEnd; if (!IsMtrrSupported ()) { return RETURN_UNSUPPORTED; } FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount (); VariableMtrrEnd = MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (2 * GetVariableMtrrCount ()) - 1; MtrrLibInitializeMtrrMask(&MtrrValidBitsMask, &MtrrValidAddressMask); TempQword = 0; MemoryType = (UINT64)Attribute; OverwriteExistingMtrr = FALSE; // // Check for an invalid parameter // if (Length == 0) { return RETURN_INVALID_PARAMETER; } if ( (BaseAddress &~MtrrValidAddressMask) != 0 || (Length &~MtrrValidAddressMask) != 0 ) { return RETURN_UNSUPPORTED; } // // Check if Fixed MTRR // Status = RETURN_SUCCESS; while ((BaseAddress < BASE_1MB) && (Length > 0) && Status == RETURN_SUCCESS) { Cr4 = PreMtrrChange (); Status = ProgramFixedMtrr (MemoryType, &BaseAddress, &Length); PostMtrrChange (Cr4); if (RETURN_ERROR (Status)) { return Status; } } if (Length == 0) { // // A Length of 0 can only make sense for fixed MTTR ranges. // Since we just handled the fixed MTRRs, we can skip the // variable MTRR section. // goto Done; } // // Since memory ranges below 1MB will be overridden by the fixed MTRRs, // we can set the bade to 0 to save variable MTRRs. // if (BaseAddress == BASE_1MB) { BaseAddress = 0; Length += SIZE_1MB; } // // Check memory base address alignment // DivU64x64Remainder (BaseAddress, Power2MaxMemory (LShiftU64 (Length, 1)), &Remainder); if (Remainder != 0) { DivU64x64Remainder (BaseAddress, Power2MaxMemory (Length), &Remainder); if (Remainder != 0) { Status = RETURN_UNSUPPORTED; goto Done; } } // // Check for overlap // UsedMtrr = MtrrGetMemoryAttributeInVariableMtrr (MtrrValidBitsMask, MtrrValidAddressMask, VariableMtrr); OverLap = CheckMemoryAttributeOverlap (BaseAddress, BaseAddress + Length - 1, VariableMtrr); if (OverLap) { Status = CombineMemoryAttribute (MemoryType, &BaseAddress, &Length, VariableMtrr, &UsedMtrr, &OverwriteExistingMtrr); if (RETURN_ERROR (Status)) { goto Done; } if (Length == 0) { // // Combined successfully // Status = RETURN_SUCCESS; goto Done; } } // // Program Variable MTRRs // // Avoid hardcode here and read data dynamically // if (UsedMtrr >= FirmwareVariableMtrrCount) { Status = RETURN_OUT_OF_RESOURCES; goto Done; } // // The memory type is the same with the type specified by // MTRR_LIB_IA32_MTRR_DEF_TYPE. // if ((!OverwriteExistingMtrr) && (Attribute == GetMtrrDefaultMemoryType ())) { // // Invalidate the now-unused MTRRs // InvalidateMtrr(VariableMtrr); goto Done; } TempQword = Length; if (TempQword == Power2MaxMemory (TempQword)) { // // Invalidate the now-unused MTRRs // InvalidateMtrr(VariableMtrr); // // Find first unused MTRR // for (MsrNum = MTRR_LIB_IA32_VARIABLE_MTRR_BASE; MsrNum < VariableMtrrEnd; MsrNum += 2 ) { if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) { break; } } ProgramVariableMtrr ( MsrNum, BaseAddress, Length, MemoryType, MtrrValidAddressMask ); } else { Positive = GetDirection (TempQword, &MtrrNumber); if ((UsedMtrr + MtrrNumber) > FirmwareVariableMtrrCount) { Status = RETURN_OUT_OF_RESOURCES; goto Done; } // // Invalidate the now-unused MTRRs // InvalidateMtrr(VariableMtrr); // // Find first unused MTRR // for (MsrNum = MTRR_LIB_IA32_VARIABLE_MTRR_BASE; MsrNum < VariableMtrrEnd; MsrNum += 2 ) { if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) { break; } } if (!Positive) { Length = Power2MaxMemory (LShiftU64 (TempQword, 1)); ProgramVariableMtrr ( MsrNum, BaseAddress, Length, MemoryType, MtrrValidAddressMask ); BaseAddress += Length; TempQword = Length - TempQword; MemoryType = MTRR_CACHE_UNCACHEABLE; } do { // // Find unused MTRR // for (; MsrNum < VariableMtrrEnd; MsrNum += 2) { if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) { break; } } Length = Power2MaxMemory (TempQword); if (!Positive) { BaseAddress -= Length; } ProgramVariableMtrr ( MsrNum, BaseAddress, Length, MemoryType, MtrrValidAddressMask ); if (Positive) { BaseAddress += Length; } TempQword -= Length; } while (TempQword > 0); } Done: return Status; }