PCI作业

该文章已生成可运行项目,

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的地址。

本文章已经生成可运行项目
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值