软件-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配置空间读写软件相关推荐

  1. linux 写pci配置空间,[转载]应用程序中pci配置空间读写

    应用程序中pci配置空间读写 ~~~~~~~~~~~~~~~~~~~~~~~ 1. 概述 主要讲述在linux操作系统中应用程序如何读写pci配置空间. 2. 基本原理 对于pci设备,由cpu通过一 ...

  2. PCIe配置空间和PCI设备中的寄存器

    1.访问PCI配置空间,PCI基本配置空间的读写使用下列函数: 原型定义在<linux/pci.h> int pci_read_config_byte(struct pci_dev *pd ...

  3. linux配置pcie无线网卡,【Linux c】读写pcie配置空间(安装lib库)

    readme.txt + pcie.c + in.txt +Makefile 可参考源码是: lspci readme.txt Please open the terminal in your lin ...

  4. PCIe 配置空间:Status 寄存器

    1. Status 寄存器位置 2. Status 寄存器细节 2.1 特殊位 对于 PCIe 设备,status 寄存器中有几个 bit 的值是固定的. Bit 4:Capability List, ...

  5. PCIE设备访问及其配置空间

    早期的PCI时期,系统为每个PCI设备分配的内存大小仅有256个Bytes.到后来的PCIE时期,随着设备性能增强,PCIE设备的配置空间扩展至4K个Bytes.在这里需要注意: PCIE一共支持25 ...

  6. 【总线】【PCI】【PCIe】【转】配置空间

    PCI Express,是计算机总线PCI的一种,它沿用现有的PCI编程概念及通信标准,但建基于更快的串行通信系统:PCIE总线使用的是高速差分总线,并采用端到端的连接方式, 现在的高速总线基本上都是 ...

  7. PCIe ECAM机制访问PCIE的配置空间

    1.PCIe ECAM机制 PCI Express Enhanced Configuration Access Mechanism (ECAM)是访问PCIe配置空间的一种机制.是将PCIe的配置空间 ...

  8. PCIe学习笔记之pcie结构和配置空间

    PCIe概述 PCI Express,是计算机总线PCI的一种,它沿用现有的PCI编程概念及通信标准,但建基于更快的串行通信系统. PCIE总线使用的是高速差分总线,并采用端到端的连接方式, 现在的高 ...

  9. pci配置基地址_PCI/PCIe基础——配置空间

    简介 PCI/PCIe设备有自己的独立地址空间,这部分空间会映射到整个系统的地址空间. 映射地址在BIOS/UEFI下指定(如果有的话,对于使用非BIOS启动的OS,不清楚),它有两种类型,一种是MM ...

最新文章

  1. 如何使用Navicat恢复数据库脚本
  2. thinkphp url缩短
  3. wxWidgets随笔(11)-wxFrame类
  4. 用户画像标签维度_神策数据钟秉哲:一文了解用户标签画像,从洞察到突破
  5. Python自动化运维开发----基础(八)字符串
  6. 根据可信计算机系统评估准则 用户,根据可信计算机系统评估准则(TESEC),用户能定义访问控制要求的自.._简答题试题答案...
  7. mysql三张表关联查询成绩表_mysql三张表关联查询
  8. zookeeper注册中心 kerberos_ZooKeeper 并不适合做注册中心
  9. CCF NOI1018 打电话
  10. 打造个人电脑安全终极防线
  11. Anylogic学习--------------数学函数
  12. 基于数据挖掘技术的客户关系管理系统设计与实现
  13. 如何注册域名的详细图文过程分享
  14. 美国市场数据分析初创公司Simon Data获2000万美元融资
  15. 应用二 stm32使用esp8266进行串口wifi通信
  16. 第七章第二十三题(游戏:储物柜难题)(Game: locker problem)
  17. 牛客华为机试-查找排序
  18. 每次要主板放电后才能正常开机
  19. 如何检查投标文件以及常见错误?
  20. 关于微信小程序webview的使用

热门文章

  1. 从玉农业考察汕尾华侨管理区-林裕豪:携手谋定农业产业
  2. 优缺点 快速扫描 硬盘监测_有了这6款mac硬盘检测工具 你就能够快速检测磁盘的状态和错误情况...
  3. 计算神经科学简介~【整理自】类脑计算背后的计算神经科学框架
  4. 通过Google Earth观看全球夜景地图
  5. ant design vue table分页
  6. 计算机唤醒休眠蓝屏,分享win10睡眠唤醒就蓝屏的解决办法
  7. WIN10系统从睡眠状态唤醒后电脑变卡顿
  8. pytorch-词嵌入基础
  9. 数据可视化利器D3.js教程 API
  10. 自定义View将圆角矩形绘制在Canvas上