blob: d573e532bac822984f151cc9a388cd56300b2560 [file] [log] [blame]
/** @file
Provides the basic interfaces to abstract a PCI Host Bridge Resource Allocation.
Copyright (c) 1999 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "PciHostBridge.h"
#include "PciRootBridge.h"
#include "PciHostResource.h"
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 *mIoMmu;
EFI_EVENT mIoMmuEvent;
VOID *mIoMmuRegistration;
/**
This routine gets translation offset from a root bridge instance by resource type.
@param RootBridge The Root Bridge Instance for the resources.
@param ResourceType The Resource Type of the translation offset.
@retval The Translation Offset of the specified resource.
**/
UINT64
GetTranslationByResourceType (
IN PCI_ROOT_BRIDGE_INSTANCE *RootBridge,
IN PCI_RESOURCE_TYPE ResourceType
)
{
switch (ResourceType) {
case TypeIo:
return RootBridge->Io.Translation;
case TypeMem32:
return RootBridge->Mem.Translation;
case TypePMem32:
return RootBridge->PMem.Translation;
case TypeMem64:
return RootBridge->MemAbove4G.Translation;
case TypePMem64:
return RootBridge->PMemAbove4G.Translation;
case TypeBus:
return RootBridge->Bus.Translation;
default:
ASSERT (FALSE);
return 0;
}
}
/**
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) ? DEBUG_ERROR : DEBUG_VERBOSE,
"%a: %a: add [%Lx, %Lx): %r\n",
gEfiCallerBaseName,
__func__,
IntersectionBase,
IntersectionEnd,
Status
));
return Status;
}
DEBUG ((
DEBUG_ERROR,
"%a: %a: desc [%Lx, %Lx) type %u conflicts with "
"aperture [%Lx, %Lx)\n",
gEfiCallerBaseName,
__func__,
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 ((
DEBUG_ERROR,
"%a: %a: GetIoSpaceMap(): %r\n",
gEfiCallerBaseName,
__func__,
Status
));
return Status;
}
for (Index = 0; Index < NumberOfDescriptors; Index++) {
Status = IntersectIoDescriptor (Base, Length, &IoSpaceMap[Index]);
if (EFI_ERROR (Status)) {
goto FreeIoSpaceMap;
}
}
DEBUG_CODE_BEGIN ();
//
// 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);
}
DEBUG_CODE_END ();
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) ? DEBUG_ERROR : DEBUG_VERBOSE,
"%a: %a: add [%Lx, %Lx): %r\n",
gEfiCallerBaseName,
__func__,
IntersectionBase,
IntersectionEnd,
Status
));
return Status;
}
DEBUG ((
DEBUG_ERROR,
"%a: %a: desc [%Lx, %Lx) type %u cap %Lx conflicts "
"with aperture [%Lx, %Lx) cap %Lx\n",
gEfiCallerBaseName,
__func__,
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 ((
DEBUG_ERROR,
"%a: %a: GetMemorySpaceMap(): %r\n",
gEfiCallerBaseName,
__func__,
Status
));
return Status;
}
for (Index = 0; Index < NumberOfDescriptors; Index++) {
Status = IntersectMemoryDescriptor (
Base,
Length,
Capabilities,
&MemorySpaceMap[Index]
);
if (EFI_ERROR (Status)) {
goto FreeMemorySpaceMap;
}
}
DEBUG_CODE_BEGIN ();
//
// 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);
}
DEBUG_CODE_END ();
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 **)&mIoMmu);
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;
UINT64 HostAddress;
RootBridges = PciHostBridgeGetRootBridges (&RootBridgeCount);
if ((RootBridges == NULL) || (RootBridgeCount == 0)) {
return EFI_UNSUPPORTED;
}
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) {
//
// Base and Limit in PCI_ROOT_BRIDGE_APERTURE are device address.
// For GCD resource manipulation, we need to use host address.
//
HostAddress = TO_HOST_ADDRESS (
RootBridges[Index].Io.Base,
RootBridges[Index].Io.Translation
);
Status = AddIoSpace (
HostAddress,
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,
&HostAddress,
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) {
//
// Base and Limit in PCI_ROOT_BRIDGE_APERTURE are device address.
// For GCD resource manipulation, we need to use host address.
//
HostAddress = TO_HOST_ADDRESS (
MemApertures[MemApertureIndex]->Base,
MemApertures[MemApertureIndex]->Translation
);
Status = AddMemoryMappedIoSpace (
HostAddress,
MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1,
EFI_MEMORY_UC
);
ASSERT_EFI_ERROR (Status);
Status = gDS->SetMemorySpaceAttributes (
HostAddress,
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,
&HostAddress,
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
// Strictly speaking, alignment requirement should be applied to device
// address instead of host address which is used in GCD manipulation below,
// but as we restrict the alignment of Translation to be larger than any BAR
// alignment in the root bridge, we can simplify the situation and consider
// the same alignment requirement is also applied to host address.
//
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;
UINT64 Translation;
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 ((DEBUG_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 ((DEBUG_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;
//
// RESTRICTION: To simplify the situation, we require the alignment of
// Translation must be larger than any BAR alignment in the same root
// bridge, so that resource allocation alignment can be applied to
// both device address and host address.
//
Translation = GetTranslationByResourceType (RootBridge, Index);
if ((Translation & Alignment) != 0) {
DEBUG ((
DEBUG_ERROR,
"[%a:%d] Translation %lx is not aligned to %lx!\n",
__func__,
DEBUG_LINE_NUMBER,
Translation,
Alignment
));
ASSERT ((Translation & Alignment) == 0);
//
// This may be caused by too large alignment or too small
// Translation; pick the 1st possibility and return out of resource,
// which can also go thru the same process for out of resource
// outside the loop.
//
ReturnStatus = EFI_OUT_OF_RESOURCES;
continue;
}
switch (Index) {
case TypeIo:
//
// Base and Limit in PCI_ROOT_BRIDGE_APERTURE are device address.
// For AllocateResource is manipulating GCD resource, we need to use
// host address here.
//
BaseAddress = AllocateResource (
FALSE,
RootBridge->ResAllocNode[Index].Length,
MIN (15, BitsOfAlignment),
TO_HOST_ADDRESS (
ALIGN_VALUE (RootBridge->Io.Base, Alignment + 1),
RootBridge->Io.Translation
),
TO_HOST_ADDRESS (
RootBridge->Io.Limit,
RootBridge->Io.Translation
)
);
break;
case TypeMem64:
BaseAddress = AllocateResource (
TRUE,
RootBridge->ResAllocNode[Index].Length,
MIN (63, BitsOfAlignment),
TO_HOST_ADDRESS (
ALIGN_VALUE (RootBridge->MemAbove4G.Base, Alignment + 1),
RootBridge->MemAbove4G.Translation
),
TO_HOST_ADDRESS (
RootBridge->MemAbove4G.Limit,
RootBridge->MemAbove4G.Translation
)
);
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),
TO_HOST_ADDRESS (
ALIGN_VALUE (RootBridge->Mem.Base, Alignment + 1),
RootBridge->Mem.Translation
),
TO_HOST_ADDRESS (
RootBridge->Mem.Limit,
RootBridge->Mem.Translation
)
);
break;
case TypePMem64:
BaseAddress = AllocateResource (
TRUE,
RootBridge->ResAllocNode[Index].Length,
MIN (63, BitsOfAlignment),
TO_HOST_ADDRESS (
ALIGN_VALUE (RootBridge->PMemAbove4G.Base, Alignment + 1),
RootBridge->PMemAbove4G.Translation
),
TO_HOST_ADDRESS (
RootBridge->PMemAbove4G.Limit,
RootBridge->PMemAbove4G.Translation
)
);
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),
TO_HOST_ADDRESS (
ALIGN_VALUE (RootBridge->PMem.Base, Alignment + 1),
RootBridge->PMem.Translation
),
TO_HOST_ADDRESS (
RootBridge->PMem.Limit,
RootBridge->PMem.Translation
)
);
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 ((DEBUG_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 ((
DEBUG_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 ((DEBUG_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;
//
// AddrRangeMin in Resource Descriptor here should be device address
// instead of host address, or else PCI bus driver cannot set correct
// address into PCI BAR registers.
// Base in ResAllocNode is a host address, so conversion is needed.
//
Descriptor->AddrRangeMin = TO_DEVICE_ADDRESS (
RootBridge->ResAllocNode[Index].Base,
GetTranslationByResourceType (RootBridge, Index)
);
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;
}