UEFI——PCIe子系统(II) HostBridge&RootBridge初始化

在前面有关PCIe基础知识的介绍中中已经结合Spec详细的介绍了PCIe的空间情况、如何读取PCIe的配置空间以及读取出来的PCIe配置空间的含义。以上的介绍已经大致回答了之前提出的第二个问题:
利用CFC&CF8两个端口读取回来的信息具体含义是什么?
接下来我们开始第一个问题的分析整理:
PCIe设备的Bus Device Function号是如何分配的?

BDF的分配是UEFI在扫描 PCIe设备、分配资源、构建PCIe层级结构、最终生成完整的PCIe子系统这一系列操作中的一个小步骤。PCIe设备的扫描以及层级的构建是UEFI中一个非常重要的模块,因此单独孤立的看BDF的分配对于理解这个过程的帮助有限,因此接下来我会将PCIe子构建的整个流程按照代码顺序做一个简单的梳理。

PCIe相关概念

在结合代码进行深入的了解之前,首先需要了解的是一些PCIe相关的基础概念,这些概念是能够看懂代码的基础,也是实际常常能够用到的东西。

Root Complex (RC)

在这里插入图片描述

A defined System Element that includes at least one Host Bridge, Root Port, or Root Complex Integrated Endpoint. 1
一个被定义的系统元素,至少包括一个HostBridge,RootPort 或者 Root Complex Integrated Endpoint

总结

Pcie Spec中的说明并不是很精确,查阅了很多的资料之后个人对此的理解总结如下:

  1. RootComplex是对一个实体的称呼,但是并不是固定的一个结构构成,不同公司、不同平台的RC的物理组成都是不同的。但是一般来说,将参与实现了CPU和PCIe子系统之间数据交换传递的组件统称为RootComplex
  2. 从逻辑结构上来说RootComplex一般是由HostBridge和若干个用来扩展 Root Port的PCIe Bridge 组成
  3. RC 主要的作用是将CPU和memory子系统与PCIe(子系统)层级结构连接起来。此处的连接指的是进行不同空间的地址转换,更具体的说就是将CPU域的地址与PCIe域的地址进行相互转换 具体如前述 PCIe基础知识
  4. RC是PCIe层级结构的起始点,如果将PCIe层级结构比作树,那么RC就是这个树的根

Port/RootPort

前面介绍RC的过程中就提到了root port的概念。要明确这一个概念首先先了解Port的意思 1

在这里插入图片描述

port

  1. 逻辑上,指组件和PCIe link之间的接口
  2. 物理上,指位于同一个芯片上的一组发射/接收器,这组发射接收器定义了一个link

更加详细的解释2
在这里插入图片描述

Port是PCIe 组成和链路之间的接口, 由发射器和接收器组成。
Upstream port 指的是指向RC的port,Downstream port是指向背离RC的port。
Endpoint 是 Upstream port ,RC port 是 Downstream port
Ingress Port是接收packet的port, Egress Port是发送packet的port

下面再来看root port的概念
在这里插入图片描述

Root Port是位于RC上的port,这些port通过相关联的虚拟PCI-PCI bridge 映射层级结构中的部分

总结

这样看起来比较抽象,可以综合上面的解释,在对root (complex) port进行一个说明
Root port就是位于RC上的连接RC与link的interface, 从物理结构上看是一对发射接收器,逻辑上是一个downstream port

HostBridge

RC中还提到了HostBridge,下面是PCIe Spec对HostBridge 的定义1
在这里插入图片描述

Host Bridge 是RC的一部分,用以连接CPU和PCIe层级结构

在PI Spec 3中对于Host Bridge 有更详细的描述
在这里插入图片描述

PCI host bridge
能够产生一个或者多个PCI Root Bridge的软件抽象。由一个host bus controller产生的所有的PCI bus都属于同一个coherency domain。 PCI Host Bridge 是一个抽象的概念,可能会由很多的硬件设备组成。大多数的系统都可以抽象成只有一个PCI Host Bridge的模型。当处理PCI 资源分配的时候这种软件层面的抽象是必须的,因为对一个PCI root bridge 的资源分配要依赖于另外的root bridge,并且在资源分配期间必须考虑所有相关的PCI Host Bridge

在这里插入图片描述

每一个PCI Host Bridge 都是由一个或者多个PCI root bridge组成,并且存在与PCI root bridge相关的硬件寄存器。这些寄存器控制 由PCI root bridge生成的root bus和所有这个root bus生成的子bus 申请的bus memory IO资源

总结

综合以上的描述,可以对host bridge 进行总结

  1. Host bridge是将RC中的部分结构进行软件抽象得到的概念,Host bridge就是RC中用来实现地址转化的结构
  2. Host bridge能够产生一个或者多个PCI root bridge,同一个Host Bridge产生的root bridge共享资源
  3. Host bridge会提供protocol用于管理Root Bridge

RootBridge

PI Spec中的介绍
在这里插入图片描述

Root Bridge
Root Bridge会产生root PCI bus。Root Bridge连接了root PCI bus和非PCI bus. 一个PCI host bridge 可能会有一个或者多个root bridge。对于同一个host bridge 下的 一个root bridge 的配置要依赖于其下其余的root bridge的情况

UEFI Spec中的介绍
在这里插入图片描述

PCI root bridge 能够产生物理PCI bus的芯片组组件。它也是一组PCI 设备的父设备,这些PCI设备共享PCI IO ,Memory, prefetchable memory区域。一个PCI host bus controller 由一个或者多个PCI root bridge组成

总结

综合以上可以得到对PCI root bridge 的总结

  1. Root beridge是由host bridge产生的连接root PCI bus和非PCI bus的桥
  2. Root beridge下的所有PCI device1共享PCI IO ,Memory, prefetchable memory区域

Bridge

前面说了Host Bridge Root Bridge,关于Bridge的具体含义也需要看一下
在这里插入图片描述

Bridge
定义的系统基本元素之一。
将PCI/PCI-X Segment (or PCIe Port) 与内部组件或者另外的PCI/PCI-X Segment (or PCIe Port)进行互联.
在RC或者Switch中虚拟的bridge必须使用使用规范中的配置接口进行描述

简单来说,PCI bridge 提供了以其他总线或者与PCIe总线的连接。实际上,我看到的比较常见的桥的功能就是扩展出一个PCI bus

Switch

在这里插入图片描述

Switch
定义的系统基本元素之一。它连接了两个或者多个port,允许 packet从一个port路由到另一个。
对于软件来说,switch可以看做虚拟的P2Pbridge的集合

从上述我们可以总结

  1. switch是用来实现数据在不同port之间转换的结构
  2. 我们把switch看成bridg的结合即可,不需要过多特殊考虑

综合

前面分散的说了很多概念,在实际的代码和应用中,除了了解这些概念更重要的是掌握这些概念之间的关系以及使用的情况。以EDKII代码为例,说一下我自己对这些概念的理解

HostBridge : RC的组成部分用来实现地址转化的结构,Host Bridge 和 RootBridge 由HostBus连接.这个Bus 是在 Chipset Component内部进行连接的,其在系统中的抽象就是Pci Host Bus Controller
RootBeidge : RC的组成部分,由HostBridge产生,其延伸出的就是 Pci Local Bus
RootPort : 位于RC上的连接 RC与下级PCI结构的 Port

还有需要补充的一点,在一个系统中,理论上可以存在多个HostBridge,实际上很多代码中,也是视为存在多个HostBridge进行处理的。但是目前为止起码我接触到的平台实际上还都是一个HostBridge的
前面说HostBridge能够产生RootBrodge,产生的RootBridge的数量是不确定的,可以是一个也可以是多个。一般来说,简单的桌面系统是一个RootBridge的,但是一般大型的服务器,都是存在多个RootBridge的

根据前面对于PCIe一些相关的概念的描述,可以得到一个包含了RC ,host bridge, root bridge switch的PCIe结构关系的示意图(个人理解)
在这里插入图片描述

PCIe系统的构建

在UEFI中,针对PCIe子系统的构建主要可以分成两个部分:

  1. HostBridge&RootBridge的结构体初始化
  2. PCIe设备的扫描与资源分配
    下面对结构体初始化的代码进行简单分析

HostBridge的初始化

前面已经说过HostBridge的具体实现情况是与平台相关的,不同平台的实现方法是不同的。但是在代码中,都是需要对HostBridge进行初始化并且构建相应的结构体描述。现在不讨论对于HostBridge的初始化,仅仅对后面构建相应的结构进行介绍,这一部分在 PciHostBridge.c 文件中。
!!!再次说明,不同平台对于HostBridge的实现情况是不同的,所以初始化、结构构建情况也是不同的,当前只是以EDKII中的代码作为示例
在详细了解初始化函数之前先看一下 inf 文件
在这里插入图片描述
可以看到这是一个DXE_DRIVER类型的driver,这种类型的driver在DXE的初始阶段执行,通常用来做硬件的初始化并且产生后续需要使用的protocol。入口函数为 InitializePciHostBridge
大致分析一下 InitializePciHostBridge函数

获取RootBridges信息

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;
  }

函数一开始就对当前HostBridge下的RootBridges进行了枚举,获取到了当前这个HostBridge下的所有的RootBridges的基础信息和RootBridges的数量。RootBridges为 PCI_ROOT_BRIDGE 类型的结构体,其定义如下

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;

这个地方需要说明两个问题

  1. EDKII中这个文件中的操作是基于系统只有一个HostBridge来进行的,这也是大部分系统的基本情况(目前我暂时没有遇到过多个HostBridge的)。但是这种假设是比较片面的。这种代码的书写方式也是不利于扩展的,实际上很多平台在这个位置都是对所有HostBridge先进行一个扫描,枚举出所有的HostBridge然后再依次对每一个HostBridge下面的RootBridge进行扫描。
  2. HostBridge硬件层面的初始化是PEI阶段初始化完成的,其基本信息用HOB保存下来,在DXE阶段解析对应的HOB就能获得HostBridge的信息了
  3. PciHostBridgeGetRootBridges在不同平台的实现完全不同。在EDKII中有Ovmf &Uefipayload的实现情况,感兴趣的可以自己详细研究一下,这两种实现是两个思路:Ovmf是扫描设备信息进行信息的获取(因为是虚拟机),而Uefipayload是根据PEI阶段传递的HOB进行信息的获取与构建
  4. 为了简单起见,以 UEFI中的PCI设备扫描及分配Mem/Io空间过程_allocatezeropool-CSDN博客 文章中的PciHostBridgeGetRootBridges 代码进行简单的介绍,所有PciHostBridgeGetRootBridges 的最终目的都是为了获取RootBridge的基本信息(这个函数和平台是高度相关的 所以不同平台上是不同的)
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 *RootBridges;结构体的部分成员,其中最重要的成员就是Bus ,IO ,Mem的 Base &limit。
Io.Base表示了这个root bridge的IO空间可分配的基地址,这个基地址就是我们前面说过的要写入Bar空间的基地址 只不过后面扫描出的设备多了,写入的地址需要累加变大就是了。Io.Limit表示了长度。Mem.Base和Mem.Limit同理

创建HostBridge Instance 结构体

接下来是根据已知的HostBridge信息创建结构体 PCI_HOST_BRIDGE_INSTANCE *HostBridge;

  //
  // 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;

此处仅对部分成员初始化了,未初始化的成员后面会进行相应的操作
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;

此结构体中三个关键的成员分别进行简单的说明

  1. EFI_HANDLE Handle: 此时没有对这个Handle进行初始化,后面会进行。此处的Handle和之前我们介绍的Handle一样,在软件层面表示了当前的HostBridge Controller,这个Handle同样会插入 handle database链表进行相应的维护
  2. LIST_ENTRY RootBridges 前面反复的说过,LIST_ENTRY是一个很明显的信号,表示这个位置能够扩展出一个双向链表。后面就会发现,此处在这个PCI_HOST_BRIDGE_INSTANCE 下,连接的是PCI_ROOT_BRIDGE_INSTANCE链表
  3. EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL ResAlloc 定义了EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL的函数,这些函数在后面扫描设备、申请空间的时候需要用到

创建RootBridge Instance结构体

接下来对当前HostBridge下的所有的RootBridge进行了遍历

 for (Index = 0; Index < RootBridgeCount; Index++) 

对每一个获得的RootBridge创建相应的Instance结构体

    //
    // Create Root Bridge Handle Instance
    //
    RootBridge = CreateRootBridge (&RootBridges[Index]);
    ASSERT (RootBridge != NULL);
    if (RootBridge == NULL) {
      continue;
    }

PCI_ROOT_BRIDGE_INSTANCE结构体如下

typedef struct {
  UINT32                             Signature;
  LIST_ENTRY                         Link;
  EFI_HANDLE                         Handle;
  UINT64                             AllocationAttributes;
  UINT64                             Attributes;
  UINT64                             Supports;
  PCI_RES_NODE                       ResAllocNode[TypeMax];
  PCI_ROOT_BRIDGE_APERTURE           Bus;
  PCI_ROOT_BRIDGE_APERTURE           Io;
  PCI_ROOT_BRIDGE_APERTURE           Mem;
  PCI_ROOT_BRIDGE_APERTURE           PMem;
  PCI_ROOT_BRIDGE_APERTURE           MemAbove4G;
  PCI_ROOT_BRIDGE_APERTURE           PMemAbove4G;
  BOOLEAN                            DmaAbove4G;
  BOOLEAN                            NoExtendedConfigSpace;
  VOID                               *ConfigBuffer;
  EFI_DEVICE_PATH_PROTOCOL           *DevicePath;
  CHAR16                             *DevicePathStr;
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL    RootBridgeIo;

  BOOLEAN                            ResourceSubmitted;
  LIST_ENTRY                         Maps;
} PCI_ROOT_BRIDGE_INSTANCE;

部分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;
  // ... 
 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 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)) {
      //
      // Base in ResAllocNode is a host address, while Base in Aperture is a
      // device address.
      //
      RootBridge->ResAllocNode[Index].Base = TO_HOST_ADDRESS (
                                               Aperture->Base,
                                               Aperture->Translation
                                               );
      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;

在CreateRootBridge函数中可以看到PCI_ROOT_BRIDGE_INSTANCE结构体的绝大部分成员都是直接从PCI_ROOT_BRIDGE RootBridges 获得的,但是有一个位置的参数是经过了转换的

    RootBridge->ResAllocNode[Index].Type = Index;
    if (Bridge->ResourceAssigned && (Aperture->Limit >= Aperture->Base)) {
      //
      // Base in ResAllocNode is a host address, while Base in Aperture is a
      // device address.
      //
      RootBridge->ResAllocNode[Index].Base = TO_HOST_ADDRESS (
                                               Aperture->Base,
                                               Aperture->Translation
                                               );
      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;
    }
  }

这个位置实现的就是我们前面一直在说的PCI CPU域地址的转换。当前在 ResAllocNode结构体数组中保存的Base地址是Host域的地址,而Aperture 中的Base 表示的就是当前device 域中的地址
后面就是将RootBridge->RootBridgeIo 对应的函数都进行了初始化

接下来对所有的ResourceAssigned进行了统一 保证所有结构体中的ResourceAssigned是相同的值

    //
    // Make sure all root bridges share the same ResourceAssigned value.
    //
    if (Index == 0) {
      ResourceAssigned = RootBridges[Index].ResourceAssigned;
    } else {
      ASSERT (ResourceAssigned == RootBridges[Index].ResourceAssigned);
    }

下面的部分是针对现在有的信息将对应的结果添加到GCD中,这个部分没有详细的研究 先跳过这个部分

接下来 将初始化完成的RootBridge添加到HostBridge->RootBridges链表中。

    //
    // Insert Root Bridge Handle Instance
    //
    InsertTailList (&HostBridge->RootBridges, &RootBridge->Link);

然后安装 HostBridgeResourceAllocationProtocol到HostBridge Handle

  //
  // 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);
  }

在RootBridge Handle上安装对应的 Protocol

  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);
  }

这一段代码中在此遍历了创建好的RootBridge Instance结构体,并且在每个对应的Handle上都安装了DevicePathProtocolRootBridgeIoProtocol
最后free掉了最开始Get到的 PCI_ROOT_BRIDGE RootBridges数组

  PciHostBridgeFreeRootBridges (RootBridges, RootBridgeCount);

IOMMU

最后这个部分创建了一个与IOMMU相关的Event 当IoMmuProtocol被注册的时候这个函数就会被调用

  if (!EFI_ERROR (Status)) {
    mIoMmuEvent = EfiCreateProtocolNotifyEvent (
                    &gEdkiiIoMmuProtocolGuid,
                    TPL_CALLBACK,
                    IoMmuProtocolCallback,
                    NULL,
                    &mIoMmuRegistration
                    );
  }

当前不关心这个部分 跳过

总结

HostBridge初始化函数 InitializePciHostBridge主要完成的工作有以下几点

  1. 根据 前期传递得到or自己定义 的信息中获得RootBridge的基本信息
  2. 创建HostBridge Instance 结构体并且进行相应的初始化
  3. 创建RootBridge Instance 结构体并且进行相应的初始化,RootBridge的结构体中还包含了起始的资源信息可以被用在后面对设备进行资源的分配
    函数执行完成后创建的结构体和关系大致如下
    在这里插入图片描述

  1. PCI Local Bus Specification Revision 3.0 3.2.2.3.2 ↩︎ ↩︎ ↩︎

  2. PCIE System Architecture ↩︎

  3. Platform Initialization (PI) Specification 1.6 ↩︎

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值