| /** @file | |
| Provides the basic interfaces to abstract a PCI Host Bridge Resource Allocation. | |
| Copyright (c) 1999 - 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 "PciHostBridge.h" | |
| #include "PciRootBridge.h" | |
| #include "PciHostResource.h" | |
| EFI_METRONOME_ARCH_PROTOCOL *mMetronome; | |
| EFI_CPU_IO2_PROTOCOL *mCpuIo; | |
| GLOBAL_REMOVE_IF_UNREFERENCED CHAR16 *mAcpiAddressSpaceTypeStr[] = { | |
| L"Mem", L"I/O", L"Bus" | |
| }; | |
| GLOBAL_REMOVE_IF_UNREFERENCED CHAR16 *mPciResourceTypeStr[] = { | |
| L"I/O", L"Mem", L"PMem", L"Mem64", L"PMem64", L"Bus" | |
| }; | |
| EDKII_IOMMU_PROTOCOL *mIoMmuProtocol; | |
| EFI_EVENT mIoMmuEvent; | |
| VOID *mIoMmuRegistration; | |
| /** | |
| Ensure the compatibility of an IO space descriptor with the IO aperture. | |
| The IO space descriptor can come from the GCD IO space map, or it can | |
| represent a gap between two neighboring IO space descriptors. In the latter | |
| case, the GcdIoType field is expected to be EfiGcdIoTypeNonExistent. | |
| If the IO space descriptor already has type EfiGcdIoTypeIo, then no action is | |
| taken -- it is by definition compatible with the aperture. | |
| Otherwise, the intersection of the IO space descriptor is calculated with the | |
| aperture. If the intersection is the empty set (no overlap), no action is | |
| taken; the IO space descriptor is compatible with the aperture. | |
| Otherwise, the type of the descriptor is investigated again. If the type is | |
| EfiGcdIoTypeNonExistent (representing a gap, or a genuine descriptor with | |
| such a type), then an attempt is made to add the intersection as IO space to | |
| the GCD IO space map. This ensures continuity for the aperture, and the | |
| descriptor is deemed compatible with the aperture. | |
| Otherwise, the IO space descriptor is incompatible with the IO aperture. | |
| @param[in] Base Base address of the aperture. | |
| @param[in] Length Length of the aperture. | |
| @param[in] Descriptor The descriptor to ensure compatibility with the | |
| aperture for. | |
| @retval EFI_SUCCESS The descriptor is compatible. The GCD IO space | |
| map may have been updated, for continuity | |
| within the aperture. | |
| @retval EFI_INVALID_PARAMETER The descriptor is incompatible. | |
| @return Error codes from gDS->AddIoSpace(). | |
| **/ | |
| EFI_STATUS | |
| IntersectIoDescriptor ( | |
| IN UINT64 Base, | |
| IN UINT64 Length, | |
| IN CONST EFI_GCD_IO_SPACE_DESCRIPTOR *Descriptor | |
| ) | |
| { | |
| UINT64 IntersectionBase; | |
| UINT64 IntersectionEnd; | |
| EFI_STATUS Status; | |
| if (Descriptor->GcdIoType == EfiGcdIoTypeIo) { | |
| return EFI_SUCCESS; | |
| } | |
| IntersectionBase = MAX (Base, Descriptor->BaseAddress); | |
| IntersectionEnd = MIN (Base + Length, | |
| Descriptor->BaseAddress + Descriptor->Length); | |
| if (IntersectionBase >= IntersectionEnd) { | |
| // | |
| // The descriptor and the aperture don't overlap. | |
| // | |
| return EFI_SUCCESS; | |
| } | |
| if (Descriptor->GcdIoType == EfiGcdIoTypeNonExistent) { | |
| Status = gDS->AddIoSpace (EfiGcdIoTypeIo, IntersectionBase, | |
| IntersectionEnd - IntersectionBase); | |
| DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE, | |
| "%a: %a: add [%Lx, %Lx): %r\n", gEfiCallerBaseName, __FUNCTION__, | |
| IntersectionBase, IntersectionEnd, Status)); | |
| return Status; | |
| } | |
| DEBUG ((EFI_D_ERROR, "%a: %a: desc [%Lx, %Lx) type %u conflicts with " | |
| "aperture [%Lx, %Lx)\n", gEfiCallerBaseName, __FUNCTION__, | |
| Descriptor->BaseAddress, Descriptor->BaseAddress + Descriptor->Length, | |
| (UINT32)Descriptor->GcdIoType, Base, Base + Length)); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /** | |
| Add IO space to GCD. | |
| The routine checks the GCD database and only adds those which are | |
| not added in the specified range to GCD. | |
| @param Base Base address of the IO space. | |
| @param Length Length of the IO space. | |
| @retval EFI_SUCCES The IO space was added successfully. | |
| **/ | |
| EFI_STATUS | |
| AddIoSpace ( | |
| IN UINT64 Base, | |
| IN UINT64 Length | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINTN NumberOfDescriptors; | |
| EFI_GCD_IO_SPACE_DESCRIPTOR *IoSpaceMap; | |
| Status = gDS->GetIoSpaceMap (&NumberOfDescriptors, &IoSpaceMap); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((EFI_D_ERROR, "%a: %a: GetIoSpaceMap(): %r\n", | |
| gEfiCallerBaseName, __FUNCTION__, Status)); | |
| return Status; | |
| } | |
| for (Index = 0; Index < NumberOfDescriptors; Index++) { | |
| Status = IntersectIoDescriptor (Base, Length, &IoSpaceMap[Index]); | |
| if (EFI_ERROR (Status)) { | |
| goto FreeIoSpaceMap; | |
| } | |
| } | |
| DEBUG_CODE ( | |
| // | |
| // Make sure there are adjacent descriptors covering [Base, Base + Length). | |
| // It is possible that they have not been merged; merging can be prevented | |
| // by allocation. | |
| // | |
| UINT64 CheckBase; | |
| EFI_STATUS CheckStatus; | |
| EFI_GCD_IO_SPACE_DESCRIPTOR Descriptor; | |
| for (CheckBase = Base; | |
| CheckBase < Base + Length; | |
| CheckBase = Descriptor.BaseAddress + Descriptor.Length) { | |
| CheckStatus = gDS->GetIoSpaceDescriptor (CheckBase, &Descriptor); | |
| ASSERT_EFI_ERROR (CheckStatus); | |
| ASSERT (Descriptor.GcdIoType == EfiGcdIoTypeIo); | |
| } | |
| ); | |
| FreeIoSpaceMap: | |
| FreePool (IoSpaceMap); | |
| return Status; | |
| } | |
| /** | |
| Ensure the compatibility of a memory space descriptor with the MMIO aperture. | |
| The memory space descriptor can come from the GCD memory space map, or it can | |
| represent a gap between two neighboring memory space descriptors. In the | |
| latter case, the GcdMemoryType field is expected to be | |
| EfiGcdMemoryTypeNonExistent. | |
| If the memory space descriptor already has type | |
| EfiGcdMemoryTypeMemoryMappedIo, and its capabilities are a superset of the | |
| required capabilities, then no action is taken -- it is by definition | |
| compatible with the aperture. | |
| Otherwise, the intersection of the memory space descriptor is calculated with | |
| the aperture. If the intersection is the empty set (no overlap), no action is | |
| taken; the memory space descriptor is compatible with the aperture. | |
| Otherwise, the type of the descriptor is investigated again. If the type is | |
| EfiGcdMemoryTypeNonExistent (representing a gap, or a genuine descriptor with | |
| such a type), then an attempt is made to add the intersection as MMIO space | |
| to the GCD memory space map, with the specified capabilities. This ensures | |
| continuity for the aperture, and the descriptor is deemed compatible with the | |
| aperture. | |
| Otherwise, the memory space descriptor is incompatible with the MMIO | |
| aperture. | |
| @param[in] Base Base address of the aperture. | |
| @param[in] Length Length of the aperture. | |
| @param[in] Capabilities Capabilities required by the aperture. | |
| @param[in] Descriptor The descriptor to ensure compatibility with the | |
| aperture for. | |
| @retval EFI_SUCCESS The descriptor is compatible. The GCD memory | |
| space map may have been updated, for | |
| continuity within the aperture. | |
| @retval EFI_INVALID_PARAMETER The descriptor is incompatible. | |
| @return Error codes from gDS->AddMemorySpace(). | |
| **/ | |
| EFI_STATUS | |
| IntersectMemoryDescriptor ( | |
| IN UINT64 Base, | |
| IN UINT64 Length, | |
| IN UINT64 Capabilities, | |
| IN CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor | |
| ) | |
| { | |
| UINT64 IntersectionBase; | |
| UINT64 IntersectionEnd; | |
| EFI_STATUS Status; | |
| if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo && | |
| (Descriptor->Capabilities & Capabilities) == Capabilities) { | |
| return EFI_SUCCESS; | |
| } | |
| IntersectionBase = MAX (Base, Descriptor->BaseAddress); | |
| IntersectionEnd = MIN (Base + Length, | |
| Descriptor->BaseAddress + Descriptor->Length); | |
| if (IntersectionBase >= IntersectionEnd) { | |
| // | |
| // The descriptor and the aperture don't overlap. | |
| // | |
| return EFI_SUCCESS; | |
| } | |
| if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeNonExistent) { | |
| Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo, | |
| IntersectionBase, IntersectionEnd - IntersectionBase, | |
| Capabilities); | |
| DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE, | |
| "%a: %a: add [%Lx, %Lx): %r\n", gEfiCallerBaseName, __FUNCTION__, | |
| IntersectionBase, IntersectionEnd, Status)); | |
| return Status; | |
| } | |
| DEBUG ((EFI_D_ERROR, "%a: %a: desc [%Lx, %Lx) type %u cap %Lx conflicts " | |
| "with aperture [%Lx, %Lx) cap %Lx\n", gEfiCallerBaseName, __FUNCTION__, | |
| Descriptor->BaseAddress, Descriptor->BaseAddress + Descriptor->Length, | |
| (UINT32)Descriptor->GcdMemoryType, Descriptor->Capabilities, | |
| Base, Base + Length, Capabilities)); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /** | |
| Add MMIO space to GCD. | |
| The routine checks the GCD database and only adds those which are | |
| not added in the specified range to GCD. | |
| @param Base Base address of the MMIO space. | |
| @param Length Length of the MMIO space. | |
| @param Capabilities Capabilities of the MMIO space. | |
| @retval EFI_SUCCES The MMIO space was added successfully. | |
| **/ | |
| EFI_STATUS | |
| AddMemoryMappedIoSpace ( | |
| IN UINT64 Base, | |
| IN UINT64 Length, | |
| IN UINT64 Capabilities | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINTN NumberOfDescriptors; | |
| EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; | |
| Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((EFI_D_ERROR, "%a: %a: GetMemorySpaceMap(): %r\n", | |
| gEfiCallerBaseName, __FUNCTION__, Status)); | |
| return Status; | |
| } | |
| for (Index = 0; Index < NumberOfDescriptors; Index++) { | |
| Status = IntersectMemoryDescriptor (Base, Length, Capabilities, | |
| &MemorySpaceMap[Index]); | |
| if (EFI_ERROR (Status)) { | |
| goto FreeMemorySpaceMap; | |
| } | |
| } | |
| DEBUG_CODE ( | |
| // | |
| // Make sure there are adjacent descriptors covering [Base, Base + Length). | |
| // It is possible that they have not been merged; merging can be prevented | |
| // by allocation and different capabilities. | |
| // | |
| UINT64 CheckBase; | |
| EFI_STATUS CheckStatus; | |
| EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; | |
| for (CheckBase = Base; | |
| CheckBase < Base + Length; | |
| CheckBase = Descriptor.BaseAddress + Descriptor.Length) { | |
| CheckStatus = gDS->GetMemorySpaceDescriptor (CheckBase, &Descriptor); | |
| ASSERT_EFI_ERROR (CheckStatus); | |
| ASSERT (Descriptor.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo); | |
| ASSERT ((Descriptor.Capabilities & Capabilities) == Capabilities); | |
| } | |
| ); | |
| FreeMemorySpaceMap: | |
| FreePool (MemorySpaceMap); | |
| return Status; | |
| } | |
| /** | |
| Event notification that is fired when IOMMU protocol is installed. | |
| @param Event The Event that is being processed. | |
| @param Context Event Context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| IoMmuProtocolCallback ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL, (VOID **)&mIoMmuProtocol); | |
| if (!EFI_ERROR(Status)) { | |
| gBS->CloseEvent (mIoMmuEvent); | |
| } | |
| } | |
| /** | |
| Entry point of this driver. | |
| @param ImageHandle Image handle of this driver. | |
| @param SystemTable Pointer to standard EFI system table. | |
| @retval EFI_SUCCESS Succeed. | |
| @retval EFI_DEVICE_ERROR Fail to install PCI_ROOT_BRIDGE_IO protocol. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| InitializePciHostBridge ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| PCI_HOST_BRIDGE_INSTANCE *HostBridge; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| PCI_ROOT_BRIDGE *RootBridges; | |
| UINTN RootBridgeCount; | |
| UINTN Index; | |
| PCI_ROOT_BRIDGE_APERTURE *MemApertures[4]; | |
| UINTN MemApertureIndex; | |
| BOOLEAN ResourceAssigned; | |
| LIST_ENTRY *Link; | |
| RootBridges = PciHostBridgeGetRootBridges (&RootBridgeCount); | |
| if ((RootBridges == NULL) || (RootBridgeCount == 0)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| Status = gBS->LocateProtocol (&gEfiMetronomeArchProtocolGuid, NULL, (VOID **) &mMetronome); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = gBS->LocateProtocol (&gEfiCpuIo2ProtocolGuid, NULL, (VOID **) &mCpuIo); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Most systems in the world including complex servers have only one Host Bridge. | |
| // | |
| HostBridge = AllocateZeroPool (sizeof (PCI_HOST_BRIDGE_INSTANCE)); | |
| ASSERT (HostBridge != NULL); | |
| HostBridge->Signature = PCI_HOST_BRIDGE_SIGNATURE; | |
| HostBridge->CanRestarted = TRUE; | |
| InitializeListHead (&HostBridge->RootBridges); | |
| ResourceAssigned = FALSE; | |
| // | |
| // Create Root Bridge Device Handle in this Host Bridge | |
| // | |
| for (Index = 0; Index < RootBridgeCount; Index++) { | |
| // | |
| // Create Root Bridge Handle Instance | |
| // | |
| RootBridge = CreateRootBridge (&RootBridges[Index]); | |
| ASSERT (RootBridge != NULL); | |
| if (RootBridge == NULL) { | |
| continue; | |
| } | |
| // | |
| // Make sure all root bridges share the same ResourceAssigned value. | |
| // | |
| if (Index == 0) { | |
| ResourceAssigned = RootBridges[Index].ResourceAssigned; | |
| } else { | |
| ASSERT (ResourceAssigned == RootBridges[Index].ResourceAssigned); | |
| } | |
| if (RootBridges[Index].Io.Base <= RootBridges[Index].Io.Limit) { | |
| Status = AddIoSpace ( | |
| RootBridges[Index].Io.Base, | |
| RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if (ResourceAssigned) { | |
| Status = gDS->AllocateIoSpace ( | |
| EfiGcdAllocateAddress, | |
| EfiGcdIoTypeIo, | |
| 0, | |
| RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1, | |
| &RootBridges[Index].Io.Base, | |
| gImageHandle, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| } | |
| // | |
| // Add all the Mem/PMem aperture to GCD | |
| // Mem/PMem shouldn't overlap with each other | |
| // Root bridge which needs to combine MEM and PMEM should only report | |
| // the MEM aperture in Mem | |
| // | |
| MemApertures[0] = &RootBridges[Index].Mem; | |
| MemApertures[1] = &RootBridges[Index].MemAbove4G; | |
| MemApertures[2] = &RootBridges[Index].PMem; | |
| MemApertures[3] = &RootBridges[Index].PMemAbove4G; | |
| for (MemApertureIndex = 0; MemApertureIndex < ARRAY_SIZE (MemApertures); MemApertureIndex++) { | |
| if (MemApertures[MemApertureIndex]->Base <= MemApertures[MemApertureIndex]->Limit) { | |
| Status = AddMemoryMappedIoSpace ( | |
| MemApertures[MemApertureIndex]->Base, | |
| MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1, | |
| EFI_MEMORY_UC | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = gDS->SetMemorySpaceAttributes ( | |
| MemApertures[MemApertureIndex]->Base, | |
| MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1, | |
| EFI_MEMORY_UC | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_WARN, "PciHostBridge driver failed to set EFI_MEMORY_UC to MMIO aperture - %r.\n", Status)); | |
| } | |
| if (ResourceAssigned) { | |
| Status = gDS->AllocateMemorySpace ( | |
| EfiGcdAllocateAddress, | |
| EfiGcdMemoryTypeMemoryMappedIo, | |
| 0, | |
| MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1, | |
| &MemApertures[MemApertureIndex]->Base, | |
| gImageHandle, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| } | |
| } | |
| // | |
| // Insert Root Bridge Handle Instance | |
| // | |
| InsertTailList (&HostBridge->RootBridges, &RootBridge->Link); | |
| } | |
| // | |
| // When resources were assigned, it's not needed to expose | |
| // PciHostBridgeResourceAllocation protocol. | |
| // | |
| if (!ResourceAssigned) { | |
| HostBridge->ResAlloc.NotifyPhase = NotifyPhase; | |
| HostBridge->ResAlloc.GetNextRootBridge = GetNextRootBridge; | |
| HostBridge->ResAlloc.GetAllocAttributes = GetAttributes; | |
| HostBridge->ResAlloc.StartBusEnumeration = StartBusEnumeration; | |
| HostBridge->ResAlloc.SetBusNumbers = SetBusNumbers; | |
| HostBridge->ResAlloc.SubmitResources = SubmitResources; | |
| HostBridge->ResAlloc.GetProposedResources = GetProposedResources; | |
| HostBridge->ResAlloc.PreprocessController = PreprocessController; | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &HostBridge->Handle, | |
| &gEfiPciHostBridgeResourceAllocationProtocolGuid, &HostBridge->ResAlloc, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| RootBridge->RootBridgeIo.ParentHandle = HostBridge->Handle; | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &RootBridge->Handle, | |
| &gEfiDevicePathProtocolGuid, RootBridge->DevicePath, | |
| &gEfiPciRootBridgeIoProtocolGuid, &RootBridge->RootBridgeIo, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| PciHostBridgeFreeRootBridges (RootBridges, RootBridgeCount); | |
| if (!EFI_ERROR (Status)) { | |
| mIoMmuEvent = EfiCreateProtocolNotifyEvent ( | |
| &gEdkiiIoMmuProtocolGuid, | |
| TPL_CALLBACK, | |
| IoMmuProtocolCallback, | |
| NULL, | |
| &mIoMmuRegistration | |
| ); | |
| } | |
| return Status; | |
| } | |
| /** | |
| This routine constructs the resource descriptors for all root bridges and call PciHostBridgeResourceConflict(). | |
| @param HostBridge The Host Bridge Instance where the resource adjustment happens. | |
| **/ | |
| VOID | |
| ResourceConflict ( | |
| IN PCI_HOST_BRIDGE_INSTANCE *HostBridge | |
| ) | |
| { | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Resources; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; | |
| EFI_ACPI_END_TAG_DESCRIPTOR *End; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| LIST_ENTRY *Link; | |
| UINTN RootBridgeCount; | |
| PCI_RESOURCE_TYPE Index; | |
| PCI_RES_NODE *ResAllocNode; | |
| RootBridgeCount = 0; | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridgeCount++; | |
| } | |
| Resources = AllocatePool ( | |
| RootBridgeCount * (TypeMax * sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)) + | |
| sizeof (EFI_ACPI_END_TAG_DESCRIPTOR) | |
| ); | |
| ASSERT (Resources != NULL); | |
| for (Link = GetFirstNode (&HostBridge->RootBridges), Descriptor = Resources | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| for (Index = TypeIo; Index < TypeMax; Index++) { | |
| ResAllocNode = &RootBridge->ResAllocNode[Index]; | |
| Descriptor->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; | |
| Descriptor->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; | |
| Descriptor->AddrRangeMin = ResAllocNode->Base; | |
| Descriptor->AddrRangeMax = ResAllocNode->Alignment; | |
| Descriptor->AddrLen = ResAllocNode->Length; | |
| Descriptor->SpecificFlag = 0; | |
| switch (ResAllocNode->Type) { | |
| case TypeIo: | |
| Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_IO; | |
| break; | |
| case TypePMem32: | |
| Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE; | |
| case TypeMem32: | |
| Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; | |
| Descriptor->AddrSpaceGranularity = 32; | |
| break; | |
| case TypePMem64: | |
| Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE; | |
| case TypeMem64: | |
| Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; | |
| Descriptor->AddrSpaceGranularity = 64; | |
| break; | |
| case TypeBus: | |
| Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_BUS; | |
| break; | |
| default: | |
| break; | |
| } | |
| Descriptor++; | |
| } | |
| // | |
| // Terminate the root bridge resources. | |
| // | |
| End = (EFI_ACPI_END_TAG_DESCRIPTOR *) Descriptor; | |
| End->Desc = ACPI_END_TAG_DESCRIPTOR; | |
| End->Checksum = 0x0; | |
| Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) (End + 1); | |
| } | |
| // | |
| // Terminate the host bridge resources. | |
| // | |
| End = (EFI_ACPI_END_TAG_DESCRIPTOR *) Descriptor; | |
| End->Desc = ACPI_END_TAG_DESCRIPTOR; | |
| End->Checksum = 0x0; | |
| DEBUG ((DEBUG_ERROR, "Call PciHostBridgeResourceConflict().\n")); | |
| PciHostBridgeResourceConflict (HostBridge->Handle, Resources); | |
| FreePool (Resources); | |
| } | |
| /** | |
| Allocate Length of MMIO or IO resource with alignment BitsOfAlignment | |
| from GCD range [BaseAddress, Limit). | |
| @param Mmio TRUE for MMIO and FALSE for IO. | |
| @param Length Length of the resource to allocate. | |
| @param BitsOfAlignment Alignment of the resource to allocate. | |
| @param BaseAddress The starting address the allocation is from. | |
| @param Limit The ending address the allocation is to. | |
| @retval The base address of the allocated resource or MAX_UINT64 if allocation | |
| fails. | |
| **/ | |
| UINT64 | |
| AllocateResource ( | |
| BOOLEAN Mmio, | |
| UINT64 Length, | |
| UINTN BitsOfAlignment, | |
| UINT64 BaseAddress, | |
| UINT64 Limit | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| if (BaseAddress < Limit) { | |
| // | |
| // Have to make sure Aligment is handled since we are doing direct address allocation | |
| // | |
| BaseAddress = ALIGN_VALUE (BaseAddress, LShiftU64 (1, BitsOfAlignment)); | |
| while (BaseAddress + Length <= Limit + 1) { | |
| if (Mmio) { | |
| Status = gDS->AllocateMemorySpace ( | |
| EfiGcdAllocateAddress, | |
| EfiGcdMemoryTypeMemoryMappedIo, | |
| BitsOfAlignment, | |
| Length, | |
| &BaseAddress, | |
| gImageHandle, | |
| NULL | |
| ); | |
| } else { | |
| Status = gDS->AllocateIoSpace ( | |
| EfiGcdAllocateAddress, | |
| EfiGcdIoTypeIo, | |
| BitsOfAlignment, | |
| Length, | |
| &BaseAddress, | |
| gImageHandle, | |
| NULL | |
| ); | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| return BaseAddress; | |
| } | |
| BaseAddress += LShiftU64 (1, BitsOfAlignment); | |
| } | |
| } | |
| return MAX_UINT64; | |
| } | |
| /** | |
| Enter a certain phase of the PCI enumeration process. | |
| @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL instance. | |
| @param Phase The phase during enumeration. | |
| @retval EFI_SUCCESS Succeed. | |
| @retval EFI_INVALID_PARAMETER Wrong phase parameter passed in. | |
| @retval EFI_NOT_READY Resources have not been submitted yet. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| NotifyPhase ( | |
| IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, | |
| IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PHASE Phase | |
| ) | |
| { | |
| PCI_HOST_BRIDGE_INSTANCE *HostBridge; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| LIST_ENTRY *Link; | |
| EFI_PHYSICAL_ADDRESS BaseAddress; | |
| UINTN BitsOfAlignment; | |
| UINT64 Alignment; | |
| EFI_STATUS Status; | |
| EFI_STATUS ReturnStatus; | |
| PCI_RESOURCE_TYPE Index; | |
| PCI_RESOURCE_TYPE Index1; | |
| PCI_RESOURCE_TYPE Index2; | |
| BOOLEAN ResNodeHandled[TypeMax]; | |
| UINT64 MaxAlignment; | |
| HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); | |
| switch (Phase) { | |
| case EfiPciHostBridgeBeginEnumeration: | |
| if (!HostBridge->CanRestarted) { | |
| return EFI_NOT_READY; | |
| } | |
| // | |
| // Reset Root Bridge | |
| // | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| for (Index = TypeIo; Index < TypeMax; Index++) { | |
| RootBridge->ResAllocNode[Index].Type = Index; | |
| RootBridge->ResAllocNode[Index].Base = 0; | |
| RootBridge->ResAllocNode[Index].Length = 0; | |
| RootBridge->ResAllocNode[Index].Status = ResNone; | |
| RootBridge->ResourceSubmitted = FALSE; | |
| } | |
| } | |
| HostBridge->CanRestarted = TRUE; | |
| break; | |
| case EfiPciHostBridgeBeginBusAllocation: | |
| // | |
| // No specific action is required here, can perform any chipset specific programing | |
| // | |
| HostBridge->CanRestarted = FALSE; | |
| break; | |
| case EfiPciHostBridgeEndBusAllocation: | |
| // | |
| // No specific action is required here, can perform any chipset specific programing | |
| // | |
| break; | |
| case EfiPciHostBridgeBeginResourceAllocation: | |
| // | |
| // No specific action is required here, can perform any chipset specific programing | |
| // | |
| break; | |
| case EfiPciHostBridgeAllocateResources: | |
| ReturnStatus = EFI_SUCCESS; | |
| // | |
| // Make sure the resource for all root bridges has been submitted. | |
| // | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| if (!RootBridge->ResourceSubmitted) { | |
| return EFI_NOT_READY; | |
| } | |
| } | |
| DEBUG ((EFI_D_INFO, "PciHostBridge: NotifyPhase (AllocateResources)\n")); | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| for (Index = TypeIo; Index < TypeBus; Index++) { | |
| ResNodeHandled[Index] = FALSE; | |
| } | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| DEBUG ((EFI_D_INFO, " RootBridge: %s\n", RootBridge->DevicePathStr)); | |
| for (Index1 = TypeIo; Index1 < TypeBus; Index1++) { | |
| if (RootBridge->ResAllocNode[Index1].Status == ResNone) { | |
| ResNodeHandled[Index1] = TRUE; | |
| } else { | |
| // | |
| // Allocate the resource node with max alignment at first | |
| // | |
| MaxAlignment = 0; | |
| Index = TypeMax; | |
| for (Index2 = TypeIo; Index2 < TypeBus; Index2++) { | |
| if (ResNodeHandled[Index2]) { | |
| continue; | |
| } | |
| if (MaxAlignment <= RootBridge->ResAllocNode[Index2].Alignment) { | |
| MaxAlignment = RootBridge->ResAllocNode[Index2].Alignment; | |
| Index = Index2; | |
| } | |
| } | |
| ASSERT (Index < TypeMax); | |
| ResNodeHandled[Index] = TRUE; | |
| Alignment = RootBridge->ResAllocNode[Index].Alignment; | |
| BitsOfAlignment = LowBitSet64 (Alignment + 1); | |
| BaseAddress = MAX_UINT64; | |
| switch (Index) { | |
| case TypeIo: | |
| BaseAddress = AllocateResource ( | |
| FALSE, | |
| RootBridge->ResAllocNode[Index].Length, | |
| MIN (15, BitsOfAlignment), | |
| ALIGN_VALUE (RootBridge->Io.Base, Alignment + 1), | |
| RootBridge->Io.Limit | |
| ); | |
| break; | |
| case TypeMem64: | |
| BaseAddress = AllocateResource ( | |
| TRUE, | |
| RootBridge->ResAllocNode[Index].Length, | |
| MIN (63, BitsOfAlignment), | |
| ALIGN_VALUE (RootBridge->MemAbove4G.Base, Alignment + 1), | |
| RootBridge->MemAbove4G.Limit | |
| ); | |
| if (BaseAddress != MAX_UINT64) { | |
| break; | |
| } | |
| // | |
| // If memory above 4GB is not available, try memory below 4GB | |
| // | |
| case TypeMem32: | |
| BaseAddress = AllocateResource ( | |
| TRUE, | |
| RootBridge->ResAllocNode[Index].Length, | |
| MIN (31, BitsOfAlignment), | |
| ALIGN_VALUE (RootBridge->Mem.Base, Alignment + 1), | |
| RootBridge->Mem.Limit | |
| ); | |
| break; | |
| case TypePMem64: | |
| BaseAddress = AllocateResource ( | |
| TRUE, | |
| RootBridge->ResAllocNode[Index].Length, | |
| MIN (63, BitsOfAlignment), | |
| ALIGN_VALUE (RootBridge->PMemAbove4G.Base, Alignment + 1), | |
| RootBridge->PMemAbove4G.Limit | |
| ); | |
| if (BaseAddress != MAX_UINT64) { | |
| break; | |
| } | |
| // | |
| // If memory above 4GB is not available, try memory below 4GB | |
| // | |
| case TypePMem32: | |
| BaseAddress = AllocateResource ( | |
| TRUE, | |
| RootBridge->ResAllocNode[Index].Length, | |
| MIN (31, BitsOfAlignment), | |
| ALIGN_VALUE (RootBridge->PMem.Base, Alignment + 1), | |
| RootBridge->PMem.Limit | |
| ); | |
| break; | |
| default: | |
| ASSERT (FALSE); | |
| break; | |
| } | |
| DEBUG ((DEBUG_INFO, " %s: Base/Length/Alignment = %lx/%lx/%lx - ", | |
| mPciResourceTypeStr[Index], BaseAddress, RootBridge->ResAllocNode[Index].Length, Alignment)); | |
| if (BaseAddress != MAX_UINT64) { | |
| RootBridge->ResAllocNode[Index].Base = BaseAddress; | |
| RootBridge->ResAllocNode[Index].Status = ResAllocated; | |
| DEBUG ((DEBUG_INFO, "Success\n")); | |
| } else { | |
| ReturnStatus = EFI_OUT_OF_RESOURCES; | |
| DEBUG ((DEBUG_ERROR, "Out Of Resource!\n")); | |
| } | |
| } | |
| } | |
| } | |
| if (ReturnStatus == EFI_OUT_OF_RESOURCES) { | |
| ResourceConflict (HostBridge); | |
| } | |
| // | |
| // Set resource to zero for nodes where allocation fails | |
| // | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| for (Index = TypeIo; Index < TypeBus; Index++) { | |
| if (RootBridge->ResAllocNode[Index].Status != ResAllocated) { | |
| RootBridge->ResAllocNode[Index].Length = 0; | |
| } | |
| } | |
| } | |
| return ReturnStatus; | |
| case EfiPciHostBridgeSetResources: | |
| // | |
| // HostBridgeInstance->CanRestarted = FALSE; | |
| // | |
| break; | |
| case EfiPciHostBridgeFreeResources: | |
| // | |
| // HostBridgeInstance->CanRestarted = FALSE; | |
| // | |
| ReturnStatus = EFI_SUCCESS; | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| for (Index = TypeIo; Index < TypeBus; Index++) { | |
| if (RootBridge->ResAllocNode[Index].Status == ResAllocated) { | |
| switch (Index) { | |
| case TypeIo: | |
| Status = gDS->FreeIoSpace (RootBridge->ResAllocNode[Index].Base, RootBridge->ResAllocNode[Index].Length); | |
| if (EFI_ERROR (Status)) { | |
| ReturnStatus = Status; | |
| } | |
| break; | |
| case TypeMem32: | |
| case TypePMem32: | |
| case TypeMem64: | |
| case TypePMem64: | |
| Status = gDS->FreeMemorySpace (RootBridge->ResAllocNode[Index].Base, RootBridge->ResAllocNode[Index].Length); | |
| if (EFI_ERROR (Status)) { | |
| ReturnStatus = Status; | |
| } | |
| break; | |
| default: | |
| ASSERT (FALSE); | |
| break; | |
| } | |
| RootBridge->ResAllocNode[Index].Type = Index; | |
| RootBridge->ResAllocNode[Index].Base = 0; | |
| RootBridge->ResAllocNode[Index].Length = 0; | |
| RootBridge->ResAllocNode[Index].Status = ResNone; | |
| } | |
| } | |
| RootBridge->ResourceSubmitted = FALSE; | |
| } | |
| HostBridge->CanRestarted = TRUE; | |
| return ReturnStatus; | |
| case EfiPciHostBridgeEndResourceAllocation: | |
| // | |
| // The resource allocation phase is completed. No specific action is required | |
| // here. This notification can be used to perform any chipset specific programming. | |
| // | |
| break; | |
| case EfiPciHostBridgeEndEnumeration: | |
| // | |
| // The Host Bridge Enumeration is completed. No specific action is required here. | |
| // This notification can be used to perform any chipset specific programming. | |
| // | |
| break; | |
| default: | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Return the device handle of the next PCI root bridge that is associated with | |
| this Host Bridge. | |
| @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. | |
| @param RootBridgeHandle Returns the device handle of the next PCI Root Bridge. | |
| On input, it holds the RootBridgeHandle returned by the most | |
| recent call to GetNextRootBridge().The handle for the first | |
| PCI Root Bridge is returned if RootBridgeHandle is NULL on input. | |
| @retval EFI_SUCCESS Succeed. | |
| @retval EFI_NOT_FOUND Next PCI root bridge not found. | |
| @retval EFI_INVALID_PARAMETER Wrong parameter passed in. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GetNextRootBridge ( | |
| IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, | |
| IN OUT EFI_HANDLE *RootBridgeHandle | |
| ) | |
| { | |
| BOOLEAN ReturnNext; | |
| LIST_ENTRY *Link; | |
| PCI_HOST_BRIDGE_INSTANCE *HostBridge; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| if (RootBridgeHandle == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); | |
| ReturnNext = (BOOLEAN) (*RootBridgeHandle == NULL); | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| if (ReturnNext) { | |
| *RootBridgeHandle = RootBridge->Handle; | |
| return EFI_SUCCESS; | |
| } | |
| ReturnNext = (BOOLEAN) (*RootBridgeHandle == RootBridge->Handle); | |
| } | |
| if (ReturnNext) { | |
| ASSERT (IsNull (&HostBridge->RootBridges, Link)); | |
| return EFI_NOT_FOUND; | |
| } else { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| } | |
| /** | |
| Returns the attributes of a PCI Root Bridge. | |
| @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. | |
| @param RootBridgeHandle The device handle of the PCI Root Bridge | |
| that the caller is interested in. | |
| @param Attributes The pointer to attributes of the PCI Root Bridge. | |
| @retval EFI_SUCCESS Succeed. | |
| @retval EFI_INVALID_PARAMETER Attributes parameter passed in is NULL or | |
| RootBridgeHandle is not an EFI_HANDLE | |
| that was returned on a previous call to | |
| GetNextRootBridge(). | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GetAttributes ( | |
| IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, | |
| IN EFI_HANDLE RootBridgeHandle, | |
| OUT UINT64 *Attributes | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| PCI_HOST_BRIDGE_INSTANCE *HostBridge; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| if (Attributes == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| if (RootBridgeHandle == RootBridge->Handle) { | |
| *Attributes = RootBridge->AllocationAttributes; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /** | |
| This is the request from the PCI enumerator to set up | |
| the specified PCI Root Bridge for bus enumeration process. | |
| @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. | |
| @param RootBridgeHandle The PCI Root Bridge to be set up. | |
| @param Configuration Pointer to the pointer to the PCI bus resource descriptor. | |
| @retval EFI_SUCCESS Succeed. | |
| @retval EFI_OUT_OF_RESOURCES Not enough pool to be allocated. | |
| @retval EFI_INVALID_PARAMETER RootBridgeHandle is not a valid handle. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| StartBusEnumeration ( | |
| IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, | |
| IN EFI_HANDLE RootBridgeHandle, | |
| OUT VOID **Configuration | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| PCI_HOST_BRIDGE_INSTANCE *HostBridge; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; | |
| EFI_ACPI_END_TAG_DESCRIPTOR *End; | |
| if (Configuration == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| if (RootBridgeHandle == RootBridge->Handle) { | |
| *Configuration = AllocatePool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); | |
| if (*Configuration == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) *Configuration; | |
| Descriptor->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; | |
| Descriptor->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; | |
| Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_BUS; | |
| Descriptor->GenFlag = 0; | |
| Descriptor->SpecificFlag = 0; | |
| Descriptor->AddrSpaceGranularity = 0; | |
| Descriptor->AddrRangeMin = RootBridge->Bus.Base; | |
| Descriptor->AddrRangeMax = 0; | |
| Descriptor->AddrTranslationOffset = 0; | |
| Descriptor->AddrLen = RootBridge->Bus.Limit - RootBridge->Bus.Base + 1; | |
| End = (EFI_ACPI_END_TAG_DESCRIPTOR *) (Descriptor + 1); | |
| End->Desc = ACPI_END_TAG_DESCRIPTOR; | |
| End->Checksum = 0x0; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /** | |
| This function programs the PCI Root Bridge hardware so that | |
| it decodes the specified PCI bus range. | |
| @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. | |
| @param RootBridgeHandle The PCI Root Bridge whose bus range is to be programmed. | |
| @param Configuration The pointer to the PCI bus resource descriptor. | |
| @retval EFI_SUCCESS Succeed. | |
| @retval EFI_INVALID_PARAMETER Wrong parameters passed in. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SetBusNumbers ( | |
| IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, | |
| IN EFI_HANDLE RootBridgeHandle, | |
| IN VOID *Configuration | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| PCI_HOST_BRIDGE_INSTANCE *HostBridge; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; | |
| EFI_ACPI_END_TAG_DESCRIPTOR *End; | |
| if (Configuration == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; | |
| End = (EFI_ACPI_END_TAG_DESCRIPTOR *) (Descriptor + 1); | |
| // | |
| // Check the Configuration is valid | |
| // | |
| if ((Descriptor->Desc != ACPI_ADDRESS_SPACE_DESCRIPTOR) || | |
| (Descriptor->ResType != ACPI_ADDRESS_SPACE_TYPE_BUS) || | |
| (End->Desc != ACPI_END_TAG_DESCRIPTOR) | |
| ) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| if (RootBridgeHandle == RootBridge->Handle) { | |
| if (Descriptor->AddrLen == 0) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if ((Descriptor->AddrRangeMin < RootBridge->Bus.Base) || | |
| (Descriptor->AddrRangeMin + Descriptor->AddrLen - 1 > RootBridge->Bus.Limit) | |
| ) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Update the Bus Range | |
| // | |
| RootBridge->ResAllocNode[TypeBus].Base = Descriptor->AddrRangeMin; | |
| RootBridge->ResAllocNode[TypeBus].Length = Descriptor->AddrLen; | |
| RootBridge->ResAllocNode[TypeBus].Status = ResAllocated; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /** | |
| Submits the I/O and memory resource requirements for the specified PCI Root Bridge. | |
| @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. | |
| @param RootBridgeHandle The PCI Root Bridge whose I/O and memory resource requirements. | |
| are being submitted. | |
| @param Configuration The pointer to the PCI I/O and PCI memory resource descriptor. | |
| @retval EFI_SUCCESS Succeed. | |
| @retval EFI_INVALID_PARAMETER Wrong parameters passed in. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SubmitResources ( | |
| IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, | |
| IN EFI_HANDLE RootBridgeHandle, | |
| IN VOID *Configuration | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| PCI_HOST_BRIDGE_INSTANCE *HostBridge; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; | |
| PCI_RESOURCE_TYPE Type; | |
| // | |
| // Check the input parameter: Configuration | |
| // | |
| if (Configuration == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| if (RootBridgeHandle == RootBridge->Handle) { | |
| DEBUG ((EFI_D_INFO, "PciHostBridge: SubmitResources for %s\n", RootBridge->DevicePathStr)); | |
| // | |
| // Check the resource descriptors. | |
| // If the Configuration includes one or more invalid resource descriptors, all the resource | |
| // descriptors are ignored and the function returns EFI_INVALID_PARAMETER. | |
| // | |
| for (Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) { | |
| if (Descriptor->ResType > ACPI_ADDRESS_SPACE_TYPE_BUS) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((EFI_D_INFO, " %s: Granularity/SpecificFlag = %ld / %02x%s\n", | |
| mAcpiAddressSpaceTypeStr[Descriptor->ResType], Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag, | |
| (Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0 ? L" (Prefetchable)" : L"" | |
| )); | |
| DEBUG ((EFI_D_INFO, " Length/Alignment = 0x%lx / 0x%lx\n", Descriptor->AddrLen, Descriptor->AddrRangeMax)); | |
| switch (Descriptor->ResType) { | |
| case ACPI_ADDRESS_SPACE_TYPE_MEM: | |
| if (Descriptor->AddrSpaceGranularity != 32 && Descriptor->AddrSpaceGranularity != 64) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (Descriptor->AddrSpaceGranularity == 32 && Descriptor->AddrLen >= SIZE_4GB) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // If the PCI root bridge does not support separate windows for nonprefetchable and | |
| // prefetchable memory, then the PCI bus driver needs to include requests for | |
| // prefetchable memory in the nonprefetchable memory pool. | |
| // | |
| if (((RootBridge->AllocationAttributes & EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM) != 0) && | |
| ((Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0) | |
| ) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| case ACPI_ADDRESS_SPACE_TYPE_IO: | |
| // | |
| // Check aligment, it should be of the form 2^n-1 | |
| // | |
| if (GetPowerOfTwo64 (Descriptor->AddrRangeMax + 1) != (Descriptor->AddrRangeMax + 1)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| break; | |
| default: | |
| ASSERT (FALSE); | |
| break; | |
| } | |
| } | |
| if (Descriptor->Desc != ACPI_END_TAG_DESCRIPTOR) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| for (Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) { | |
| if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) { | |
| if (Descriptor->AddrSpaceGranularity == 32) { | |
| if ((Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0) { | |
| Type = TypePMem32; | |
| } else { | |
| Type = TypeMem32; | |
| } | |
| } else { | |
| ASSERT (Descriptor->AddrSpaceGranularity == 64); | |
| if ((Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0) { | |
| Type = TypePMem64; | |
| } else { | |
| Type = TypeMem64; | |
| } | |
| } | |
| } else { | |
| ASSERT (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_IO); | |
| Type = TypeIo; | |
| } | |
| RootBridge->ResAllocNode[Type].Length = Descriptor->AddrLen; | |
| RootBridge->ResAllocNode[Type].Alignment = Descriptor->AddrRangeMax; | |
| RootBridge->ResAllocNode[Type].Status = ResSubmitted; | |
| } | |
| RootBridge->ResourceSubmitted = TRUE; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /** | |
| This function returns the proposed resource settings for the specified | |
| PCI Root Bridge. | |
| @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. | |
| @param RootBridgeHandle The PCI Root Bridge handle. | |
| @param Configuration The pointer to the pointer to the PCI I/O | |
| and memory resource descriptor. | |
| @retval EFI_SUCCESS Succeed. | |
| @retval EFI_OUT_OF_RESOURCES Not enough pool to be allocated. | |
| @retval EFI_INVALID_PARAMETER RootBridgeHandle is not a valid handle. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GetProposedResources ( | |
| IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, | |
| IN EFI_HANDLE RootBridgeHandle, | |
| OUT VOID **Configuration | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| PCI_HOST_BRIDGE_INSTANCE *HostBridge; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| UINTN Index; | |
| UINTN Number; | |
| VOID *Buffer; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; | |
| EFI_ACPI_END_TAG_DESCRIPTOR *End; | |
| UINT64 ResStatus; | |
| HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| if (RootBridgeHandle == RootBridge->Handle) { | |
| for (Index = 0, Number = 0; Index < TypeBus; Index++) { | |
| if (RootBridge->ResAllocNode[Index].Status != ResNone) { | |
| Number++; | |
| } | |
| } | |
| Buffer = AllocateZeroPool (Number * sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); | |
| if (Buffer == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Buffer; | |
| for (Index = 0; Index < TypeBus; Index++) { | |
| ResStatus = RootBridge->ResAllocNode[Index].Status; | |
| if (ResStatus != ResNone) { | |
| Descriptor->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; | |
| Descriptor->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3;; | |
| Descriptor->GenFlag = 0; | |
| Descriptor->AddrRangeMin = RootBridge->ResAllocNode[Index].Base; | |
| Descriptor->AddrRangeMax = 0; | |
| Descriptor->AddrTranslationOffset = (ResStatus == ResAllocated) ? EFI_RESOURCE_SATISFIED : PCI_RESOURCE_LESS; | |
| Descriptor->AddrLen = RootBridge->ResAllocNode[Index].Length; | |
| switch (Index) { | |
| case TypeIo: | |
| Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_IO; | |
| break; | |
| case TypePMem32: | |
| Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE; | |
| case TypeMem32: | |
| Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; | |
| Descriptor->AddrSpaceGranularity = 32; | |
| break; | |
| case TypePMem64: | |
| Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE; | |
| case TypeMem64: | |
| Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; | |
| Descriptor->AddrSpaceGranularity = 64; | |
| break; | |
| } | |
| Descriptor++; | |
| } | |
| } | |
| End = (EFI_ACPI_END_TAG_DESCRIPTOR *) Descriptor; | |
| End->Desc = ACPI_END_TAG_DESCRIPTOR; | |
| End->Checksum = 0; | |
| *Configuration = Buffer; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| /** | |
| This function is called for all the PCI controllers that the PCI | |
| bus driver finds. Can be used to Preprogram the controller. | |
| @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. | |
| @param RootBridgeHandle The PCI Root Bridge handle. | |
| @param PciAddress Address of the controller on the PCI bus. | |
| @param Phase The Phase during resource allocation. | |
| @retval EFI_SUCCESS Succeed. | |
| @retval EFI_INVALID_PARAMETER RootBridgeHandle is not a valid handle. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PreprocessController ( | |
| IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, | |
| IN EFI_HANDLE RootBridgeHandle, | |
| IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS PciAddress, | |
| IN EFI_PCI_CONTROLLER_RESOURCE_ALLOCATION_PHASE Phase | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| PCI_HOST_BRIDGE_INSTANCE *HostBridge; | |
| PCI_ROOT_BRIDGE_INSTANCE *RootBridge; | |
| if ((UINT32) Phase > EfiPciBeforeResourceCollection) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); | |
| for (Link = GetFirstNode (&HostBridge->RootBridges) | |
| ; !IsNull (&HostBridge->RootBridges, Link) | |
| ; Link = GetNextNode (&HostBridge->RootBridges, Link) | |
| ) { | |
| RootBridge = ROOT_BRIDGE_FROM_LINK (Link); | |
| if (RootBridgeHandle == RootBridge->Handle) { | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_INVALID_PARAMETER; | |
| } |