edk2中MdeModulePkg\Bus\Pci路径下PciHostBridgeDxe的源码阅读,记录一下学习过程。PciHostBridgeDxe是DXE_DRIVER,即只提供软件服务,并不绑定硬件。这个驱动为后续PciBusDxe的执行提供root bridge instances相关数据结构。入口函数是InitializePciHostBridge,驱动主体代码如下。
/**
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;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. 首先是PciHostBridgeGetRootBridges,这个函数在不同的平台上的实现细节不一样,下边是其中一种,但目的都是返回一个包含所有root bridge instances的数组。接口是统一的,实现细节有差异。
/**
Return all the root bridge instances in an array.
@param Count Return the count of root bridge instances.
@return All the root bridge instances in an array.
The array should be passed into PciHostBridgeFreeRootBridges()
when it's not used.
**/
PCI_ROOT_BRIDGE *
EFIAPI
PciHostBridgeGetRootBridges (
UINTN *Count
)
{
return ScanForRootBridges (Count);
}
按照PCI spec,一个PCI总线域的Bus取值范围为 0 ~ 255,Dev 0 ~ 31, Func 0 ~ 7. ScanForRootBridges据此通过Bus/Dev/Func三层for循环轮询,挨个检查每个Func的configuration space的Vendor ID是否为FFFFh. 按照PCI spec定义FFFFh表示Func不存在。如果轮询发现一个Bus下至少存在一个Func, 则表示Root Bridge存在(有意义)。然后在RootBridges数组为这个Root Bridge分配并初始化一个PCI_ROOT_BRIDGE元素。说白了,其实就是用一个PCI_ROOT_BRIDGE structure数组来抽象所有的Root Bridges.
/**
Scan for all root bridges in platform.
@param[out] NumberOfRootBridges Number of root bridges detected
@retval Pointer to the allocated PCI_ROOT_BRIDGE structure array.
**/
PCI_ROOT_BRIDGE *
ScanForRootBridges (
OUT UINTN *NumberOfRootBridges
)
{
UINTN PrimaryBus;
UINTN SubBus;
UINT8 Device;
UINT8 Function;
UINTN NumberOfDevices;
UINTN Address;
PCI_TYPE01 Pci;
UINT64 Attributes;
UINT64 Base;
UINT64 Limit;
UINT64 Value;
PCI_ROOT_BRIDGE_APERTURE Io, Mem, MemAbove4G, PMem, PMemAbove4G, *MemAperture;
PCI_ROOT_BRIDGE *RootBridges;
UINTN BarOffsetEnd;
*NumberOfRootBridges = 0;
RootBridges = NULL;
//
// After scanning all the PCI devices on the PCI root bridge's primary bus,
// update the Primary Bus Number for the next PCI root bridge to be this PCI
// root bridge's subordinate bus number + 1.
//
for (PrimaryBus = 0; PrimaryBus <= PCI_MAX_BUS; PrimaryBus = SubBus + 1) {
SubBus = PrimaryBus;
Attributes = 0;
ZeroMem (&Io, sizeof (Io));
ZeroMem (&Mem, sizeof (Mem));
ZeroMem (&MemAbove4G, sizeof (MemAbove4G));
ZeroMem (&PMem, sizeof (PMem));
ZeroMem (&PMemAbove4G, sizeof (PMemAbove4G));
Io.Base = Mem.Base = MemAbove4G.Base = PMem.Base = PMemAbove4G.Base = MAX_UINT64;
//
// Scan all the PCI devices on the primary bus of the PCI root bridge
//
for (Device = 0, NumberOfDevices = 0; Device <= PCI_MAX_DEVICE; Device++) {
for (Function = 0; Function <= PCI_MAX_FUNC; Function++) {
//
// Compute the PCI configuration address of the PCI device to probe
//
Address = PCI_LIB_ADDRESS (PrimaryBus, Device, Function, 0);
//
// Read the Vendor ID from the PCI Configuration Header
//
if (PciRead16 (Address) == MAX_UINT16) {
if (Function == 0) {
//
// If the PCI Configuration Read fails, or a PCI device does not
// exist, then skip this entire PCI device
//
break;
} else {
//
// If PCI function != 0, VendorId == 0xFFFF, we continue to search
// PCI function.
//
continue;
}
}
//
// Read the entire PCI Configuration Header
//
PciReadBuffer (Address, sizeof (Pci), &Pci);
//
// Increment the number of PCI device found on the primary bus of the
// PCI root bridge
//
NumberOfDevices++;
...
} // End Func
} // End Dev
//
// If at least one PCI device was found on the primary bus of this PCI
// root bridge, then the PCI root bridge exists.
//
if (NumberOfDevices > 0) {
RootBridges = ReallocatePool (
(*NumberOfRootBridges) * sizeof (PCI_ROOT_BRIDGE),
(*NumberOfRootBridges + 1) * sizeof (PCI_ROOT_BRIDGE),
RootBridges
);
ASSERT (RootBridges != NULL);
AdjustRootBridgeResource (&Io, &Mem, &MemAbove4G, &PMem, &PMemAbove4G);
InitRootBridge (
Attributes, Attributes, 0,
(UINT8) PrimaryBus, (UINT8) SubBus,
&Io, &Mem, &MemAbove4G, &PMem, &PMemAbove4G,
&RootBridges[*NumberOfRootBridges]
);
RootBridges[*NumberOfRootBridges].ResourceAssigned = TRUE;
//
// Increment the index for the next PCI Root Bridge
//
(*NumberOfRootBridges)++;
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2. 因为一般大多数系统只含有一个Host Bridge, 驱动接下来初始化一个PCI_HOST_BRIDGE_INSTANCE结构变量,用以抽象Host Bridge.
然后,依照上边的PCI_ROOT_BRIDGE structure数组元素依次生成一个PCI_ROOT_BRIDGE_INSTANCE structure并初始化,将每个Root Bridge的IO/MEM资源记录到GCD.
最后将PCI_ROOT_BRIDGE_INSTANCE structure逐一添加到HostBridge->RootBridges链表。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3. 如果没有分配资源,则给Host Bridge安装gEfiPciHostBridgeResourceAllocationProtocolGuid.
轮询HostBridge->RootBridges,给每个节点(Root Bridge)安装gEfiDevicePathProtocolGuid和gEfiPciRootBridgeIoProtocolGuid.
释放PCI_ROOT_BRIDGE structure数组占用的资源。
如果正常则创建gEdkiiIoMmuProtocolGuid的NotifyEvent.