问题:在做项目中,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;
}
···