ASK:Write a shell application to list PCI devices which are required to fill SSID/SVID
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/PciRootBridgeIo.h>
#include <IndustryStandard/Pci.h>
#include <Library/PrintLib.h>
#include <Library/UefiLib.h>
EFI_STATUS
EFIAPI
ScanEntryPoint(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
EFI_STATUS Status;
UINT64 PciAddress;//pci64位地址
UINT16 VendorId;
UINT32 SubsystemInfo;
UINT16 Svid, Ssid;//Svid,Ssid总共占16位
Status = gBS->LocateProtocol(&gEfiPciRootBridgeIoProtocolGuid, NULL, (void**)&PciRootBridgeIo);//获取协议实例
if (EFI_ERROR(Status))
{
Print(L"Failed to locate PCI ROOT BRIDGE IO: %r\n", Status);
return Status;
}
for (UINT16 Bus = 0; Bus < 256; Bus++)//总线号0-255
{
for (UINT8 Device = 0; Device < 32; Device++)//设备号0-31
{
for (UINT8 Function = 0; Function < 8; Function++)//功能号0-7
{
PciAddress = EFI_PCI_ADDRESS(Bus, Device, Function, PCI_VENDOR_ID_OFFSET);//获取pci地址
VendorId = 0xFFFF;//表示无效ID
Status = PciRootBridgeIo->Pci.Read(PciRootBridgeIo, EfiPciWidthUint16, PciAddress, 1, &VendorId);
if (EFI_ERROR(Status) || VendorId == 0xFFFF)//如果错误或者是无效地址则跳过这次循环
{
continue;
}
PciAddress = EFI_PCI_ADDRESS(Bus, Device, Function, 0x2C);//SubsystemId和SubsystemVendorId地址位于0x2c处
SubsystemInfo = 0;
Status =PciRootBridgeIo->Pci.Read(PciRootBridgeIo, EfiPciWidthUint32, PciAddress, 1, &SubsystemInfo);//调用pci的读操作,将读的内容写入subsystemInfo
if (!EFI_ERROR(Status))
{
Svid = SubsystemInfo & 0xFFFF;//进行与操作,将低16位读取出来
Ssid = (SubsystemInfo >> 16) & 0xFFFF;//Ssid在高16位,将它右移16位,并进行与操作,提取出来
}
Print(L"PCI %02x:%02x.%x - SVID: %04x, SSID: %04x\n", Bus, Device, Function, Svid, Ssid);
}
}
}
return EFI_SUCCESS;
}
效果图:

笔记:
typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_PROTOCOL)(
IN EFI_GUID *Protocol,
IN VOID *Registration, OPTIONAL
OUT VOID **Interface
);
为啥要调用locateprotocol:
-
UEFI 通过 协议(Protocol) 抽象硬件和系统功能。每个协议是一个结构体,包含函数指针和数据成员,定义了对某种资源的操作方式。
-
UEFI 禁止应用程序直接操作硬件(如内存映射或端口 I/O),必须通过协议接口访问。
-
原因:
-
兼容性:不同硬件平台(x86、ARM、RISC-V)的寄存器操作方式不同。
-
安全性:防止恶意代码直接篡改硬件状态。
-
资源管理:固件可以统一跟踪资源分配(如内存、中断)。
-
LocateProtocol原型,作用是直接通过 GUID 查找全局协议实例。第一个参数传的是你要用到的协议的guid(全局唯一标识符),在本次作业中要用到的协议为:EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL,改结构体原型为:
struct _EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL {
///
/// The EFI_HANDLE of the PCI Host Bridge of which this PCI Root Bridge is a member.
///
EFI_HANDLE ParentHandle;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM PollMem;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM PollIo;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Mem;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Io;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Pci;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_COPY_MEM CopyMem;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_MAP Map;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_UNMAP Unmap;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ALLOCATE_BUFFER AllocateBuffer;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FREE_BUFFER FreeBuffer;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FLUSH Flush;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GET_ATTRIBUTES GetAttributes;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_SET_ATTRIBUTES SetAttributes;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_CONFIGURATION Configuration;
///
/// The segment number that this PCI root bridge resides.
///
UINT32 SegmentNumber;
};
第二个注册键,通常为 NULL。仅在特殊场景(如事件回调)中使用,第三个为指针的指针,也就是传结构体指针的地址,在代码中指的是*PciRootBridgeIo的地址。
1574

被折叠的 条评论
为什么被折叠?



