7.PCIE配置空间读写软件
软件-7.PCIE配置空间读写
软件-7.PCIE配置空间读写
- 软件-7.PCIE配置空间读写
- 软件读写配置空间
- 驱动层接口
- 原理分析
- 驱动层代码接口
- 驱动层接口与原理
- 相关参考
- 基础知识
- raw_pci_ops 得初始化
- raw_pci_ext_ops 得原理-ECAM
- 如何获取ECAM得基地址?
- 软件读写配置空间
软件读写配置空间
驱动层接口
inline int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
inline int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
inline int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
inline int pci_write_config_byte(struct pci_dev *dev, int where, u8 val);
inline int pci_write_config_word(struct pci_dev *dev, int where, u16 val);
inline int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);
根本就是调用 pci总线接口
int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 *val); //读字节
int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 *val); //读字
int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 *val); //读双字
int pci_bus_write_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 val); //写字节
int pci_bus_write_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 val); //写字
int pci_bus_write_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 val); //写双字
在drivers\pci\access.c 中
int pci_bus_write_config_##size \(struct pci_bus *bus, unsigned int devfn, int pos, type value) \
{ \int res; \unsigned long flags; \if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \pci_lock_config(flags); \res = bus->ops->write(bus, devfn, pos, len, value); \ // 重点pci_unlock_config(flags); \return res; \
}
所以主要代码是 : bus->ops
原理分析
驱动层代码接口
根总线读写操作接口初始化
pci_acpi_scan_root->acpi_pci_root_create // 重要参数: acpi_pci_root_ops->pci_create_root_bus // bridge->ops=acpi_pci_root_ops.pci_ops, 也就是 pci_root_ops->pci_register_host_bridge->bus->ops = bridge->ops // acpi_pci_root_ops.ops, 也就是 pci_root_ops
所以bus得操作接口为 acpi_pci_root_ops
struct pci_ops pci_root_ops = {.read = pci_read,.write = pci_write,
};
static struct acpi_pci_root_ops acpi_pci_root_ops = {.pci_ops = &pci_root_ops,
};
所以只需要分析 pci_root_ops即可
int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,int reg, int len, u32 val)
{if (domain == 0 && reg < 256 && raw_pci_ops)return raw_pci_ops->write(domain, bus, devfn, reg, len, val); // 基础配置空间if (raw_pci_ext_ops)return raw_pci_ext_ops->write(domain, bus, devfn, reg, len, val); // 扩展配置空间return -EINVAL;
}
所以可以得到结论:
pci读写 0x00-0xFF 使用raw_pci_ops
pci读写 0x100-0xFFF 使用 raw_pci_ext_ops
驱动层接口与原理
相关参考
- 《PCI Express Base_r5_1》 7.2 章节 描述了PC访问PCIE配置空间机制
- 《10th Generation Intel® Core™ Processors, Datasheet Volume 1 of 2》 讲述了Intel 访问PCIE配置空间机制
- PCIe ECAM介绍 讲了ECAM访问
基础知识
在X86架构上有关于这部分的描述:
10th Generation Intel® Core™ Processors, Datasheet Volume 1 of 2 中 P29页描述:
PCI Express 将配置空间扩展到每个设备/功能4K字节。
PCI Express 配置空间分为 一个PCI兼容区域(就是前256个字节组成)和 一个扩展的PCIExpress 区域(就是 0x100-0xFFF)。
PCI前256字节配置空间:可以通过 PCI规范中定义的机制(就是 通过 0cf8-0cff : PCI conf1 两个ioport通过BDF来寻址访问 ) 或 使用PCI Express 增强配置机制(ECAM- PCI Express Enhanced Configuration Access Mechanism)访问机制来访问PCI兼容区域。
PCI的0x100-0xFFF的ECAM访问,使用ioremap去访问PCI Express区域,这个属于硬件支持,基地址从ACPI来获取到。
PCI Express 主机桥,将内存映射的PCI Express 配置空间访问从主机处理器转换为PCI Express 配置周期。为了保持与PCI配置寻址机制的兼容性,建议系统软件仅使用32位操作(32位对齐)访问增强的配置空间。有关PCI兼容和PCI Express 增强配置机制和事务规则的详细信息,请参阅《 PCI Express基本规范》。
raw_pci_ops 得初始化
初始化入口
// init.c:45:arch_initcall(pci_arch_init);
pci_arch_init// 《Linux那些事之PCI》P5中描述了三种PCI access mode, // 内核中CONFIG_PCI_DIRECT这个宏有配直接Direct去访问pci_direct_probe(); // 初始化0xCF8和0xCFC,并初始化if (x86_init.pci.arch_init && !x86_init.pci.arch_init()) // 函数没实现,哈哈return 0;// pci_pcbios_init(); // CONFIG_PCI_BIOS--不配置,不用看pci_direct_init(type); // raw_pci_ops = raw_pci_ext_ops 预留读写pci配置空间的接口
pci_probe & PCI_PROBE_CONF1 # 判断,什么是CONF1和CONF2
request_region(0xCF8, 8, "PCI conf1") # 为什么使用0xCF8
pci_check_type1() # 检测type1
raw_pci_ops = &pci_direct_conf1; # !!! 初始化pci配置空间操作接口
结论(重点):
所以raw_pci_ops = &pci_direct_conf1; 可以看到 使用0xCF8和0xCFC访问PCI基础配置空间
inno@DEV-005:~$ sudo cat /proc/ioports | grep "PCI conf" # 申请到得IO接口
0cf8-0cff : PCI conf1// arch/x86/direct.c中 // 访问格式
#define PCI_CONF1_ADDRESS(bus, devfn, reg) \(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \| (devfn << 8) | (reg & 0xFC))
outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8); // 配置地址
u32 value = inl(0xCFC); // 读取配置
在深入PCI与PCIe之二:软件篇 中描述了CONFIG_ADD_RESS得结构:
CONFIG_ADDRESS寄存器格式:
31 位:Enabled位。
23:16 位:总线编号。 // bus
15:11 位:设备编号。 // devfn[7:3]
10: 8 位:功能编号。 // devfn[2:0]
7: 2 位:配置空间寄存器编号。 // 配置空间偏移地址, 注:因为是32位端口,所以4字节访问。
1: 0 位:恒为“00”。这是因为CF8h、CFCh端口是32位端口。
raw_pci_ext_ops 得原理-ECAM
参考:
- PCIe ECAM介绍
- PCI Express Base_r5_1 7.2 章节
ECAM是访问PCIe配置空间一种机制,PCIe配置空间大小是4k
4kbyte寄存器地址空间,需要12bit bit 0~bit11
Function Number bit 12~bit 14
Device Number bit 15~bit 19
Bus Number bit 20~bit 27
如何访问一个PCIe设备的配置空间呢?
比如ECAM 基地址是0xd0000000
devmem 0xd0000000就是访问00:00.0 设备偏移0寄存器,就是Device ID和Vendor ID
devmem 0xd0100000就是访问01:00.0 设备偏移0寄存器
所以,这里重点就是看ECAM得初始化
pci_acpi_scan_root // 主桥信息struct pci_root_info 和 struct pci_sysdata 初始化-> acpi_pci_root_create // ECAM初始化,主桥资源初始化-> ops->init_info(info) // 就是 acpi_pci_root_ops得 pci_acpi_root_init_infopci_acpi_root_init_info-> setup_mcfg_map
setup_mcfg_map
pci_mmcfg_late_init();// #define ACPI_SIG_MCFG "MCFG" /* PCI Memory Mapped Configuration table */acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse); // "ACPI中关于MCFG的描述"if (raw_pci_ext_ops == NULL)raw_pci_ext_ops = &pci_mmcfg; // !!! 初始化接口
如何获取ECAM得基地址?
方式一:打印ACPI表
sudo apt-get install -y iasl acpica-tools
mkdir -p testacpi && cd testacpi
acpidump > acpidump.out # 将ACPI表二进制打印到文件
acpixtract -a acpidump.out # 解析acpi表,生成各个dat文件
iasl -d mcfg.dat # iasl会解析acpi 二进制表,生成xxx.dsl描述文件
cat mcfg.dsl # 可以查看mcfg的配置文件
比如Intel,我这里看到的是 0x8000_0000,
Start BusNum=00, End BusNum=ff, 所以所有总线的ECAM都在这个空间,按照ECAM地址空间依次偏移即可。
cat /proc/ioport | grep 80000000
80000000-8FFFFFFF PCI MMCONFIG [bus 00 - ff]
方式二:看内核启动打印
比如这个ECAM的基地址是0xe0000000
[ 0.111732] PCI: MMCONFIG for domain 0000 [bus 00-ff] at [mem 0xe0000000-0xefffffff] (base 0xe0000000)
[ 0.111734] PCI: MMCONFIG at [mem 0xe0000000-0xefffffff] reserved in E820
7.PCIE配置空间读写软件相关推荐
- linux 写pci配置空间,[转载]应用程序中pci配置空间读写
应用程序中pci配置空间读写 ~~~~~~~~~~~~~~~~~~~~~~~ 1. 概述 主要讲述在linux操作系统中应用程序如何读写pci配置空间. 2. 基本原理 对于pci设备,由cpu通过一 ...
- PCIe配置空间和PCI设备中的寄存器
1.访问PCI配置空间,PCI基本配置空间的读写使用下列函数: 原型定义在<linux/pci.h> int pci_read_config_byte(struct pci_dev *pd ...
- linux配置pcie无线网卡,【Linux c】读写pcie配置空间(安装lib库)
readme.txt + pcie.c + in.txt +Makefile 可参考源码是: lspci readme.txt Please open the terminal in your lin ...
- PCIe 配置空间:Status 寄存器
1. Status 寄存器位置 2. Status 寄存器细节 2.1 特殊位 对于 PCIe 设备,status 寄存器中有几个 bit 的值是固定的. Bit 4:Capability List, ...
- PCIE设备访问及其配置空间
早期的PCI时期,系统为每个PCI设备分配的内存大小仅有256个Bytes.到后来的PCIE时期,随着设备性能增强,PCIE设备的配置空间扩展至4K个Bytes.在这里需要注意: PCIE一共支持25 ...
- 【总线】【PCI】【PCIe】【转】配置空间
PCI Express,是计算机总线PCI的一种,它沿用现有的PCI编程概念及通信标准,但建基于更快的串行通信系统:PCIE总线使用的是高速差分总线,并采用端到端的连接方式, 现在的高速总线基本上都是 ...
- PCIe ECAM机制访问PCIE的配置空间
1.PCIe ECAM机制 PCI Express Enhanced Configuration Access Mechanism (ECAM)是访问PCIe配置空间的一种机制.是将PCIe的配置空间 ...
- PCIe学习笔记之pcie结构和配置空间
PCIe概述 PCI Express,是计算机总线PCI的一种,它沿用现有的PCI编程概念及通信标准,但建基于更快的串行通信系统. PCIE总线使用的是高速差分总线,并采用端到端的连接方式, 现在的高 ...
- pci配置基地址_PCI/PCIe基础——配置空间
简介 PCI/PCIe设备有自己的独立地址空间,这部分空间会映射到整个系统的地址空间. 映射地址在BIOS/UEFI下指定(如果有的话,对于使用非BIOS启动的OS,不清楚),它有两种类型,一种是MM ...
最新文章
- 如何使用Navicat恢复数据库脚本
- thinkphp url缩短
- wxWidgets随笔(11)-wxFrame类
- 用户画像标签维度_神策数据钟秉哲:一文了解用户标签画像,从洞察到突破
- Python自动化运维开发----基础(八)字符串
- 根据可信计算机系统评估准则 用户,根据可信计算机系统评估准则(TESEC),用户能定义访问控制要求的自.._简答题试题答案...
- mysql三张表关联查询成绩表_mysql三张表关联查询
- zookeeper注册中心 kerberos_ZooKeeper 并不适合做注册中心
- CCF NOI1018 打电话
- 打造个人电脑安全终极防线
- Anylogic学习--------------数学函数
- 基于数据挖掘技术的客户关系管理系统设计与实现
- 如何注册域名的详细图文过程分享
- 美国市场数据分析初创公司Simon Data获2000万美元融资
- 应用二 stm32使用esp8266进行串口wifi通信
- 第七章第二十三题(游戏:储物柜难题)(Game: locker problem)
- 牛客华为机试-查找排序
- 每次要主板放电后才能正常开机
- 如何检查投标文件以及常见错误?
- 关于微信小程序webview的使用
热门文章
- 从玉农业考察汕尾华侨管理区-林裕豪:携手谋定农业产业
- 优缺点 快速扫描 硬盘监测_有了这6款mac硬盘检测工具 你就能够快速检测磁盘的状态和错误情况...
- 计算神经科学简介~【整理自】类脑计算背后的计算神经科学框架
- 通过Google Earth观看全球夜景地图
- ant design vue table分页
- 计算机唤醒休眠蓝屏,分享win10睡眠唤醒就蓝屏的解决办法
- WIN10系统从睡眠状态唤醒后电脑变卡顿
- pytorch-词嵌入基础
- 数据可视化利器D3.js教程 API
- 自定义View将圆角矩形绘制在Canvas上