本文是本人学习记录,不保证准确,如有错误请指出。如果侵犯请联系删除。
PCIE一共支持256条bus(8个bit),32个device(5个bit),8个function(3个bit),
假设负载全满的时候,内存分配的内存空间则是:
4K * 256 * 32 * 8 = 256 * 1024K = 256 * 1M = 256M bytes。
pcie介绍可以参考:UEFI开发历程3—PCIe总线设备的探索
《PCI Local Bus Specification 2.2.pdf》
配置空间
配置空间分三类。通用配置空间在000h-03Fh个byte大小。
一个是通用配置空间,一个是type 0配置空间,一个是type 1配置空间。我的理解是type 0 是Device,type 1是Bridge。
详情可以参考:
【精讲】PCIe基础篇——Type0 & Type1配置头及配置请求过程
PCI/PCIe那些事(3)-- Configuration Space Type 0/1
Class Code分为三个部分:ProgInterface(8-15),SubClassCode(16-23细分的类),BaseClassCode(24-31大类)对于ClassCode具体请阅读文章:PCIE的Class Code表
Picture 1 Device 配置空间
Picture 2 Bridge 配置空间
Picture 3 Type事务处理区分 《PCI-to-PCI Bridge Architecture.pdf》
Vendor ID
每个厂商都有一个对应的Vendor ID也是用于判断是哪个厂商生产的设备的依据
devicehunt 该网站可以查询对应的厂商ID。
在UEFI中读取配置空间的值后,进行匹配然后Show 其厂商。
Vendor ID为0xFFFF代表设备不存在,否则存在。
读取vendor id:
Width 表示要从哪个offset开始读多少个字节:
比如:读取vendorid时,从offset 0x00(Address)开始,读取16个bit。
Count 表示要读多少个32 bit的offset。比如要读0x00-0x0F,那Count 就是4(个人理解)。
在读取设备信息之前都需要去LocateProtocolBuffer、HandleProtocol等,也就是打开Protocol。
Protocol的资料可参考:
UEFI中的Protocol浅谈
BIOS知识枝桠—— Protocol
UEFI原理与编程(八):UEFI中的Protocol基础结构及其在内核中的表示
在UEFI中如何获取的呢?
/*
System inventory PCI device Id.
*/
typedef union _SYS_PCI_DEV_ID {
UINT32 DEV_VEN_ID;
struct {
// Manufacturer.
UINT16 VendorId;
UINT16 DeviceId; //< Device ID.
};
} SYS_PCI_DEV_ID;
Device ID
Device ID和Vendor ID组合可以区分是哪个厂商的哪个设备
Class Code是哪种设备的分类寄存器
Network、Storage、USB、Bridge、Serial、Display…
Revision ID是修订版本号
暂时不知道有什么用
遍历S BDF号
通过Socket Id 和Stack Id 来遍历Segment number和Bus number。每个Socket有几个Stack id 每个Stack 有8组Port: A B C D E F G H.
偏移地址
///
/// Rom Base Address in Bridge, defined in PCI-to-PCI Bridge Architecture Specification,
///
#define PCI_BRIDGE_ROMBAR 0x38
#define PCI_MAX_BAR 0x0006
#define PCI_MAX_CONFIG_OFFSET 0x0100
#define PCI_VENDOR_ID_OFFSET 0x00
#define PCI_DEVICE_ID_OFFSET 0x02
#define PCI_COMMAND_OFFSET 0x04
#define PCI_PRIMARY_STATUS_OFFSET 0x06
#define PCI_REVISION_ID_OFFSET 0x08
#define PCI_CLASSCODE_OFFSET 0x09
#define PCI_CACHELINE_SIZE_OFFSET 0x0C
#define PCI_LATENCY_TIMER_OFFSET 0x0D
#define PCI_HEADER_TYPE_OFFSET 0x0E
#define PCI_BIST_OFFSET 0x0F
#define PCI_BASE_ADDRESSREG_OFFSET 0x10
#define PCI_CARDBUS_CIS_OFFSET 0x28
#define PCI_SVID_OFFSET 0x2C ///< SubSystem Vendor id
#define PCI_SUBSYSTEM_VENDOR_ID_OFFSET 0x2C
#define PCI_SID_OFFSET 0x2E ///< SubSystem ID
#define PCI_SUBSYSTEM_ID_OFFSET 0x2E
#define PCI_EXPANSION_ROM_BASE 0x30
#define PCI_CAPBILITY_POINTER_OFFSET 0x34
#define PCI_INT_LINE_OFFSET 0x3C ///< Interrupt Line Register
#define PCI_INT_PIN_OFFSET 0x3D ///< Interrupt Pin Register
#define PCI_MAXGNT_OFFSET 0x3E ///< Max Grant Register
#define PCI_MAXLAT_OFFSET 0x3F ///< Max Latency Register
//
// defined in PCI-to-PCI Bridge Architecture Specification
//
#define PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET 0x18
#define PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET 0x19
#define PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET 0x1a
#define PCI_BRIDGE_SECONDARY_LATENCY_TIMER_OFFSET 0x1b
#define PCI_BRIDGE_STATUS_REGISTER_OFFSET 0x1E
#define PCI_BRIDGE_CONTROL_REGISTER_OFFSET 0x3E
//Here comes some Common PCI Header Offset Definitions.
#define PCI_VID 0x0000 // Vendor ID Register
#define PCI_DID 0x0002 // Device ID Register
#define PCI_CMD 0x0004 // PCI Command Register
#define PCI_STS 0x0006 // PCI Status Register
#define PCI_RID 0x0008 // Revision ID Register
#define PCI_IFT 0x0009 // Interface Type
#define PCI_SCC 0x000A // Sub Class Code Register
#define PCI_BCC 0x000B // Base Class Code Register
#define PCI_CLS 0x000C // Cache Line Size
#define PCI_PMLT 0x000D // Primary Master Latency Timer
#define PCI_HDR 0x000E // Header Type Register
#define PCI_BIST 0x000F // Built in Self Test Register
#define PCI_BAR0 0x0010 // Base Address Register 0
#define PCI_BAR1 0x0014 // Base Address Register 1
#define PCI_BAR2 0x0018 // Base Address Register 2
#define PCI_PBUS 0x0018 // Primary Bus Number Register
#define PCI_SBUS 0x0019 // Secondary Bus Number Register
#define PCI_SUBUS 0x001A // Subordinate Bus Number Register
#define PCI_SMLT 0x001B // Secondary Master Latency Timer
#define PCI_BAR3 0x001C // Base Address Register 3
#define PCI_IOBASE 0x001C // I/O base Register
#define PCI_IOLIMIT 0x001D // I/O Limit Register
#define PCI_SECSTATUS 0x001E // Secondary Status Register
#define PCI_BAR4 0x0020 // Base Address Register 4
#define PCI_MEMBASE 0x0020 // Memory Base Register
#define PCI_MEMLIMIT 0x0022 // Memory Limit Register
#define PCI_BAR5 0x0024 // Base Address Register 5
#define PCI_PRE_MEMBASE 0x0024 // Prefetchable memory Base register
#define PCI_PRE_MEMLIMIT 0x0026 // Prefetchable memory Limit register
#define PCI_PRE_MEMBASE_U 0x0028 // Prefetchable memory base upper 32 bits
#define PCI_PRE_MEMLIMIT_U 0x002C // Prefetchable memory limit upper 32 bits
#define PCI_SVID 0x002C // Subsystem Vendor ID
#define PCI_SID 0x002E // Subsystem ID
#define PCI_IOBASE_U 0x0030 // I/O base Upper Register
#define PCI_IOLIMIT_U 0x0032 // I/O Limit Upper Register
#define PCI_CAPP 0x0034 // Capabilities Pointer
#define PCI_EROM 0x0038 // Expansion ROM Base Address
#define PCI_INTLINE 0x003C // Interrupt Line Register
#define PCI_INTPIN 0x003D // Interrupt Pin Register
#define PCI_MAXGNT 0x003E // Max Grant Register
#define PCI_BRIDGE_CNTL 0x003E // Bridge Control Register
#define PCI_MAXLAT 0x003F // Max Latency Register
Device Path
UEFI Device Path (1): 重新认识Device Path
UEFI Device Path (2): Device Path的产生
句柄和Protocol个人理解(Handle)
个人理解逻辑图
亦可参考:如何理解UEFI中handle和protocol的概念 虽然我看不太懂。Agent Handle暂时不理解用处。
ControllerHandle理解
ControllerHandle表示打开某个协议的控制器句柄,不同的协议在同一个设备上打开时,其对应的ControllerHandle是相同的。例如,对于一个PCIe设备而言,如果驱动程序同时打开了gEfiPciIoProtocolGuid和gEfiDevicePathProtocolGuid协议,则这两个协议共享同一个ControllerHandle,即该PCIe设备的句柄。
在一些情况下,系统会为一个设备创建多个实例化的句柄,每个句柄可以用来访问该设备的不同功能或特定配置。此时,每个句柄也会有自己的ControllerHandle,但它们都指向同一个设备的结构体对象。
需要注意的是,对于不同的设备,其所对应的ControllerHandle是不同的,因为每个设备都有唯一的句柄来标识。因此,在使用EFI_OPEN_PROTOCOL_INFORMATION_ENTRY数据结构来描述打开的协议句柄和协议之间的关系时,需要注意区分不同设备的情况。
控制器句柄代表存在于平台中的控制台或引导设备。如果该句柄代表物理设备,则必须支持Device Path协议。如果该句柄代表虚拟设备,则不得支持Device Path协议。此外,设备句柄必须支持一个或多个额外的I/O协议,用于抽象化访问该设备。在UEFI规范中定义了一系列I/O协议,包括:
• Console Services: These have been replaced or supplemented by HII functionality. These protocols include the Simple Input Protocol, Simple Text Output Protocol, Simple Pointer Protocol,Serial I/O Protocol and Debug Port Protocol.
• Bootable Image Services: Block I/O Protocol, Disk I/O Protocol, Simple File System Protocol and Load File Protocol.
• Network Services: Network Interface Identifier Protocol, Simple Network Protocol and PXE Base Code Protocol.
• PCI Services: PCI Root Bridge I/O Protocol and PCI I/O Protocol.
• USB Services: USB Host Controller Protocol and USB I/O Protocol.
• SCSI Services: Extended SCSI Pass Thru Protocol and SCSI I/O Protocol.
• Graphics Services: Graphics Output Protocol.
#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002
#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004
#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER 0x00000008
#define EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010
#define EFI_OPEN_PROTOCOL_EXCLUSIVE 0x00000020
Attributes理解
这里是对UEFI规范中定义的几种协议打开方式的解释,并举例说明其使用场景:
EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL:通过指定的句柄和协议GUID来打开协议,并返回指向该协议接口的指针。该方式通常用于获取设备提供的服务,并与之交互。例如,在一个PCI控制器驱动程序中,可以使用 EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 方式来获取PCI总线协议的接口,以便访问设备的配置空间。
EFI_OPEN_PROTOCOL_GET_PROTOCOL:从指定句柄上获取指定协议的接口指针。该方式通常也用于获取设备提供的服务,与 EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 类似。例如,在一个USB驱动程序中,可以使用 EFI_OPEN_PROTOCOL_GET_PROTOCOL 方式来获取USB I/O协议的接口,以便进行USB数据传输操作。
EFI_OPEN_PROTOCOL_TEST_PROTOCOL:测试指定句柄是否支持指定协议,并返回测试结果。如果指定句柄支持指定协议,则函数返回EFI_SUCCESS;否则函数返回EFI_UNSUPPORTED。该方式通常用于测试一个设备是否支持需要的协议。例如,在一个文件系统驱动程序中,可以使用 EFI_OPEN_PROTOCOL_TEST_PROTOCOL 方式来测试指定句柄是否支持文件系统协议,以便判断设备是否可以挂载为文件系统。
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER:获取指定设备的子控制器所暴露出的协议接口。该方式通常用于枚举设备或处理设备的子组件。例如,在一个PCI总线驱动程序中,可以使用 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER 方式来获取PCI设备的控制器接口,以便管理PCI设备及其资源。
EFI_OPEN_PROTOCOL_BY_DRIVER:将指定协议安装到指定句柄上,并返回指向该协议接口的指针。该方式通常用于将驱动程序所提供的服务安装到设备句柄上,以便其它应用程序或驱动程序可以使用该服务。例如,在一个网络驱动程序中,可以使用 EFI_OPEN_PROTOCOL_BY_DRIVER 方式将TCP/IP协议栈协议安装到网络设备句柄上,以便其它应用程序或驱动程序可以使用网络服务。
EFI_OPEN_PROTOCOL_EXCLUSIVE:以独占的方式打开指定句柄上的指定协议。如果其它驱动程序或应用程序已经打开了该协议,那么该函数将返回错误码EFI_ACCESS_DENIED。该方式通常用于需要独占设备的操作。例如,在一个硬盘设备驱动程序中,可以使用 EFI_OPEN_PROTOCOL_EXCLUSIVE 方式打开硬盘控制器协议,以便进行独占式的读写操作。