/** Determine the MTRR numbers used to program a memory range. This function first checks the alignment of the base address. If the alignment of the base address <= Length, cover the memory range (BaseAddress, alignment) by a MTRR, then BaseAddress += alignment and Length -= alignment. Repeat the step until alignment > Length. Then this function determines which direction of programming the variable MTRRs for the remaining length will use fewer MTRRs. @param BaseAddress Length of Memory to program MTRR @param Length Length of Memory to program MTRR @param MtrrNumber Pointer to the number of necessary MTRRs @retval TRUE Positive direction is better. FALSE Negtive direction is better. **/ BOOLEAN GetMtrrNumberAndDirection ( IN UINT64 BaseAddress, IN UINT64 Length, IN UINTN *MtrrNumber ) { UINT64 TempQword; UINT64 Alignment; UINT32 Positive; UINT32 Subtractive; *MtrrNumber = 0; if (BaseAddress != 0) { do { // // Calculate the alignment of the base address. // Alignment = LShiftU64 (1, (UINTN)LowBitSet64 (BaseAddress)); if (Alignment > Length) { break; } (*MtrrNumber)++; BaseAddress += Alignment; Length -= Alignment; } while (TRUE); if (Length == 0) { return TRUE; } } TempQword = Length; Positive = 0; Subtractive = 0; do { TempQword -= Power2MaxMemory (TempQword); Positive++; } while (TempQword != 0); TempQword = Power2MaxMemory (LShiftU64 (Length, 1)) - Length; Subtractive++; do { TempQword -= Power2MaxMemory (TempQword); Subtractive++; } while (TempQword != 0); if (Positive <= Subtractive) { *MtrrNumber += Positive; return TRUE; } else { *MtrrNumber += Subtractive; return FALSE; } }
/** 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; }
STATIC UINT64* GetBlockEntryListFromAddress ( IN UINT64 *RootTable, IN UINT64 RegionStart, OUT UINTN *TableLevel, IN OUT UINT64 *BlockEntrySize, OUT UINT64 **LastBlockEntry ) { UINTN RootTableLevel; UINTN RootTableEntryCount; UINT64 *TranslationTable; UINT64 *BlockEntry; UINT64 *SubTableBlockEntry; UINT64 BlockEntryAddress; UINTN BaseAddressAlignment; UINTN PageLevel; UINTN Index; UINTN IndexLevel; UINTN T0SZ; UINT64 Attributes; UINT64 TableAttributes; // Initialize variable BlockEntry = NULL; // Ensure the parameters are valid if (!(TableLevel && BlockEntrySize && LastBlockEntry)) { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); return NULL; } // Ensure the Region is aligned on 4KB boundary if ((RegionStart & (SIZE_4KB - 1)) != 0) { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); return NULL; } // Ensure the required size is aligned on 4KB boundary and not 0 if ((*BlockEntrySize & (SIZE_4KB - 1)) != 0 || *BlockEntrySize == 0) { ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER); return NULL; } T0SZ = ArmGetTCR () & TCR_T0SZ_MASK; // Get the Table info from T0SZ GetRootTranslationTableInfo (T0SZ, &RootTableLevel, &RootTableEntryCount); // If the start address is 0x0 then we use the size of the region to identify the alignment if (RegionStart == 0) { // Identify the highest possible alignment for the Region Size BaseAddressAlignment = LowBitSet64 (*BlockEntrySize); } else { // Identify the highest possible alignment for the Base Address BaseAddressAlignment = LowBitSet64 (RegionStart); } // Identify the Page Level the RegionStart must belong to. Note that PageLevel // should be at least 1 since block translations are not supported at level 0 PageLevel = MAX (3 - ((BaseAddressAlignment - 12) / 9), 1); // If the required size is smaller than the current block size then we need to go to the page below. // The PageLevel was calculated on the Base Address alignment but did not take in account the alignment // of the allocation size while (*BlockEntrySize < TT_BLOCK_ENTRY_SIZE_AT_LEVEL (PageLevel)) { // It does not fit so we need to go a page level above PageLevel++; } // // Get the Table Descriptor for the corresponding PageLevel. We need to decompose RegionStart to get appropriate entries // TranslationTable = RootTable; for (IndexLevel = RootTableLevel; IndexLevel <= PageLevel; IndexLevel++) { BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, IndexLevel, RegionStart); if ((IndexLevel != 3) && ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY)) { // Go to the next table TranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE); // If we are at the last level then update the last level to next level if (IndexLevel == PageLevel) { // Enter the next level PageLevel++; } } else if ((*BlockEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) { // If we are not at the last level then we need to split this BlockEntry if (IndexLevel != PageLevel) { // Retrieve the attributes from the block entry Attributes = *BlockEntry & TT_ATTRIBUTES_MASK; // Convert the block entry attributes into Table descriptor attributes TableAttributes = TT_TABLE_AP_NO_PERMISSION; if (Attributes & TT_PXN_MASK) { TableAttributes = TT_TABLE_PXN; } // XN maps to UXN in the EL1&0 translation regime if (Attributes & TT_XN_MASK) { TableAttributes = TT_TABLE_XN; } if (Attributes & TT_NS) { TableAttributes = TT_TABLE_NS; } // Get the address corresponding at this entry BlockEntryAddress = RegionStart; BlockEntryAddress = BlockEntryAddress >> TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel); // Shift back to right to set zero before the effective address BlockEntryAddress = BlockEntryAddress << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel); // Set the correct entry type for the next page level if ((IndexLevel + 1) == 3) { Attributes |= TT_TYPE_BLOCK_ENTRY_LEVEL3; } else { Attributes |= TT_TYPE_BLOCK_ENTRY; } // Create a new translation table TranslationTable = (UINT64*)AllocateAlignedPages (EFI_SIZE_TO_PAGES(TT_ENTRY_COUNT * sizeof(UINT64)), TT_ALIGNMENT_DESCRIPTION_TABLE); if (TranslationTable == NULL) { return NULL; } // Populate the newly created lower level table SubTableBlockEntry = TranslationTable; for (Index = 0; Index < TT_ENTRY_COUNT; Index++) { *SubTableBlockEntry = Attributes | (BlockEntryAddress + (Index << TT_ADDRESS_OFFSET_AT_LEVEL(IndexLevel + 1))); SubTableBlockEntry++; } // Fill the BlockEntry with the new TranslationTable *BlockEntry = ((UINTN)TranslationTable & TT_ADDRESS_MASK_DESCRIPTION_TABLE) | TableAttributes | TT_TYPE_TABLE_ENTRY; } } else { if (IndexLevel != PageLevel) {
/** Parse PCI bar and collect the assigned PCI resouce information. @param[in] Command Supported attributes. @param[in] Bus PCI bus number. @param[in] Device PCI device number. @param[in] Function PCI function number. @param[in] BarOffsetBase PCI bar start offset. @param[in] BarOffsetEnd PCI bar end offset. @param[in] Io IO aperture. @param[in] Mem MMIO aperture. @param[in] MemAbove4G MMIO aperture above 4G. @param[in] PMem Prefetchable MMIO aperture. @param[in] PMemAbove4G Prefetchable MMIO aperture above 4G. **/ STATIC VOID PcatPciRootBridgeParseBars ( IN UINT16 Command, IN UINTN Bus, IN UINTN Device, IN UINTN Function, IN UINTN BarOffsetBase, IN UINTN BarOffsetEnd, IN PCI_ROOT_BRIDGE_APERTURE *Io, IN PCI_ROOT_BRIDGE_APERTURE *Mem, IN PCI_ROOT_BRIDGE_APERTURE *MemAbove4G, IN PCI_ROOT_BRIDGE_APERTURE *PMem, IN PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G ) { UINT32 OriginalValue; UINT32 Value; UINT32 OriginalUpperValue; UINT32 UpperValue; UINT64 Mask; UINTN Offset; UINTN LowBit; UINT64 Base; UINT64 Length; UINT64 Limit; PCI_ROOT_BRIDGE_APERTURE *MemAperture; for (Offset = BarOffsetBase; Offset < BarOffsetEnd; Offset += sizeof (UINT32)) { PcatPciRootBridgeBarExisted ( PCI_LIB_ADDRESS (Bus, Device, Function, Offset), &OriginalValue, &Value ); if (Value == 0) { continue; } if ((Value & BIT0) == BIT0) { // // IO Bar // if (Command & EFI_PCI_COMMAND_IO_SPACE) { Mask = 0xfffffffc; Base = OriginalValue & Mask; Length = ((~(Value & Mask)) & Mask) + 0x04; if (!(Value & 0xFFFF0000)) { Length &= 0x0000FFFF; } Limit = Base + Length - 1; if ((Base > 0) && (Base < Limit)) { if (Io->Base > Base) { Io->Base = Base; } if (Io->Limit < Limit) { Io->Limit = Limit; } } } } else { // // Mem Bar // if (Command & EFI_PCI_COMMAND_MEMORY_SPACE) { Mask = 0xfffffff0; Base = OriginalValue & Mask; Length = Value & Mask; if ((Value & (BIT1 | BIT2)) == 0) { // // 32bit // Length = ((~Length) + 1) & 0xffffffff; if ((Value & BIT3) == BIT3) { MemAperture = PMem; } else { MemAperture = Mem; } } else { // // 64bit // Offset += 4; PcatPciRootBridgeBarExisted ( PCI_LIB_ADDRESS (Bus, Device, Function, Offset), &OriginalUpperValue, &UpperValue ); Base = Base | LShiftU64 ((UINT64) OriginalUpperValue, 32); Length = Length | LShiftU64 ((UINT64) UpperValue, 32); if (Length != 0) { LowBit = LowBitSet64 (Length); Length = LShiftU64 (1ULL, LowBit); } if ((Value & BIT3) == BIT3) { MemAperture = PMemAbove4G; } else { MemAperture = MemAbove4G; } } Limit = Base + Length - 1; if ((Base > 0) && (Base < Limit)) { if (MemAperture->Base > Base) { MemAperture->Base = Base; } if (MemAperture->Limit < Limit) { MemAperture->Limit = Limit; } } } } } }