基于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
相关产品推荐
相关产品推荐

