UEFI中的PCI设备扫描及分配Mem/Io空间过程

最近在调试解决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;
}

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页