/** Check the direction to program variable MTRRs. This function determines which direction of programming the variable MTRRs will use fewer MTRRs. @param Input 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 GetDirection ( IN UINT64 Input, IN UINTN *MtrrNumber ) { UINT64 TempQword; UINT32 Positive; UINT32 Subtractive; TempQword = Input; Positive = 0; Subtractive = 0; do { TempQword -= Power2MaxMemory (TempQword); Positive++; } while (TempQword != 0); TempQword = Power2MaxMemory (LShiftU64 (Input, 1)) - Input; Subtractive++; do { TempQword -= Power2MaxMemory (TempQword); Subtractive++; } while (TempQword != 0); if (Positive <= Subtractive) { *MtrrNumber = Positive; return TRUE; } else { *MtrrNumber = Subtractive; return FALSE; } }
/** 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; }
/** 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; }
EFI_STATUS EFIAPI SubmitResources ( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN EFI_HANDLE RootBridgeHandle, IN VOID *Configuration ) /*++ Routine Description: Submits the I/O and memory resource requirements for the specified PCI Root Bridge Arguments: This -- The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance RootBridgeHandle -- The PCI Root Bridge whose I/O and memory resource requirements are being submitted Configuration -- The pointer to the PCI I/O and PCI memory resource descriptor Returns: --*/ // TODO: EFI_INVALID_PARAMETER - add return value to function comment // TODO: EFI_INVALID_PARAMETER - add return value to function comment // TODO: EFI_INVALID_PARAMETER - add return value to function comment // TODO: EFI_INVALID_PARAMETER - add return value to function comment // TODO: EFI_INVALID_PARAMETER - add return value to function comment // TODO: EFI_INVALID_PARAMETER - add return value to function comment // TODO: EFI_SUCCESS - add return value to function comment // TODO: EFI_INVALID_PARAMETER - add return value to function comment { EFI_LIST_ENTRY *List; PCI_HOST_BRIDGE_INSTANCE *HostBridgeInstance; PCI_ROOT_BRIDGE_INSTANCE *RootBridgeInstance; UINT8 *Temp; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *ptr; UINT64 AddrLen; UINT64 Alignment; // // Check the input parameter: Configuration // if (Configuration == NULL) { return EFI_INVALID_PARAMETER; } HostBridgeInstance = INSTANCE_FROM_RESOURCE_ALLOCATION_THIS (This); List = HostBridgeInstance->Head.ForwardLink; Temp = (UINT8 *) Configuration; while (*Temp == 0x8A) { Temp += sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR); } if (*Temp != 0x79) { return EFI_INVALID_PARAMETER; } Temp = (UINT8 *) Configuration; while (List != &HostBridgeInstance->Head) { RootBridgeInstance = DRIVER_INSTANCE_FROM_LIST_ENTRY (List); if (RootBridgeHandle == RootBridgeInstance->Handle) { while (*Temp == 0x8A) { ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp; // // Check Address Length // if (ptr->AddrLen > 0xffffffff) { return EFI_INVALID_PARAMETER; } // // Check address range alignment // if (ptr->AddrRangeMax >= 0xffffffff || ptr->AddrRangeMax != (Power2MaxMemory(ptr->AddrRangeMax + 1) - 1)) { return EFI_INVALID_PARAMETER; } switch (ptr->ResType) { case 0: // // Check invalid Address Sapce Granularity // if (ptr->AddrSpaceGranularity != 32) { return EFI_INVALID_PARAMETER; } // // check the memory resource request is supported by PCI root bridge // if (RootBridgeInstance->RootBridgeAttrib == EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM && ptr->SpecificFlag == 0x06 ) { return EFI_INVALID_PARAMETER; } AddrLen = ptr->AddrLen; Alignment = ptr->AddrRangeMax; if (ptr->AddrSpaceGranularity == 32) { if (ptr->SpecificFlag == 0x06) { // // Apply from GCD // RootBridgeInstance->ResAllocNode[TypePMem32].Status = ResSubmitted; } else { RootBridgeInstance->ResAllocNode[TypeMem32].Length = AddrLen; RootBridgeInstance->ResAllocNode[TypeMem32].Alignment = Alignment; RootBridgeInstance->ResAllocNode[TypeMem32].Status = ResRequested; HostBridgeInstance->ResourceSubmited = TRUE; } } if (ptr->AddrSpaceGranularity == 64) { if (ptr->SpecificFlag == 0x06) { RootBridgeInstance->ResAllocNode[TypePMem64].Status = ResSubmitted; } else { RootBridgeInstance->ResAllocNode[TypeMem64].Status = ResSubmitted; } } break; case 1: AddrLen = (UINTN) ptr->AddrLen; Alignment = (UINTN) ptr->AddrRangeMax; RootBridgeInstance->ResAllocNode[TypeIo].Length = AddrLen; RootBridgeInstance->ResAllocNode[TypeIo].Alignment = Alignment; RootBridgeInstance->ResAllocNode[TypeIo].Status = ResRequested; HostBridgeInstance->ResourceSubmited = TRUE; break; default: break; } Temp += sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR); } return EFI_SUCCESS; } List = List->ForwardLink; } return EFI_INVALID_PARAMETER; }