最近在调试解决PCI设备相关的问题,然后对UEFI下面的这部分实现有了初步的了解,为了防止后面慢慢的又忘记了,所以还是决定将最近的收获都记录起来,各位读者如果发现哪里记录或者总结的不对,欢迎留言指正。
PciHostBridgeDxe
这个驱动是为pci bus驱动执行做前期准备的驱动,在这个驱动里面主要是建立相应的软件结构来描述Host主桥,以及注册一些protocol是为pci bus 驱动使用的。这里面Host主桥也就是所有pci 设备的根设备节点对应的设备。在我们平台上,Host主桥我们采用的是龙芯平台自己研发的7a1000桥片。这个桥片在uefi的pei阶段就已经初始化好,在桥片内部集成了很多PCI控制器。当pci设备扫描的时候,会跳过这个主桥(bus:0 dev:0 func:0) 按照深度优先的算法来依次扫描PCI设备(这里包含PCI桥)。
下面我们从这个驱动的入口函数开始分析:
驱动的入口函数:InitializePciHostBridge(入口函数在inf文件中写死的),这个驱动和标准的uefi驱动不一样,标准的驱动每一个都是对应一个硬件设备,然后软件上是区分support,start,stop函数的,这个驱动并没有这些。恰当的说,其实这个驱动是为后面的pcibus驱动提供软件上的服务。pci bus中很多函数最终都是调用到这个函数中注册的回调函数中。下面看一些这个入口函数都做了什么,其代码如下:
EFI_STATUS
EFIAPI
InitializePciHostBridge (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
PCI_HOST_BRIDGE_INSTANCE *HostBridge;
PCI_ROOT_BRIDGE_INSTANCE *RootBridge;
PCI_ROOT_BRIDGE *RootBridges;
UINTN RootBridgeCount;
UINTN Index;
PCI_ROOT_BRIDGE_APERTURE *MemApertures[4];
UINTN MemApertureIndex;
BOOLEAN ResourceAssigned;
LIST_ENTRY *Link;
RootBridges = PciHostBridgeGetRootBridges (&RootBridgeCount);
if ((RootBridges == NULL) || (RootBridgeCount == 0)) {
return EFI_UNSUPPORTED;
}
RootBridges->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)&mEfiPciRootBridgeDevicePath[0][0];
RootBridges->AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;
Status = gBS->LocateProtocol (&gEfiMetronomeArchProtocolGuid, NULL, (VOID **) &mMetronome);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (&gEfiCpuIo2ProtocolGuid, NULL, (VOID **) &mCpuIo);
ASSERT_EFI_ERROR (Status);
HostBridge = AllocateZeroPool (sizeof (PCI_HOST_BRIDGE_INSTANCE));
ASSERT (HostBridge != NULL);
HostBridge->Signature = PCI_HOST_BRIDGE_SIGNATURE;
HostBridge->CanRestarted = TRUE;
InitializeListHead (&HostBridge->RootBridges);
ResourceAssigned = FALSE;
//
// Create Root Bridge Device Handle in this Host Bridge
//
for (Index = 0; Index < RootBridgeCount; Index++) {
//
// Create Root Bridge Handle Instance
//
RootBridge = CreateRootBridge (&RootBridges[Index]);
ASSERT (RootBridge != NULL);
if (RootBridge == NULL) {
continue;
}
//
// Make sure all root bridges share the same ResourceAssigned value.
//
if (Index == 0) {
ResourceAssigned = RootBridges[Index].ResourceAssigned;
} else {
ASSERT (ResourceAssigned == RootBridges[Index].ResourceAssigned);
}
if (RootBridges[Index].Io.Base <= RootBridges[Index].Io.Limit) {
Status = gDS->AddIoSpace (
EfiGcdIoTypeIo,
RootBridges[Index].Io.Base,
RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1
);
ASSERT_EFI_ERROR (Status);
if (ResourceAssigned) {
Status = gDS->AllocateIoSpace (
EfiGcdAllocateAddress,
EfiGcdIoTypeIo,
0,
RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1,
&RootBridges[Index].Io.Base,
gImageHandle,
NULL
);
ASSERT_EFI_ERROR (Status);
}
}
//
// Add all the Mem/PMem aperture to GCD
// Mem/PMem shouldn't overlap with each other
// Root bridge which needs to combine MEM and PMEM should only report
// the MEM aperture in Mem
//
MemApertures[0] = &RootBridges[Index].Mem;
MemApertures[1] = &RootBridges[Index].MemAbove4G;
MemApertures[2] = &RootBridges[Index].PMem;
MemApertures[3] = &RootBridges[Index].PMemAbove4G;
for (MemApertureIndex = 0; MemApertureIndex < ARRAY_SIZE (MemApertures); MemApertureIndex++) {
if (MemApertures[MemApertureIndex]->Base <= MemApertures[MemApertureIndex]->Limit) {
Status = AddMemoryMappedIoSpace (
MemApertures[MemApertureIndex]->Base,
MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1,
EFI_MEMORY_UC
);
ASSERT_EFI_ERROR (Status);
Status = gDS->SetMemorySpaceAttributes (
MemApertures[MemApertureIndex]->Base,
MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1,
EFI_MEMORY_UC
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "PciHostBridge driver failed to set EFI_MEMORY_UC to MMIO aperture - %r.\n", Status));
}
if (ResourceAssigned) {
Status = gDS->AllocateMemorySpace (
EfiGcdAllocateAddress,
EfiGcdMemoryTypeMemoryMappedIo,
0,
MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1,
&MemApertures[MemApertureIndex]->Base,
gImageHandle,
NULL
);
ASSERT_EFI_ERROR (Status);
}
}
}
//
// Insert Root Bridge Handle Instance
//
InsertTailList (&HostBridge->RootBridges, &RootBridge->Link);
}
//
// When resources were assigned, it's not needed to expose
// PciHostBridgeResourceAllocation protocol.
//
if (!ResourceAssigned) {
HostBridge->ResAlloc.NotifyPhase = NotifyPhase;
HostBridge->ResAlloc.GetNextRootBridge = GetNextRootBridge;
HostBridge->ResAlloc.GetAllocAttributes = GetAttributes;
HostBridge->ResAlloc.StartBusEnumeration = StartBusEnumeration;
HostBridge->ResAlloc.SetBusNumbers = SetBusNumbers;
HostBridge->ResAlloc.SubmitResources = SubmitResources;
HostBridge->ResAlloc.GetProposedResources = GetProposedResources;
HostBridge->ResAlloc.PreprocessController = PreprocessController;
Status = gBS->InstallMultipleProtocolInterfaces (
&HostBridge->Handle,
&gEfiPciHostBridgeResourceAllocationProtocolGuid, &HostBridge->ResAlloc,
NULL
);
ASSERT_EFI_ERROR (Status);
}
for (Link = GetFirstNode (&HostBridge->RootBridges)
; !IsNull (&HostBridge->RootBridges, Link)
; Link = GetNextNode (&HostBridge->RootBridges, Link)
) {
RootBridge = ROOT_BRIDGE_FROM_LINK (Link);
RootBridge->RootBridgeIo.ParentHandle = HostBridge->Handle;
Status = gBS->InstallMultipleProtocolInterfaces (
&RootBridge->Handle,
&gEfiDevicePathProtocolGuid, RootBridge->DevicePath,
&gEfiPciRootBridgeIoProtocolGuid, &RootBridge->RootBridgeIo,
NULL
);
ASSERT_EFI_ERROR (Status);
}
PciHostBridgeFreeRootBridges (RootBridges, RootBridgeCount);
if (!EFI_ERROR (Status)) {
mIoMmuEvent = EfiCreateProtocolNotifyEvent (
&gEdkiiIoMmuProtocolGuid,
TPL_CALLBACK,
IoMmuProtocolCallback,
NULL,
&mIoMmuRegistration
);
}
return Status;
}
这个函数很长,这里不详细介绍每一断代码的作用,直接将重点的函数:
(1)PciHostBridgeGetRootBridges
代码如下:
PCI_ROOT_BRIDGE *
EFIAPI
PciHostBridgeGetRootBridges (
UINTN *Count
)
{
PCI_ROOT_BRIDGE *RootBridges;
*Count = 1;
RootBridges = AllocateZeroPool(sizeof (PCI_ROOT_BRIDGE));
if(RootBridges == NULL)
{
DEBUG ((EFI_D_ERROR, "RootBridges AllocateZeroPool error!\n"));
return NULL;
}
RootBridges->Attributes = (
EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO | EFI_PCI_ATTRIBUTE_ISA_IO | EFI_PCI_ATTRIBUTE_VGA_MEMORY | EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO | EFI_PCI_ATTRIBUTE
EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO |
EFI_PCI_ATTRIBUTE_ISA_IO_16 | EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16 | EFI_PCI_ATTRIBUTE_VGA_IO_16);
RootBridges->Supports = (
EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO | EFI_PCI_ATTRIBUTE_ISA_IO | EFI_PCI_ATTRIBUTE_VGA_MEMORY | EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO | EFI_PCI_ATTRIBUTE
EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO |
EFI_PCI_ATTRIBUTE_ISA_IO_16 | EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16 | EFI_PCI_ATTRIBUTE_VGA_IO_16);
RootBridges->DmaAbove4G = 1;
RootBridges->Bus.Base = 0;
RootBridges->Bus.Limit = 255;
RootBridges->Io.Base = 0x20000; //0xffffffffb8000000
DEBUG ((EFI_D_ERROR, "Io.Base 0x%llx\n",RootBridges->Io.Base));
RootBridges->Io.Limit = RootBridges->Io.Base + 0x2000000 ;
DEBUG ((EFI_D_ERROR, "Io.Limit 0x%llx\n",RootBridges->Io.Limit));
RootBridges->Mem.Base = 0x40000000; //0xffffffff40000000
DEBUG ((EFI_D_ERROR, "Mem.Base 0x%llx\n",RootBridges->Mem.Base));
RootBridges->Mem.Limit = RootBridges->Mem.Base + 0x40000000;
DEBUG ((EFI_D_ERROR, "Mem.Limit 0x%llx\n",RootBridges->Mem.Limit));
return RootBridges;
}
从函数实现可知,这里就是创建了一个PCI_ROOT_BRIDGE这个结构的数据,用这个结构来描述pci 设备的主桥,这个结构的定义如下:
typedef struct {
UINT32 Segment; ///< Segment number.
UINT64 Supports; ///< Supported attributes.
///< Refer to EFI_PCI_ATTRIBUTE_xxx used by GetAttributes()
///< and SetAttributes() in EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
UINT64 Attributes; ///< Initial attributes.
///< Refer to EFI_PCI_ATTRIBUTE_xxx used by GetAttributes()
///< and SetAttributes() in EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
BOOLEAN DmaAbove4G; ///< DMA above 4GB memory.
///< Set to TRUE when root bridge supports DMA above 4GB memory.
BOOLEAN NoExtendedConfigSpace; ///< When FALSE, the root bridge supports
///< Extended (4096-byte) Configuration Space.
///< When TRUE, the root bridge supports
///< 256-byte Configuration Space only.
BOOLEAN ResourceAssigned; ///< Resource assignment status of the root bridge.
///< Set to TRUE if Bus/IO/MMIO resources for root bridge have been assigned.
UINT64 AllocationAttributes; ///< Allocation attributes.
///< Refer to EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM and
///< EFI_PCI_HOST_BRIDGE_MEM64_DECODE used by GetAllocAttributes()
///< in EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL.
PCI_ROOT_BRIDGE_APERTURE Bus; ///< Bus aperture which can be used by the root bridge.
PCI_ROOT_BRIDGE_APERTURE Io; ///< IO aperture which can be used by the root bridge.
PCI_ROOT_BRIDGE_APERTURE Mem; ///< MMIO aperture below 4GB which can be used by the root bridge.
PCI_ROOT_BRIDGE_APERTURE MemAbove4G; ///< MMIO aperture above 4GB which can be used by the root bridge.
PCI_ROOT_BRIDGE_APERTURE PMem; ///< Prefetchable MMIO aperture below 4GB which can be used by the root bridge.
PCI_ROOT_BRIDGE_APERTURE PMemAbove4G; ///< Prefetchable MMIO aperture above 4GB which can be used by the root bridge.
EFI_DEVICE_PATH_PROTOCOL *DevicePath; ///< Device path.
} PCI_ROOT_BRIDGE;
//
// (Base > Limit) indicates an aperture is not available.
//
typedef struct {
//
// Base and Limit are the device address instead of host address when
// Translation is not zero
//
UINT64 Base;
UINT64 Limit;
//
// According to UEFI 2.7, Device Address = Host Address + Translation,
// so Translation = Device Address - Host Address.
// On platforms where Translation is not zero, the subtraction is probably to
// be performed with UINT64 wrap-around semantics, for we may translate an
// above-4G host address into a below-4G device address for legacy PCIe device
// compatibility.
//
// NOTE: The alignment of Translation is required to be larger than any BAR
// alignment in the same root bridge, so that the same alignment can be
// applied to both device address and host address, which simplifies the
// situation and makes the current resource allocation code in generic PCI
// host bridge driver still work.
//
UINT64 Translation;
} PCI_ROOT_BRIDGE_APERTURE;
从上面的代码可知,依次初始化了Bus中的Base,Limit;Io中Base,Limit和Mem中的Base,Limit。这几个值是至关重要的。Io中的Base值,决定者后面pci 设备中配置头中的Bar空间如果是Io空间,那么映射的基地址就是从这个地址开始的,Limit表示长度。Mem中的Base同样也是决定PCI设备映射的Bar空间的基地址是从这个地址开始的。这里面使用的是PCI总线的地址,具体如何转化到CPU可以访问的虚拟地址和具体的架构相关。这里我们使用的是MIPS架构龙芯平台,其他平台可能这里的地址就不同。通过这个函数,返回了一个PCI_ROOT_BRIDGE 类型的RootBridges,也就是描述主桥的结构,并初始化了其中重要的成员。其他成员都是在后面的代码中初始化的。
(2)通过代码
HostBridge = AllocateZeroPool (sizeof (PCI_HOST_BRIDGE_INSTANCE));
来申请一个PCI_HOST_BRIDGE_INSTANCE结构类型的成员变量HostBridge,这个HostBridge是所有pci设备软件结构的根节点。
PCI_HOST_BRIDGE_INSTANCE的定义如下:
typedef struct {
UINTN Signature;
EFI_HANDLE Handle;
LIST_ENTRY RootBridges;
BOOLEAN CanRestarted;
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL ResAlloc;
} PCI_HOST_BRIDGE_INSTANCE;
struct _EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL {
///
/// The notification from the PCI bus enumerator that it is about to enter
/// a certain phase during the enumeration process.
///
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL_NOTIFY_PHASE NotifyPhase;
///
/// Retrieves the device handle for the next PCI root bridge that is produced by the
/// host bridge to which this instance of the EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL is attached.
///
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL_GET_NEXT_ROOT_BRIDGE GetNextRootBridge;
///
/// Retrieves the allocation-related attributes of a PCI root bridge.
///
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL_GET_ATTRIBUTES GetAllocAttributes;
///
/// Sets up a PCI root bridge for bus enumeration.
///
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL_START_BUS_ENUMERATION StartBusEnumeration;
///
/// Sets up the PCI root bridge so that it decodes a specific range of bus numbers.
///
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL_SET_BUS_NUMBERS SetBusNumbers;
///
/// Submits the resource requirements for the specified PCI root bridge.
///
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL_SUBMIT_RESOURCES SubmitResources;
///
/// Returns the proposed resource assignment for the specified PCI root bridges.
///
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL_GET_PROPOSED_RESOURCES GetProposedResources;
///
/// Provides hooks from the PCI bus driver to every PCI controller
/// (device/function) at various stages of the PCI enumeration process that
/// allow the host bridge driver to preinitialize individual PCI controllers
/// before enumeration.
///
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL_PREPROCESS_CONTROLLER PreprocessController;
};
结构中LIST_ENTRY RootBridges;是一个链表,系统著所有的Host主桥以链表的形式链接在一起。(有的系统不只一个Host主桥,目前我们平台上只有一个)。
结构中EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL ResAlloc是一个回调函数结构根据上面的定义可知,里面包含了所有后面pci 设备扫描过程中用到的申请Bar空间地址等等的其他函数。
(3)RootBridge = CreateRootBridge (&RootBridges[Index]);
通过这段代码来创建了一个和Host Bridge同样结构的数据结构,然后将RootBridge插入到了HostBridge中的链表中。
CreateRootBridge的代码如下:
PCI_ROOT_BRIDGE_INSTANCE *
CreateRootBridge (
IN PCI_ROOT_BRIDGE *Bridge
)
{
PCI_ROOT_BRIDGE_INSTANCE *RootBridge;
PCI_RESOURCE_TYPE Index;
CHAR16 *DevicePathStr;
PCI_ROOT_BRIDGE_APERTURE *Aperture;
DevicePathStr = NULL;
DEBUG ((EFI_D_INFO, "RootBridge: "));
DEBUG ((EFI_D_INFO, "%s\n", DevicePathStr = ConvertDevicePathToText (Bridge->DevicePath, FALSE, FALSE)));
DEBUG ((EFI_D_INFO, " Support/Attr: %lx / %lx\n", Bridge->Supports, Bridge->Attributes));
DEBUG ((EFI_D_INFO, " DmaAbove4G: %s\n", Bridge->DmaAbove4G ? L"Yes" : L"No"));
DEBUG ((EFI_D_INFO, "NoExtConfSpace: %s\n", Bridge->NoExtendedConfigSpace ? L"Yes" : L"No"));
DEBUG ((EFI_D_INFO, " AllocAttr: %lx (%s%s)\n", Bridge->AllocationAttributes,
(Bridge->AllocationAttributes & EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM) != 0 ? L"CombineMemPMem " : L"",
(Bridge->AllocationAttributes & EFI_PCI_HOST_BRIDGE_MEM64_DECODE) != 0 ? L"Mem64Decode" : L""
));
DEBUG ((EFI_D_INFO, " Bus: %lx - %lx\n", Bridge->Bus.Base, Bridge->Bus.Limit));
DEBUG ((EFI_D_INFO, " Io: %lx - %lx\n", Bridge->Io.Base, Bridge->Io.Limit));
DEBUG ((EFI_D_INFO, " Mem: %lx - %lx\n", Bridge->Mem.Base, Bridge->Mem.Limit));
DEBUG ((EFI_D_INFO, " MemAbove4G: %lx - %lx\n", Bridge->MemAbove4G.Base, Bridge->MemAbove4G.Limit));
DEBUG ((EFI_D_INFO, " PMem: %lx - %lx\n", Bridge->PMem.Base, Bridge->PMem.Limit));
DEBUG ((EFI_D_INFO, " PMemAbove4G: %lx - %lx\n", Bridge->PMemAbove4G.Base, Bridge->PMemAbove4G.Limit));
if (!Bridge->ResourceAssigned) {
if ((Bridge->AllocationAttributes & EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM) != 0) {
//
// If this bit is set, then the PCI Root Bridge does not
// support separate windows for Non-prefetchable and Prefetchable
// memory.
//
ASSERT (Bridge->PMem.Base >= Bridge->PMem.Limit);
ASSERT (Bridge->PMemAbove4G.Base >= Bridge->PMemAbove4G.Limit);
if ((Bridge->PMem.Base < Bridge->PMem.Limit) ||
(Bridge->PMemAbove4G.Base < Bridge->PMemAbove4G.Limit)
) {
return NULL;
}
}
if ((Bridge->AllocationAttributes & EFI_PCI_HOST_BRIDGE_MEM64_DECODE) == 0) {
//
// If this bit is not set, then the PCI Root Bridge does not support
// 64 bit memory windows.
//
ASSERT (Bridge->MemAbove4G.Base >= Bridge->MemAbove4G.Limit);
ASSERT (Bridge->PMemAbove4G.Base >= Bridge->PMemAbove4G.Limit);
if ((Bridge->MemAbove4G.Base < Bridge->MemAbove4G.Limit) ||
(Bridge->PMemAbove4G.Base < Bridge->PMemAbove4G.Limit)
) {
return NULL;
}
}
}
RootBridge = AllocateZeroPool (sizeof (PCI_ROOT_BRIDGE_INSTANCE));
ASSERT (RootBridge != NULL);
RootBridge->Signature = PCI_ROOT_BRIDGE_SIGNATURE;
RootBridge->Supports = Bridge->Supports;
RootBridge->Attributes = Bridge->Attributes;
RootBridge->DmaAbove4G = Bridge->DmaAbove4G;
RootBridge->NoExtendedConfigSpace = Bridge->NoExtendedConfigSpace;
RootBridge->AllocationAttributes = Bridge->AllocationAttributes;
RootBridge->DevicePath = DuplicateDevicePath (Bridge->DevicePath);
RootBridge->DevicePathStr = DevicePathStr;
RootBridge->ConfigBuffer = AllocatePool (
TypeMax * sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)
);
ASSERT (RootBridge->ConfigBuffer != NULL);
InitializeListHead (&RootBridge->Maps);
CopyMem (&RootBridge->Bus, &Bridge->Bus, sizeof (PCI_ROOT_BRIDGE_APERTURE));
CopyMem (&RootBridge->Io, &Bridge->Io, sizeof (PCI_ROOT_BRIDGE_APERTURE));
CopyMem (&RootBridge->Mem, &Bridge->Mem, sizeof (PCI_ROOT_BRIDGE_APERTURE));
CopyMem (&RootBridge->MemAbove4G, &Bridge->MemAbove4G, sizeof (PCI_ROOT_BRIDGE_APERTURE));
CopyMem (&RootBridge->PMem, &Bridge->PMem, sizeof (PCI_ROOT_BRIDGE_APERTURE));
CopyMem (&RootBridge->PMemAbove4G, &Bridge->PMemAbove4G, sizeof (PCI_ROOT_BRIDGE_APERTURE));
for (Index = TypeIo; Index < TypeMax; Index++) {
switch (Index) {
case TypeBus:
Aperture = &RootBridge->Bus;
break;
case TypeIo:
Aperture = &RootBridge->Io;
break;
case TypeMem32:
Aperture = &RootBridge->Mem;
break;
case TypeMem64:
Aperture = &RootBridge->MemAbove4G;
break;
case TypeMem32:
Aperture = &RootBridge->Mem;
break;
case TypeMem64:
Aperture = &RootBridge->MemAbove4G;
break;
case TypePMem32:
Aperture = &RootBridge->PMem;
break;
case TypePMem64:
Aperture = &RootBridge->PMemAbove4G;
break;
default:
ASSERT (FALSE);
Aperture = NULL;
break;
}
RootBridge->ResAllocNode[Index].Type = Index;
if (Bridge->ResourceAssigned && (Aperture->Limit >= Aperture->Base)) {
RootBridge->ResAllocNode[Index].Base = Aperture->Base;
RootBridge->ResAllocNode[Index].Length = Aperture->Limit - Aperture->Base + 1;
RootBridge->ResAllocNode[Index].Status = ResAllocated;
} else {
RootBridge->ResAllocNode[Index].Base = 0;
RootBridge->ResAllocNode[Index].Length = 0;
RootBridge->ResAllocNode[Index].Status = ResNone;
}
}
RootBridge->RootBridgeIo.SegmentNumber = Bridge->Segment;
RootBridge->RootBridgeIo.PollMem = RootBridgeIoPollMem;
RootBridge->RootBridgeIo.PollIo = RootBridgeIoPollIo;
RootBridge->RootBridgeIo.Mem.Read = RootBridgeIoMemRead;
RootBridge->RootBridgeIo.Mem.Write = RootBridgeIoMemWrite;
RootBridge->RootBridgeIo.Io.Read = RootBridgeIoIoRead;
RootBridge->RootBridgeIo.Io.Write = RootBridgeIoIoWrite;
RootBridge->RootBridgeIo.CopyMem = RootBridgeIoCopyMem;
RootBridge->RootBridgeIo.Pci.Read = RootBridgeIoPciRead;
RootBridge->RootBridgeIo.Pci.Write = RootBridgeIoPciWrite;
RootBridge->RootBridgeIo.Map = RootBridgeIoMap;
RootBridge->RootBridgeIo.Unmap = RootBridgeIoUnmap;
RootBridge->RootBridgeIo.AllocateBuffer = RootBridgeIoAllocateBuffer;
RootBridge->RootBridgeIo.FreeBuffer = RootBridgeIoFreeBuffer;
RootBridge->RootBridgeIo.Flush = RootBridgeIoFlush;
RootBridge->RootBridgeIo.GetAttributes = RootBridgeIoGetAttributes;
RootBridge->RootBridgeIo.SetAttributes = RootBridgeIoSetAttributes;
RootBridge->RootBridgeIo.Configuration = RootBridgeIoConfiguration;
return RootBridge;
}
这个函数的主要作用就是创建RootBridge这个结构,然后注册结构中的回调函数,主要是RootBridgeIo中的回调函数,其实整个uefi中所有和pciio相关的函数,都是这部分注册的回调函数。
(4)注册pci bus中扫描设备过程中使用回调函数(包含,分配bus号,分配Bar空间的地址等回调函数)
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;
并注册gEfiPciHostBridgeResourceAllocationProtocolGuid和gEfiPciRootBridgeIoProtocolGuid这两个关键的protocol。到这里,这个函数的主要功能就完成了,主要就是注册了一些必要的回调函数,为后面pcibusdxe使用。我们这里不再详细介绍其中注册的每一个回调函数的功能,后面用到的时候再回过头来细说。
PciBusDxe
入口函数:
EFI_STATUS
EFIAPI
PciBusEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
//
// Initializes PCI devices pool
//
InitializePciDevicePool ();
//
// Install driver model protocol(s).
//
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gPciBusDriverBinding,
ImageHandle,
&gPciBusComponentName,
&gPciBusComponentName2
);
ASSERT_EFI_ERROR (Status);
if (FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) {
//
// If Hot Plug is supported, install EFI PCI Hot Plug Request protocol.
//
Handle = NULL;
Status = gBS->InstallProtocolInterface (
&Handle,
&gEfiPciHotPlugRequestProtocolGuid,
EFI_NATIVE_INTERFACE,
&mPciHotPlugRequest
);
}
return Status;
}
根据上面的函数可知,主要就是注册了gPciBusDriverBinding这个结构,里面包含了UEFI标准驱动所应该具有的support/start/stop函数。然后在遍历驱动的时候,如果support执行成功就会调用start函数。这部分代码是在DxeMain.c中的Dispatcher函数中执行的。详细不做介绍。InitializePciDevicePool ();这个函数很主要,其主要就是初始化一个全局的pci设备的链表。后面在扫描设备的额时候,发现一个设备就会将设备添加到LIST_ENTRY mPciDevicePool;这个全局的链表中。因此,这个全局变量包含了系统下所有的pci device。
PciBusDriverBindingSupported
代码如下:
EFI_STATUS
EFIAPI
PciBusDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
EFI_DEV_PATH_PTR Node;
//
// Check RemainingDevicePath validation
//
if (RemainingDevicePath != NULL) {
//
// Check if RemainingDevicePath is the End of Device Path Node,
// if yes, go on checking other conditions
//
if (!IsDevicePathEnd (RemainingDevicePath)) {
//
// If RemainingDevicePath isn't the End of Device Path Node,
// check its validation
//
Node.DevPath = RemainingDevicePath;
if (Node.DevPath->Type != HARDWARE_DEVICE_PATH ||
Node.DevPath->SubType != HW_PCI_DP ||
DevicePathNodeLength(Node.DevPath) != sizeof(PCI_DEVICE_PATH)) {
return EFI_UNSUPPORTED;
}
}
}
Status = gBS->OpenProtocol (
Controller,
&gEfiPciRootBridgeIoProtocolGuid,
(VOID **) &PciRootBridgeIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
if (EFI_ERROR (Status)) {
return Status;
}
//
// Close the I/O Abstraction(s) used to perform the supported test
//
gBS->CloseProtocol (
Controller,
&gEfiPciRootBridgeIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &ParentDevicePath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
if (EFI_ERROR (Status)) {
return Status;
}
//
// Close protocol, don't use device path protocol in the Support() function
//
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
return EFI_SUCCESS;
}
根据代码可知,suooprt函数的作用主要就是检查pciBus这个驱动需要使用的protocol是否都硬件install了。如果依赖的protocol没有install,这里就直接返回了。只有历依赖的protocol都install之后,才会去执行start函数。这里面依赖的protocol有gEfiDevicePathProtocolGuid,gEfiPciRootBridgeIoProtocolGuid,这两个是最主要的。其中gEfiPciRootBridgeIoProtocolGuid是后面Bus中所有的读取和写pci设备的函数都用的是这个protocol中的函数。
PciBusDriverBindingStart
代码如下:
EFIAPI
PciBusDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
//
// Initialize PciRootBridgeIo to suppress incorrect compiler warning.
//
PciRootBridgeIo = NULL;
//
// Check RemainingDevicePath validation
//
if (RemainingDevicePath != NULL) {
//
// Check if RemainingDevicePath is the End of Device Path Node,
// if yes, return EFI_SUCCESS
//
if (IsDevicePathEnd (RemainingDevicePath)) {
return EFI_SUCCESS;
}
}
gBS->LocateProtocol (
&gEfiIncompatiblePciDeviceSupportProtocolGuid,
NULL,
(VOID **) &gIncompatiblePciDeviceSupport
);
gPciPlatformProtocol = NULL;
gBS->LocateProtocol (
&gEfiPciPlatformProtocolGuid,
NULL,
(VOID **) &gPciPlatformProtocol
);
//
// If PCI Platform protocol doesn't exist, try to Pci Override Protocol.
//
if (gPciPlatformProtocol == NULL) {
gPciOverrideProtocol = NULL;
gBS->LocateProtocol (
&gEfiPciOverrideProtocolGuid,
NULL,
(VOID **) &gPciOverrideProtocol
);
}
if (mIoMmuProtocol == NULL) {
gBS->LocateProtocol (
&gEdkiiIoMmuProtocolGuid,
NULL,
(VOID **) &mIoMmuProtocol
);
}
if (PcdGetBool (PcdPciDisableBusEnumeration)) {
gFullEnumeration = FALSE;
} else {
gFullEnumeration = (BOOLEAN) ((SearchHostBridgeHandle (Controller) ? FALSE : TRUE));
}
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &ParentDevicePath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
ASSERT_EFI_ERROR (Status);
//
// Report Status Code to indicate PCI bus starts
//
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_PCI | EFI_IOB_PC_INIT),
ParentDevicePath
);
Status = EFI_SUCCESS;
//
// Enumerate the entire host bridge
// After enumeration, a database that records all the device information will be created
//
//
if (gFullEnumeration) {
//
// Get the rootbridge Io protocol to find the host bridge handle
//
Status = gBS->OpenProtocol (
Controller,
&gEfiPciRootBridgeIoProtocolGuid,
(VOID **) &PciRootBridgeIo,
gPciBusDriverBinding.DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
Status = PciEnumerator (Controller, PciRootBridgeIo->ParentHandle);
}
} else {
//
// If PCI bus has already done the full enumeration, never do it again
//
Status = PciEnumeratorLight (Controller);
}
if (EFI_ERROR (Status)) {
return Status;
}
//
// Start all the devices under the entire host bridge.
//
StartPciDevices (Controller);
if (gFullEnumeration) {
gFullEnumeration = FALSE;
Status = gBS->InstallProtocolInterface (
&PciRootBridgeIo->ParentHandle,
&gEfiPciEnumerationCompleteProtocolGuid,
EFI_NATIVE_INTERFACE,
NULL
);
ASSERT_EFI_ERROR (Status);
}
return Status;
}
这个函数的主要功能就是遍历系统下所有的bus号,以及其下面的pci设备。为其分配pci的Mem/Io空间。并使能该设备。
这里面涉及到几个主要的函数。
PciEnumerator
代码如下:
EFI_STATUS
PciEnumerator (
IN EFI_HANDLE Controller,
IN EFI_HANDLE HostBridgeHandle
)
{
EFI_STATUS Status;
EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc;
//
// Get the pci host bridge resource allocation protocol
//
Status = gBS->OpenProtocol (
HostBridgeHandle,
&gEfiPciHostBridgeResourceAllocationProtocolGuid,
(VOID **) &PciResAlloc,
gPciBusDriverBinding.DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Notify the pci bus enumeration is about to begin
//
Status = NotifyPhase (PciResAlloc, EfiPciHostBridgeBeginEnumeration);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Start the bus allocation phase
//
Status = PciHostBridgeEnumerator (PciResAlloc);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Submit the resource request
//
Status = PciHostBridgeResourceAllocator (PciResAlloc);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Notify the pci bus enumeration is about to complete
//
Status = NotifyPhase (PciResAlloc, EfiPciHostBridgeEndEnumeration);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Process P2C
//
Status = PciHostBridgeP2CProcess (PciResAlloc);
if (EFI_ERROR (Status)) {
return Status;
}
Status = PciHostBridgeDeviceAttribute (PciResAlloc);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
函数NotifyPhase其实就是HostBridge->ResAlloc.NotifyPhase = NotifyPhase;这里注册的这个函数,位于PCIHostBridge中的的代码。函数PciHostBridgeEnumerator,这里才开始去遍历所有的PCI设备。函数PciHostBridgeResourceAllocator 开始为设备分配所需要的PCI MEM、Io空间。在整个扫描过程中,这两个函数才是最主要的,也是最重要的。下面详细的看一下
PciHostBridgeEnumerator这个代码:
PciHostBridgeEnumerator
EFI_STATUS
PciHostBridgeEnumerator (
IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc
)
{
EFI_HANDLE RootBridgeHandle;
PCI_IO_DEVICE *RootBridgeDev;
EFI_STATUS Status;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
UINT16 MinBus;
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptors;
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Configuration;
UINT8 StartBusNumber;
LIST_ENTRY RootBridgeList;
LIST_ENTRY *Link;
if (FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) {
InitializeHotPlugSupport ();
}
InitializeListHead (&RootBridgeList);
//
// Notify the bus allocation phase is about to start
//
Status = NotifyPhase (PciResAlloc, EfiPciHostBridgeBeginBusAllocation);
if (EFI_ERROR (Status)) {
return Status;
}
DEBUG((EFI_D_INFO, "PCI Bus First Scanning\n"));
RootBridgeHandle = NULL;
while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) {
//
// if a root bridge instance is found, create root bridge device for it
//
RootBridgeDev = CreateRootBridge (RootBridgeHandle);
if (RootBridgeDev == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Enumerate all the buses under this root bridge
//
Status = PciRootBridgeEnumerator (
PciResAlloc,
RootBridgeDev
);
if (gPciHotPlugInit != NULL && FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) {
InsertTailList (&RootBridgeList, &(RootBridgeDev->Link));
} else {
DestroyRootBridge (RootBridgeDev);
}
if (EFI_ERROR (Status)) {
return Status;
}
}
NotifyPhase (PciResAlloc, EfiPciHostBridgeEndBusAllocation);
if (gPciHotPlugInit != NULL && FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) {
//
// Reset all assigned PCI bus number in all PPB
//
RootBridgeHandle = NULL;
Link = GetFirstNode (&RootBridgeList);
while ((PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) &&
(!IsNull (&RootBridgeList, Link))) {
RootBridgeDev = PCI_IO_DEVICE_FROM_LINK (Link);
//
// Get the Bus information
//
Status = PciResAlloc->StartBusEnumeration (
PciResAlloc,
RootBridgeHandle,
(VOID **) &Configuration
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get the bus number to start with
//
StartBusNumber = (UINT8) (Configuration->AddrRangeMin);
ResetAllPpbBusNumber (
RootBridgeDev,
StartBusNumber
);
FreePool (Configuration);
Link = RemoveEntryList (Link);
DestroyRootBridge (RootBridgeDev);
}
//
// Wait for all HPC initialized
//
Status = AllRootHPCInitialized (STALL_1_SECOND * 15);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Some root HPC failed to initialize\n"));
return Status;
}
//
// Notify the bus allocation phase is about to start for the 2nd time
//
Status = NotifyPhase (PciResAlloc, EfiPciHostBridgeBeginBusAllocation);
if (EFI_ERROR (Status)) {
return Status;
}
DEBUG((EFI_D_INFO, "PCI Bus Second Scanning\n"));
RootBridgeHandle = NULL;
while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) {
//
// if a root bridge instance is found, create root bridge device for it
//
RootBridgeDev = CreateRootBridge (RootBridgeHandle);
if (RootBridgeDev == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Enumerate all the buses under this root bridge
//
Status = PciRootBridgeEnumerator (
PciResAlloc,
RootBridgeDev
);
DestroyRootBridge (RootBridgeDev);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Notify the bus allocation phase is to end for the 2nd time
//
NotifyPhase (PciResAlloc, EfiPciHostBridgeEndBusAllocation);
}
//
// Notify the resource allocation phase is to start
//
Status = NotifyPhase (PciResAlloc, EfiPciHostBridgeBeginResourceAllocation);
if (EFI_ERROR (Status)) {
return Status;
}
RootBridgeHandle = NULL;
while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) {
//
// if a root bridge instance is found, create root bridge device for it
//
RootBridgeDev = CreateRootBridge (RootBridgeHandle);
if (RootBridgeDev == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = StartManagingRootBridge (RootBridgeDev);
if (EFI_ERROR (Status)) {
return Status;
}
PciRootBridgeIo = RootBridgeDev->PciRootBridgeIo;
Status = PciRootBridgeIo->Configuration (PciRootBridgeIo, (VOID **) &Descriptors);
if (EFI_ERROR (Status)) {
return Status;
}
Status = PciGetBusRange (&Descriptors, &MinBus, NULL, NULL);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Determine root bridge attribute by calling interface of Pcihostbridge
// protocol
//
DetermineRootBridgeAttributes (
PciResAlloc,
RootBridgeDev
);
//
// Collect all the resource information under this root bridge
// A database that records all the information about pci device subject to this
// root bridge will then be created
//
Status = PciPciDeviceInfoCollector (
RootBridgeDev,
(UINT8) MinBus
);
if (EFI_ERROR (Status)) {
return Status;
}
InsertRootBridge (RootBridgeDev);
//
// Record the hostbridge handle
//
AddHostBridgeEnumerator (RootBridgeDev->PciRootBridgeIo->ParentHandle);
}
return EFI_SUCCESS;
}
通过函数PciRootBridgeEnumerator调用PciScanBus,在函数PciScanBus内,遍历Device和Function
for(Device = 0,Device < 32,Device++)
{
for(Function =0,Function < 8,Function++)
{
status = PciDevicePresent()//读取PCI配置够等信息,看VendId 是否不等于0,或者不等于0xffff表示是个PCI设备
(1)如果返不是一个设备,并且Function =0,那么直接break,进行下一个device的扫描
(2)如果返回值错误,表示不是一个设备,那么继续下一个Function的扫描
(3)如果扫到了一个设备向下执行
Status = PciSearchDevice()//根据配置头信息,判断是桥设备还是PCI设备,然后初始化Bar空间的信息
(1)如果是正常的PCI设备调用函数PreprocessController处理(具体做什么不清楚)
(2)如果是桥设备调用函数PciSearchDevice()重新获取桥的配置头信息,然后调用函数PciAllocateBusNumber,
将遍历的Bus号加1,然后调用函数PreprocessController,再开始调用PciScanBus()扫描这个桥下面的设备。
}
}
注意:UEFI在扫描PCI设备的时候,从上到下依次扫描,扫描到一个设备后,就开始根据PCI配置头的信息开始分配BAR空间的基地址,大小和对其等寄存器。这个基地址是在PCIHOSTBRIDGE那个驱动中写死的Mem空间的基地址0x40000000开始分的,之后每一个设备都依次根据地址的大小和对其方式递增。具体详细的代码逻辑请看具体的代码。
PciHostBridgeResourceAllocator
代码如下:
EFI_STATUS
PciHostBridgeResourceAllocator (
IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc
)
{
PCI_IO_DEVICE *RootBridgeDev;
EFI_HANDLE RootBridgeHandle;
VOID *AcpiConfig;
EFI_STATUS Status;
UINT64 IoBase;
UINT64 Mem32Base;
UINT64 PMem32Base;
UINT64 Mem64Base;
UINT64 PMem64Base;
UINT64 IoResStatus;
UINT64 Mem32ResStatus;
UINT64 PMem32ResStatus;
UINT64 Mem64ResStatus;
UINT64 PMem64ResStatus;
UINT64 MaxOptionRomSize;
PCI_RESOURCE_NODE *IoBridge;
PCI_RESOURCE_NODE *Mem32Bridge;
PCI_RESOURCE_NODE *PMem32Bridge;
PCI_RESOURCE_NODE *Mem64Bridge;
PCI_RESOURCE_NODE *PMem64Bridge;
PCI_RESOURCE_NODE IoPool;
PCI_RESOURCE_NODE Mem32Pool;
PCI_RESOURCE_NODE PMem32Pool;
PCI_RESOURCE_NODE Mem64Pool;
PCI_RESOURCE_NODE PMem64Pool;
BOOLEAN ReAllocate;
EFI_DEVICE_HANDLE_EXTENDED_DATA_PAYLOAD HandleExtendedData;
EFI_RESOURCE_ALLOC_FAILURE_ERROR_DATA_PAYLOAD AllocFailExtendedData;
ReAllocate = FALSE;
//
// It may try several times if the resource allocation fails
//
while (TRUE) {
//
// Initialize resource pool
//
InitializeResourcePool (&IoPool, PciBarTypeIo16);
InitializeResourcePool (&Mem32Pool, PciBarTypeMem32);
InitializeResourcePool (&PMem32Pool, PciBarTypePMem32);
InitializeResourcePool (&Mem64Pool, PciBarTypeMem64);
InitializeResourcePool (&PMem64Pool, PciBarTypePMem64);
RootBridgeDev = NULL;
RootBridgeHandle = 0;
while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) {
//
// Get Root Bridge Device by handle
//
RootBridgeDev = GetRootBridgeByHandle (RootBridgeHandle);
if (RootBridgeDev == NULL) {
return EFI_NOT_FOUND;
}
IoBridge = CreateResourceNode (
RootBridgeDev,
0,
FeaturePcdGet (PcdPciBridgeIoAlignmentProbe) ? 0x1FF: 0xFFF,
RB_IO_RANGE,
PciBarTypeIo16,
PciResUsageTypical
);
Mem32Bridge = CreateResourceNode (
RootBridgeDev,
0,
0xFFFFF,
RB_MEM32_RANGE,
PciBarTypeMem32,
PciResUsageTypical
);
PMem32Bridge = CreateResourceNode (
RootBridgeDev,
0,
0xFFFFF,
RB_PMEM32_RANGE,
PciBarTypePMem32,
PciResUsageTypical
);
Mem64Bridge = CreateResourceNode (
RootBridgeDev,
0,
0xFFFFF,
RB_MEM64_RANGE,
PciBarTypeMem64,
PciResUsageTypical
);
PMem64Bridge = CreateResourceNode (
RootBridgeDev,
0,
0xFFFFF,
RB_PMEM64_RANGE,
PciBarTypePMem64,
PciResUsageTypical
);
//
// Create resourcemap by going through all the devices subject to this root bridge
//
CreateResourceMap (
RootBridgeDev,
IoBridge,
Mem32Bridge,
PMem32Bridge,
Mem64Bridge,
PMem64Bridge
);
//
// Get the max ROM size that the root bridge can process
//
RootBridgeDev->RomSize = Mem32Bridge->Length;
if (!ReAllocate) {
//
// Get Max Option Rom size for current root bridge
//
MaxOptionRomSize = GetMaxOptionRomSize (RootBridgeDev);
//
// Enlarger the mem32 resource to accomdate the option rom
// if the mem32 resource is not enough to hold the rom
//
if (MaxOptionRomSize > Mem32Bridge->Length) {
Mem32Bridge->Length = MaxOptionRomSize;
RootBridgeDev->RomSize = MaxOptionRomSize;
//
// Alignment should be adjusted as well
//
if (Mem32Bridge->Alignment < MaxOptionRomSize - 1) {
Mem32Bridge->Alignment = MaxOptionRomSize - 1;
}
}
}
Status = ConstructAcpiResourceRequestor (
RootBridgeDev,
IoBridge,
Mem32Bridge,
PMem32Bridge,
Mem64Bridge,
PMem64Bridge,
&AcpiConfig
);
//
// Insert these resource nodes into the database
//
InsertResourceNode (&IoPool, IoBridge);
InsertResourceNode (&Mem32Pool, Mem32Bridge);
InsertResourceNode (&PMem32Pool, PMem32Bridge);
InsertResourceNode (&Mem64Pool, Mem64Bridge);
InsertResourceNode (&PMem64Pool, PMem64Bridge);
if (Status == EFI_SUCCESS) {
//
// Submit the resource requirement
//
Status = PciResAlloc->SubmitResources (
PciResAlloc,
RootBridgeDev->Handle,
AcpiConfig
);
//
// If SubmitResources returns error, PciBus isn't able to start.
// It's a fatal error so assertion is added.
//
DEBUG ((EFI_D_INFO, "PciBus: HostBridge->SubmitResources() - %r\n", Status));
ASSERT_EFI_ERROR (Status);
}
if (AcpiConfig != NULL) {
FreePool (AcpiConfig);
}
if (EFI_ERROR (Status)) {
//
// Destroy all the resource tree
//
DestroyResourceTree (&IoPool);
DestroyResourceTree (&Mem32Pool);
DestroyResourceTree (&PMem32Pool);
DestroyResourceTree (&Mem64Pool);
DestroyResourceTree (&PMem64Pool);
return Status;
}
}
//
// End while, at least one Root Bridge should be found.
//
ASSERT (RootBridgeDev != NULL);
//
// Notify platform to start to program the resource
//
Status = NotifyPhase (PciResAlloc, EfiPciHostBridgeAllocateResources);
DEBUG ((EFI_D_INFO, "PciBus: HostBridge->NotifyPhase(AllocateResources) - %r\n", Status));
if (!FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) {
//
// If Hot Plug is not supported
//
if (EFI_ERROR (Status)) {
//
// Allocation failed, then return
//
return EFI_OUT_OF_RESOURCES;
}
//
// Allocation succeed.
// Get host bridge handle for status report, and then skip the main while
//
HandleExtendedData.Handle = RootBridgeDev->PciRootBridgeIo->ParentHandle;
break;
} else {
//
// If Hot Plug is supported
//
if (!EFI_ERROR (Status)) {
//
// Allocation succeed, then continue the following
//
break;
}
RootBridgeDev = NULL;
RootBridgeHandle = 0;
IoResStatus = EFI_RESOURCE_SATISFIED;
Mem32ResStatus = EFI_RESOURCE_SATISFIED;
PMem32ResStatus = EFI_RESOURCE_SATISFIED;
Mem64ResStatus = EFI_RESOURCE_SATISFIED;
PMem64ResStatus = EFI_RESOURCE_SATISFIED;
while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) {
//
// Get RootBridg Device by handle
//
RootBridgeDev = GetRootBridgeByHandle (RootBridgeHandle);
if (RootBridgeDev == NULL) {
return EFI_NOT_FOUND;
}
//
// Get host bridge handle for status report
//
HandleExtendedData.Handle = RootBridgeDev->PciRootBridgeIo->ParentHandle;
//
// Get acpi resource node for all the resource types
//
AcpiConfig = NULL;
Status = PciResAlloc->GetProposedResources (
PciResAlloc,
RootBridgeDev->Handle,
&AcpiConfig
);
if (EFI_ERROR (Status)) {
return Status;
}
if (AcpiConfig != NULL) {
//
// Adjust resource allocation policy for each RB
//
GetResourceAllocationStatus (
AcpiConfig,
&IoResStatus,
&Mem32ResStatus,
&PMem32ResStatus,
&Mem64ResStatus,
&PMem64ResStatus
);
FreePool (AcpiConfig);
}
}
ZeroMem (&AllocFailExtendedData, sizeof (AllocFailExtendedData));
REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
EFI_PROGRESS_CODE,
EFI_IO_BUS_PCI | EFI_IOB_EC_RESOURCE_CONFLICT,
(VOID *) &AllocFailExtendedData,
sizeof (AllocFailExtendedData)
);
Status = PciHostBridgeAdjustAllocation (
&IoPool,
&Mem32Pool,
&PMem32Pool,
&Mem64Pool,
&PMem64Pool,
IoResStatus,
Mem32ResStatus,
PMem32ResStatus,
Mem64ResStatus,
PMem64ResStatus
);
//
// Destroy all the resource tree
//
DestroyResourceTree (&IoPool);
DestroyResourceTree (&Mem32Pool);
DestroyResourceTree (&PMem32Pool);
DestroyResourceTree (&Mem64Pool);
DestroyResourceTree (&PMem64Pool);
NotifyPhase (PciResAlloc, EfiPciHostBridgeFreeResources);
if (EFI_ERROR (Status)) {
return Status;
}
ReAllocate = TRUE;
}
}
//
// End main while
//
//
// Raise the EFI_IOB_PCI_RES_ALLOC status code
//
REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
EFI_PROGRESS_CODE,
EFI_IO_BUS_PCI | EFI_IOB_PCI_RES_ALLOC,
(VOID *) &HandleExtendedData,
sizeof (HandleExtendedData)
);
//
// Notify pci bus driver starts to program the resource
//
Status = NotifyPhase (PciResAlloc, EfiPciHostBridgeSetResources);
if (EFI_ERROR (Status)) {
return Status;
}
RootBridgeDev = NULL;
RootBridgeHandle = 0;
while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) {
//
// Get RootBridg Device by handle
//
RootBridgeDev = GetRootBridgeByHandle (RootBridgeHandle);
if (RootBridgeDev == NULL) {
return EFI_NOT_FOUND;
}
//
// Get acpi resource node for all the resource types
//
AcpiConfig = NULL;
Status = PciResAlloc->GetProposedResources (
PciResAlloc,
RootBridgeDev->Handle,
&AcpiConfig
);
if (EFI_ERROR (Status)) {
return Status;
}
GetResourceBase (
AcpiConfig,
&IoBase,
&Mem32Base,
&PMem32Base,
&Mem64Base,
&PMem64Base
);
//
// Process option rom for this root bridge
//
ProcessOptionRom (RootBridgeDev, Mem32Base, RootBridgeDev->RomSize);
//
// Create the entire system resource map from the information collected by
// enumerator. Several resource tree was created
//
FindResourceNode (RootBridgeDev, &IoPool, &IoBridge);
FindResourceNode (RootBridgeDev, &Mem32Pool, &Mem32Bridge);
FindResourceNode (RootBridgeDev, &PMem32Pool, &PMem32Bridge);
FindResourceNode (RootBridgeDev, &Mem64Pool, &Mem64Bridge);
FindResourceNode (RootBridgeDev, &PMem64Pool, &PMem64Bridge);
ASSERT (IoBridge != NULL);
ASSERT (Mem32Bridge != NULL);
ASSERT (PMem32Bridge != NULL);
ASSERT (Mem64Bridge != NULL);
ASSERT (PMem64Bridge != NULL);
//
// Program IO resources
//
ProgramResource (
IoBase,
IoBridge
);
//
// Program Mem32 resources
//
ProgramResource (
Mem32Base,
Mem32Bridge
);
//
// Program PMem32 resources
//
ProgramResource (
PMem32Base,
PMem32Bridge
);
//
// Program Mem64 resources
//
ProgramResource (
Mem64Base,
Mem64Bridge
);
//
// Program PMem64 resources
//
ProgramResource (
PMem64Base,
PMem64Bridge
);
IoBridge ->PciDev->PciBar[IoBridge ->Bar].BaseAddress = IoBase;
Mem32Bridge ->PciDev->PciBar[Mem32Bridge ->Bar].BaseAddress = Mem32Base;
PMem32Bridge->PciDev->PciBar[PMem32Bridge->Bar].BaseAddress = PMem32Base;
Mem64Bridge ->PciDev->PciBar[Mem64Bridge ->Bar].BaseAddress = Mem64Base;
PMem64Bridge->PciDev->PciBar[PMem64Bridge->Bar].BaseAddress = PMem64Base;
//
// Dump the resource map for current root bridge
//
DEBUG_CODE (
PCI_RESOURCE_NODE *Resources[5];
Resources[0] = IoBridge;
Resources[1] = Mem32Bridge;
Resources[2] = PMem32Bridge;
Resources[3] = Mem64Bridge;
Resources[4] = PMem64Bridge;
DumpResourceMap (RootBridgeDev, Resources, ARRAY_SIZE (Resources));
);
FreePool (AcpiConfig);
}
//
// Destroy all the resource tree
//
DestroyResourceTree (&IoPool);
DestroyResourceTree (&Mem32Pool);
DestroyResourceTree (&PMem32Pool);
DestroyResourceTree (&Mem64Pool);
DestroyResourceTree (&PMem64Pool);
//
// Notify the resource allocation phase is to end
//
Status = NotifyPhase (PciResAlloc, EfiPciHostBridgeEndResourceAllocation);
return Status;
}