/** @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; | |
} |