You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于EDK2环境,如何通过PCI I/O Protocol获取PCI设备链路状态?

获取PCIe设备链路速度状态的实现方案

可以通过EDK2的PCI I/O Protocol获取PCIe设备的链路速度状态,只有PCIe设备具备链路速度属性,传统PCI设备无此概念。具体实现步骤如下:

1. 识别PCIe设备

首先需要遍历PCI设备的配置空间Capability列表,找到PCIe Capability(Capability ID = 0x10),以此判断设备是否为PCIe设备:

  • 从标准配置空间的0x34偏移处读取Capability指针,开始遍历整个Capability链表
  • 每个Capability条目首字节为ID,若ID等于0x10,则找到PCIe Capability,记录其起始偏移

2. 读取链路速度相关寄存器

找到PCIe Capability后,读取以下两个关键寄存器(均为32位):

  • Link Capabilities寄存器:位于PCIe Capability起始偏移+0x04处,其中Bits 15:14表示设备支持的最大链路速度
  • Link Status寄存器:位于PCIe Capability起始偏移+0x14处,其中Bits 15:14表示当前协商后的实际链路速度

速度编码对应关系(可根据PCIe版本扩展):

  • 00 → 1.0 GT/s(PCIe 1.0)
  • 01 → 2.5 GT/s(PCIe 2.0)
  • 10 → 5.0 GT/s(PCIe 3.0)
  • 11 → 8.0 GT/s(PCIe 4.0)
  • 100 → 16 GT/s(PCIe 5.0)
  • 101 → 32 GT/s(PCIe 6.0)

3. EDK2代码示例

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/PciIo.h>

EFI_STATUS GetPcieLinkSpeed(EFI_PCI_IO_PROTOCOL *PciIo, UINT8 Bus, UINT8 Dev, UINT8 Func) {
    EFI_STATUS Status;
    UINT8 CapPtr;
    UINT8 CapId;
    UINT32 LinkCap;
    UINT32 LinkStatus;
    UINTN PcieCapOffset = 0;

    // 读取标准配置空间的Capability指针(偏移0x34)
    Status = PciIo->Pci.Read(PciIo, EFI_PCI_IO_PROTOCOL_WIDTH_8, 0x34, 1, &CapPtr);
    if (EFI_ERROR(Status)) return Status;

    // 遍历Capability链表,查找PCIe Capability
    while (CapPtr != 0) {
        Status = PciIo->Pci.Read(PciIo, EFI_PCI_IO_PROTOCOL_WIDTH_8, CapPtr, 1, &CapId);
        if (EFI_ERROR(Status)) break;

        if (CapId == 0x10) { // PCIe Capability ID
            PcieCapOffset = CapPtr;
            break;
        }

        // 读取下一个Capability指针
        Status = PciIo->Pci.Read(PciIo, EFI_PCI_IO_PROTOCOL_WIDTH_8, CapPtr + 1, 1, &CapPtr);
        if (EFI_ERROR(Status)) break;
    }

    if (PcieCapOffset == 0) {
        Print(L"[Bus %02x][Dev %02x][Func %02x] 不是PCIe设备\n", Bus, Dev, Func);
        return EFI_SUCCESS;
    }

    // 读取Link Capabilities寄存器
    Status = PciIo->Pci.Read(PciIo, EFI_PCI_IO_PROTOCOL_WIDTH_32, PcieCapOffset + 0x04, 1, &LinkCap);
    if (EFI_ERROR(Status)) return Status;

    // 读取Link Status寄存器
    Status = PciIo->Pci.Read(PciIo, EFI_PCI_IO_PROTOCOL_WIDTH_32, PcieCapOffset + 0x14, 1, &LinkStatus);
    if (EFI_ERROR(Status)) return Status;

    // 解析速度值(用3位兼容更高版本PCIe)
    UINT8 MaxSpeed = (LinkCap >> 14) & 0x7;
    UINT8 CurrentSpeed = (LinkStatus >> 14) & 0x7;

    // 速度映射字符串
    CHAR16* SpeedStr[] = {
        L"1.0 GT/s (PCIe 1.0)",
        L"2.5 GT/s (PCIe 2.0)",
        L"5.0 GT/s (PCIe 3.0)",
        L"8.0 GT/s (PCIe 4.0)",
        L"16 GT/s (PCIe 5.0)",
        L"32 GT/s (PCIe 6.0)"
    };

    CHAR16* MaxSpeedStr = (MaxSpeed < ARRAY_SIZE(SpeedStr)) ? SpeedStr[MaxSpeed] : L"未知速度";
    CHAR16* CurrentSpeedStr = (CurrentSpeed < ARRAY_SIZE(SpeedStr)) ? SpeedStr[CurrentSpeed] : L"未知速度";

    // 打印设备链路状态
    Print(L"PCIe Device: [Bus %02x][Dev %02x][Func %02x]\n", Bus, Dev, Func);
    Print(L"  最大支持链路速度: %s\n", MaxSpeedStr);
    Print(L"  当前链路速度: %s\n", CurrentSpeedStr);

    return EFI_SUCCESS;
}

注意事项

  • 确保已正确打开目标设备的PCI I/O Protocol实例
  • 部分设备可能处于未训练状态,此时Current Speed可能显示为未知,需结合设备实际工作状态判断
  • 若需要获取链路宽度信息,可读取Link Capabilities和Link Status寄存器中的宽度字段(Bits 7:0)

内容的提问来源于stack exchange,提问作者Sharingan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:53:11