UEFI查找PCI设备

问题:在做项目中,BIOS如何获得PCI外插卡设备信息?

1、使用rootbridge 定位

1、进入OS查看PCI设备

huawei@huawei:~$ lspci -tv
 +-[0004:00]-+-00.0  Ampere Computing, LLC Device e110
 |           +-01.0-[01]--
 |           +-05.0-[02]--
 |           +-06.0-[03]--
 |           +-07.0-[04]--
 |           \-08.0-[05]--
 +-[0003:00]-+-00.0  Ampere Computing, LLC Device e110
 |           +-01.0-[01]----00.0  Renesas Technology Corp. uPD720201 USB 3.0 Host Controller
 |           +-03.0-[02]--
 |           +-05.0-[03]--
 |           \-07.0-[04]--
 +-[0002:00]-+-00.0  Ampere Computing, LLC Device e110
 |           +-01.0-[01]--
 |           +-03.0-[02]--
 |           +-05.0-[03]--
 |           \-07.0-[04]--
 +-[0001:00]-+-00.0  Ampere Computing, LLC Device e100
 |           \-01.0-[01]----00.0  Huawei Technologies Co., Ltd. Device 3714
 \-[0000:00]-+-00.0  Ampere Computing, LLC Device e100
             +-01.0-[01]--
             \-03.0-[02]--

  0b:00:00.0 第一个是segment,后面依次是bus device function
  下面01.0是device和function
  [01]、[02]、[03]是rootport下面的bus号。
  基本思路:循环扫描segment,bus ,device ,function,判断四个元素合成地址上是否有设备,XX:00:00:00 为PCI桥设备,其它就是PCI设备。

2、PCI Root Bridge I/O Support

  PCI Root Bridge I/O Protocol 给通过PCI Host Bus Controller 产生的PCI Root Brideg 提供一个I/O抽象, 一个PCI主机总线控制器是一个硬件组件,它允许访问一组PCI设备,它们共享一个公共的PCII/O和PCI内存资源池。PCI设备驱动程序将不会直接使用此协议。相反,他们将使用由PCI总线驱动程序产生的I/O抽象,只有需要直接访问整个PCI总线的驱动程序才应该使用此协议。EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL中提供的接口用于对内存、I/O和PCI配置空间执行基本操作。
  一个PCI segment是最多256 个共享PCI配置空间的PCI 总线的集合。根据芯片组的不同,单个EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL可以抽象出PCI Segment的一部分,或整个PCI Segment.一个PCI host bridge可以产生一个或多个PCI root bridges。当一个PCI主桥产生多个PCI root bridge时,可能有多个PCI segments.一个PCI root Bridge控制器包括一个EFI_DEVICE_PATH_Protocol实例和EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
在这里插入图片描述
  下图显示了具有一个具有PCI root bridge 的PCI host bus的示例。此PCI root bridge 产生一个PCI本地总线,它可以包含在主板或PCI插槽上的PCI设备
在这里插入图片描述

3、代码实现

UINTN
PciSoltEnumeratedDevice (
  OUT KL_SET_SYSTEM_INFO_FOR_PCIE_CARD  *KLpSetSystemInfo,
  OUT KL_SET_PCIE_CARD_BDF_INFO  *pPcieCardInfo
)
{
    EFI_STATUS              Status;
    EFI_HANDLE              *RbHandleBuffer;
    UINTN                   RbHandleCount=0;
    UINTN                   Index;
    PCI_CFG_ADDR            addr;
    UINT32                  buff = 0;
    EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL     *RbIoProtocol;
    UINT8                   PriBus;
    UINT8                   SecBus;
    UINT16						      Device;
    UINT16						      Function;
    UINT16                  SoltNum1 = 0;
    UINT16                  SoltNum2 = 0;
    UINT16                  PortNum = 0;
    UINT32                                PciVidDid ;
    UINT32                                SubPciVidDid ;
    UINT16  VendorId = 0xFFFF;
    UINT16  DeviceId = 0xFFFF;
    UINT8                           BaseClass;
    UINT8                           SubClass;
    UINT8                           CardType;
    
    
    // Locate all PciIo protocol
    Status = gBS->LocateHandleBuffer (
                            ByProtocol,
                            &gEfiPciRootBridgeIoProtocolGuid,
                            NULL,
                            &RbHandleCount,
                            &RbHandleBuffer );
    
    if(EFI_ERROR(Status)){
        DEBUG((DEBUG_ERROR,"ERROR Locating RbIo Protocols: Status:%r \n", Status));
        return FALSE;
    }
    
    DEBUG((DEBUG_ERROR, "RbHandleCount:%d\n",RbHandleCount));

    /* Loop through each root complex */
    for (Index = 0 ; Index < RbHandleCount; Index ++) { // Handle Count
        Status = gBS->HandleProtocol (
                                RbHandleBuffer[Index],
                                &gEfiPciRootBridgeIoProtocolGuid,
                                (VOID **)&RbIoProtocol );
        if(EFI_ERROR(Status)){
            continue;

        }

        /* Loop through each root port */
        //device/function根据OS下 lspci布线得到,
        for (Device = 1; Device <= 8; Device++) {
          for (Function = 0;Function <8; Function++){
            addr.Addr.Bus = 0;
            addr.Addr.Device = Device;
            addr.Addr.Function = Function;
            addr.Addr.Register = 0;
            addr.Addr.ExtendedRegister = 0;
            
            Status = RbIoProtocol->Pci.Read(RbIoProtocol, EfiPciWidthUint32, addr.ADDR, 1, (VOID*)&buff);
            if(EFI_ERROR(Status)){
                DEBUG((DEBUG_ERROR,"RbIo Protocols Pci Read Failed - Status: %r\n", Status));
                continue;
            }
        
            if (buff == 0xFFFFFFFF){
                //DEBUG((DEBUG_ERROR,"Device Not Exist!\n"));
                continue;
            }
            DEBUG((DEBUG_ERROR,"buff:0x%04x\n",buff));
            addr.Addr.Register = PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET;
            Status = RbIoProtocol->Pci.Read(RbIoProtocol, EfiPciWidthUint8, addr.ADDR, 1, (VOID*)&SecBus);

            addr.Addr.Bus = (UINT8)SecBus;
            addr.Addr.Device = 0;
            addr.Addr.Function = 0;
            addr.Addr.Register = 0;
            addr.Addr.ExtendedRegister = 0;
            DEBUG((EFI_D_ERROR, " PCIE SECONDARY_BUS:0x%04x\n",SecBus));
            Status = RbIoProtocol->Pci.Read(RbIoProtocol, EfiPciWidthUint32, addr.ADDR, 1, (VOID*)&PciVidDid);
             
            if(EFI_ERROR(Status)){
                DEBUG((DEBUG_ERROR,"RbIo Protocols Pci Read Failed - Status: %r,SoltNum1:%d\n", Status,SoltNum1));
                continue;
            }
            VendorId = (UINT16)(PciVidDid & 0xFFFF);
            DeviceId = (UINT16)(PciVidDid >> 16) & 0xFFFF;
            //过滤掉USB 设备
            if (((VendorId == 0x1912) && (DeviceId == 0x0014))||((VendorId == 0x1CC4) && (DeviceId == 0x0123))){
              continue;
            }
            if (PciVidDid != 0xFFFFFFFF){  

                addr.Addr.Register = 0x0B;
                Status = RbIoProtocol->Pci.Read(RbIoProtocol, EfiPciWidthUint32, addr.ADDR, 1, (VOID*)&BaseClass);
                if ( BaseClass == 0x02 ){  
                  continue;
                }
                SoltNum2++;   
                addr.Addr.Register = PCI_SVID_OFFSET;
                Status = RbIoProtocol->Pci.Read(RbIoProtocol, EfiPciWidthUint32, addr.ADDR, 1, (VOID*)&SubPciVidDid);
                KLpSetSystemInfo->PcieCardInfo[SoltNum2].VIDSLOT = (UINT16)(PciVidDid & 0xFFFF);
                KLpSetSystemInfo->PcieCardInfo[SoltNum2].DIDSLOT = (UINT16)(PciVidDid >> 16) & 0xFFFF;
                KLpSetSystemInfo->PcieCardInfo[SoltNum2].SUBVID  = (UINT16)(SubPciVidDid & 0xFFFF);
                KLpSetSystemInfo->PcieCardInfo[SoltNum2].SUBDID  = (UINT16)(SubPciVidDid >> 16) & 0xFFFF;


            }
          }
          
        
        } // Handle Count

    }

  DEBUG((EFI_D_ERROR,"%a() DeviceExist,SoltNum=%d\n",__FUNCTION__, SoltNum1));
  
  return SoltNum1;
}

参考OS下的lspci结果,可以对应上

2、gEfiPciIoProtocolGuid 定位

  BUS ,Device,Function ,可以识别网卡设备,但是网卡下面的 几个port口识别不出, port口插网卡的功能是相同的,每个port口都绑定了gEfiPciIoProtocol,可使用gEfiPciIoProtocol识别。网卡类型使用BaseClass == 2判断,raid卡使用 ((BaseClass == 0x01)&& ((SubClass == 0x07)||(SubClass == 0x04)))判断。

UINTN 
ReportEthernetPortDeviceInfoToBMC(
  OUT SET_SYSTEM_BDF_INFO_FOR_NET_PORT_ROOT_BDF *pSetNetPortRootInfo
)
{
  EFI_STATUS                         Status = EFI_SUCCESS;
  UINTN                              HandleArrayCount;
  EFI_HANDLE                         *HandleArray;
  UINTN                              Index;
  UINTN                              Index2;
  EFI_PCI_IO_PROTOCOL                *PciIo;
  UINT8                              PortNum = 0;
  UINT8                              Segment;
  UINT8                              BusNum;
  UINT8                              DevNum;
  UINT8                              FunNum;
  UINT8                              CardType;
  EFI_HANDLE                         HandleSlot;
  EFI_PCI_IO_PROTOCOL                *PciIoSlot;
  UINT8                           BaseClass;
  UINT8                           SubClass;

  Status = gBS->LocateHandleBuffer(
             ByProtocol,
             &gEfiPciIoProtocolGuid,
             NULL,
             &HandleArrayCount,
             &HandleArray
           );
  if (EFI_ERROR(Status)) {
    return PortNum;
  }
  DEBUG((EFI_D_ERROR, "HandleArrayCount=%d\n",HandleArrayCount ));
  for (Index2=0; Index2<HandleArrayCount; Index2++){
    Status = gBS->HandleProtocol(
               HandleArray[Index2],
               &gEfiPciIoProtocolGuid,
               (VOID **)&PciIo
             );
    Status = PciIo->Pci.Read(
               PciIo,
               EfiPciIoWidthUint8,
               0x0B, //PCI_CLASSCODE_OFFSET
               1,
               &BaseClass
             );
    Status = PciIo->Pci.Read(
               PciIo,
               EfiPciIoWidthUint8,
               0x0A, //PCI_CLASSCODE_OFFSET
               1,
               &SubClass
             );

    // network card
    if (BaseClass == 0x02){  
      CardType = 0;
    }
    //raid card
    if((BaseClass == 0x01)&& ((SubClass == 0x07)||(SubClass == 0x04))){  
      CardType = 2;  
    }
      //report net port data here
      //net card
    if ((CardType == 0) || (CardType == 2)) {
        PciIo->GetLocation(
        PciIo,
        &Segment,
        &BusNum,
        &DevNum,
        &FunNum);
      DEBUG((EFI_D_ERROR, "Index2=%d net card: seg=0x%x bus=0x%x dev=0x%x fun=0x%x\n", Index2, Segment, BusNum, DevNum, FunNum));
      
      pSetNetPortRootInfo->BDF_Info[PortNum].CardType = CardType; 
      pSetNetPortRootInfo->BDF_Info[PortNum].BusNum = BusNum;
      pSetNetPortRootInfo->BDF_Info[PortNum].DeviceNum = DevNum;
      pSetNetPortRootInfo->BDF_Info[PortNum].FunctionNum = FunNum;
      pSetNetPortRootInfo->BDF_Info[PortNum].Segment = Segment;
      pSetNetPortRootInfo->BDF_Info[PortNum].SlotNum = PortNum;
      
      PortNum++;
    }
  } 
  return PortNum;     
}

···
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UEFI(Unified Extensible Firmware Interface)是一种新一代的固件接口标准,用于替代传统的BIOS(Basic Input/Output System)。UEFI的引入使得计算机启动速度更快、性能更好,并且提供更多的扩展功能。 在UEFI中,PCI(Peripheral Component Interconnect)是一种计算机总线技术,用于连接外部设备,例如网络适配器、显卡等。PCI总线上的每个设备都有一个唯一的标识符,称为设备ID,用于识别设备并管理其操作。 在UEFI中,PCI设备的遍历是指系统固件或操作系统对机器上所有PCI设备进行枚举和初始化的过程。这个过程通常在计算机启动时进行,以确保所有PCI设备都能被正确地识别、配置和使用。 PCI设备的遍历过程一般分为以下几个步骤: 1. 初始化PCI总线控制器:UEFI固件首先初始化PCI总线控制器,以便与PCI总线上的设备进行通信。 2. 遍历PCI设备UEFI固件通过PCI总线控制器逐个枚举PCI设备,根据设备ID识别每个设备,并获取设备的配置空间信息。 3. 配置PCI设备UEFI固件根据设备的配置空间信息,为每个设备分配资源,例如内存地址、中断等。 4. 初始化PCI设备UEFI固件对每个PCI设备进行初始化,例如加载设备的固件驱动程序、设置设备的状态等。 5. 注册PCI设备UEFI固件将已经初始化的PCI设备注册到系统固件表中,以便操作系统能够识别和使用这些设备。 通过UEFIPCI遍历过程,计算机系统能够正确地识别和配置PCI设备,使其能够正常工作。这对于操作系统、驱动程序和应用程序的运行都非常重要。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值