PCI总线学习笔记:读写篇

news2024/10/6 14:38:50

前言

最近在写E1000网卡的驱动,这其中涉及到了PCI总线的相关内容。但是网上大部分关于PCI的文章都只局限在概念上的描述,并没有给出具体的例子来解释。这其实也是情理之中的,因为PCI总线规范就像是一个抽象的接口,其具体怎么实现是与具体的设备有关的,这也是学习硬件最让人头痛的地方:有时难以区分概念与实现的边界,例如,对于CPU是如何区分访存和MMIO这个问题(两者在CPU看来都是对一个物理地址进行访问),作为概念层的x86体系结构规范并没有明确规定这个该如何实现,而只是在手册里面提到了这样一个概念;而其具体实现方式在不同的CPU型号之间是不同的,例如在Intel Xeon系列CPU中就是通过一个叫做SAD(Source Address Decoder)的硬件来完成的。

Anyway,本文旨在记录一些我在学习中遇到的问题,以及这些问题的答案,希望能够给到读者一些帮助。

如果需要更加深入地理解PCI总线工作原理,建议配合《PCI Express体系结构导读》(王齐 著)使用。

PCI设备是如何读写的?

PCI是总线规范,其读写是通过总线事务来完成的,简单来说就是,按照一定的约定,向总线上写入事务类型,地址等参数,然后再使用数据线传输数据。(具体过程可以参考《PCI Express体系结构导读》)
(需要注意的是,这里的地址其实是PCI域的地址,和存储器域的地址并不等价,两者要通过HOST主桥做转换,但是由于在x86下,存储器域地址和PCI域地址在数值上是相等的,所以本文不再区分这个概念了,统一使用地址这个名词,这部分具体见《PCI Express体系结构导读》)

如何遍历PCI总线来发现存活设备?

实现思路:可以通过枚举所有可能的Bus Number, Device Number 和 Function Number来探测所有的存活设备。对于一组特定的Bus, Dev和Func,如果这个功能存在,那么在其配置空间中的Vendor ID和Device ID就是有效值,可以由此来判断。

如何访问配置空间?

访问配置空间的方式与具体的体系机构有关,例如在 MPC8548 处理器的 HOST 主桥中,与 PCI 设备配置空间相关的寄存器由CFG_ADDR、CFG_DATA 和 INT_ACK 寄存器组成。系统软件使用 CFG_ADDR 和 CFG_DATA 寄存器访问PCI 设备的配置空间,软件通过向CFG_ADDR寄存器中写入地址,然后访问CFG_DATA寄存器,当CFG_ADDR的EN位为1时,HOST 主桥将对这个寄存器的访问转换为 PCI 配置读写总线事务并发送到 PCI 总线上。而在x86体系结构下,CFG_ADDR 和 CFG_DATA是通过2个IO端口来实现的,CONFIG_ADDRESS地址是0xcf8,CONFIG_DATA地址是0xcfc,也就是说,可以通过IN和OUT指令对这两个端口进行读写来实现对配置空间的访问。
虽然访问CFG_ADDR的方式与具体的体系结构有关,但是CFG_ADDR的格式是由PCI Spec规定好了的,其具体含义如下图所示:
在这里插入图片描述

Bus Number,Device Number与设备被插在主板上哪个PCI插槽有关,其编号方式示意图如下(不完全严谨,但是这个不用细究,只需要知道这两个值可以唯一确定一个PCI插槽即可):

在这里插入图片描述

Function Number表示PCI设备上的功能号,一个PCI设备可以最多有8个功能(但是一般的PCI设备都是单功能,只是PCI规范提供了扩展的一种可能性)。
Register Number的含义见下图(配置空间也是PCI Spec规定了的内容,这里只展示了PCI设备的配置空间,PCI桥的配置空间略有差别):
在这里插入图片描述
例如,如果我想要访问某个设备某个功能的Revision ID,那么Register Number就设置为0x08,然后读取一个4字节的数据,取其中的第一个字节即可。

BAR寄存器如何工作的?

这也是初学者容易迷惑的地方,这一章节将尝试回答如下问题:

  • BAR寄存器保存的地址是什么地址?有什么用?
  • BAR寄存器中的值是谁负责分配的?

BAR寄存器的作用?

向BAR寄存器中写入了一个地址就相当于标记了这段地址是属于这个BAR的了,以后所有对这个地址的访存操作都会转发到这个设备,由这个设备进行操作。
例如,如果我有一个E1000网卡,我把这个网卡配置空间的BAR0设置为了0xabcde000,那么当我向0xabcde002的位置写入数据的时候,E1000网卡就会收到这个写数据的操作,并进行相应的动作。而具体访问这个地址会造成什么结果,这个是和具体的设备相关的,例如,对于E1000网卡,BAR0对应的是E1000相关寄存器,即如果BAR0设置为了0xabcde000,那么访问0xabcde000到0xabcdefff就等价于访问了E1000网卡的寄存器,如下图所示:
在这里插入图片描述
而具体每个地址对应到哪个寄存器,每个寄存器是什么作用,则需要继续查阅E1000的手册,下图是部分寄存器的偏移量以及名称:
在这里插入图片描述
(注:BAR寄存器有IO模式和MEM模式,MEM模式就是上面所述的情况,可以直接通过访存来实现,而IO方式则需要通过IN和OUT指令来访问IO端口来实现,这里不再赘述了)

BAR寄存器的大小?

接下来的问题是:这里我只设置了一个Base Address,我怎么知道这个区域的大小呢?例如,我把BAR0设置为了0xabcde000,那么为什么对地址0xbbcde000的访问不会转发到这个设备来呢?
这其实是通过一个规定来实现的,即如果BAR空间的大小为M,那么BAR寄存器中地址的低 l o g 2 M log_2M log2M位一定是0。例如,BAR1对应的空间大小是 2 12 2^{12} 212字节,那么BAR1的值一定是0xfffff000,0xabcde000之类的,不可以是0xabcde010,因为需要保证这个地址的二进制位的低12位是0。
而且这种规定还是由硬件来实现的,也就是说,如果这个空间大小是 2 12 2^{12} 212字节,那么即使我向这个BAR寄存器里面写0xfffffff,最终这个寄存器里的值只会是0xfffff000,硬件会自动把低12位给强制置零。事实上,软件也是通过这个小trick来获取到这个BAR空间的大小的。
(而且推测硬件也是通过这种方式来快速匹配总线事物的目标设备是不是自己,因为如果这个BAR空间大小是 2 12 2^{12} 212字节,那么只需要把地址线的高20位和BAR寄存器的高20位做比较即可得出结论)
(具体可参考这篇Stackoverflow Post)
(事实上,IO模式还是MEM模式,这也是由硬件定好的,软件是不可能通过写寄存器来更改的)

怎么就知道是访问这个设备了?

现在又有一个问题:我只是在E1000网卡的BAR0寄存器设置了一个值,然后我使用MOV $0xabcde002,%eax指令(假设虚拟地址0xabcde002对应的物理地址就是0xabcde002),CPU就会去找E1000网卡了。那么CPU是怎么知道这个信息的?
这涉及到总线的工作原理了,读0xabcde002这个指令不是单独发给E1000网卡的,而是广播给了总线上的所有设备,主设备会把访存地址发送到地址线上,然后每个PCI设备都拿地址线上的地址和自己的BAR寄存器匹配,如果匹配上了,就按照控制线上的指示进行操作,并通过数据线来传递数据。(具体可见这篇博客文章)

此时还有一个问题,CPU拿到MOV $0xabcde002,%eax指令的时候,只知道要去访问物理地址0xabcde002,那它为什么不去访问存储器的对应位置,而是把访存请求发到了E1000设备?
答案是:有相应的硬件设备来进行这种路由操作,不同型号的CPU对于这个功能的实现不尽相同,例如(参考资料【3】【4】),对于Intel Xeon系列的CPU,其内部有一个叫做SAD(Source Address Decoder)的硬件,这个硬件保存了对于MMIO区域的配置,负责把MMIO请求转发到PCI主桥中。
(注意,上文提到的0xabcde002指的都是物理地址,实际上在开启分页后,CPU处理的都是虚拟地址,虚拟地址需要通过MMU转换为物理地址)
更通用地来讲,对于x86架构,这部分工作应该是由北桥(North Bridge)来完成的,北桥应该负责把访问PCI设备的请求转发到PCI总线上。
在这里插入图片描述

MMIO究竟是怎么实现的?

有了上面的铺垫,还原MMIO的全过程就很简单了。
这里假设我把E1000网卡的BAR0设置为了0xabcde000,MEM类型;现在想要访问E1000网卡中offset为0的寄存器,且已知虚拟地址0xffabe000经过MMU变换成物理地址之后是0xabcde000,那么现在只需要一个movl $0xffabe000, %eax指令,就可以把想要的值存储到eax中。
上述整个过程具体分解如下:

  1. 0xffabe000这个地址通过MMU转换为物理地址0xabcde000
  2. CPU把读物理地址0xabcde000的请求发到北桥
  3. 北桥看出这是一个MMIO区域的地址,所以把请求发到PCI总线
  4. PCI总线上的E1000网卡匹配成功,把数据发送到总线的数据线上
  5. 数据逐层向上传递到CPU

逐级向下转发是如何实现的?

PCI桥也有自己的Base和Limit寄存器,可以记录这个PCI桥所管辖的地址范围,所以可以实现向下转发,具体见这篇文章。

BAR寄存器地址是谁分配的?

通过上面的描述,可以看出,BAR寄存器地址分配是一个难度比较高的任务,因为需要保证每个BAR寄存器的值之间一定不能有冲突,否则就会出现2个PCI设备同时响应一个总线事务的混乱局面,那么这个BAR寄存器值是谁分配的呢?
答案是BIOS等firmware在启动时分配的,而我们写操作系统时只需要读取firmware预分配好的值,然后直接利用就行,这个分配工作不需要操作系统来完成,具体见这篇Stackoverflow Post。

参考资料

【1】《PCI Express体系结构导读》(王齐 著)
【2】Intel E1000 Manual
【3】Physical Address Decoding in Intel Xeon v3/v4 CPUs: A Supplemental Datasheet
【4】Intel Xeon 7500 Datasheet

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1573590.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[LeetCode][LCR133]位 1 的个数——快速从右边消去1

题目 LCR 133. 位 1 的个数 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。 提示: 请注意,在某些语言…

CTF之矛盾

这一题就是php的弱比较“” 这里要求输入的不是数字,并且输入要为1才打印flag 那我们就输入一个1后面接随便什么字符,因为php的弱比较将字符与数字进行比较的时候,会把字符转换成数字再比较,当转换到字符时后面便都为空了 flag{…

蓝桥杯 经验技巧篇

1. 注意事项 👨‍🏫 官方通知 👨‍🏫 资料文档 时间:4月13日 9:00~13:00 (时长 4小时)物品 准考证(赛前一周开放下载,自行打印)学生证身份证笔、水、外套&a…

层次式架构设计-体系结构概述

层次式架构是软件体系结构设计中最为常用的一种架构形式,它为软件系统提供了一种在结构、行为和属性方面的高级抽象。其核心思想是将系统组成为一种层次结构,每一层为上层服务,并作为下层的客户。 层次式架构设计技术: 表现层中间…

LeetCode刷题之31.下一个排列

文章目录 1. 题目2.分析3.解答3.1 先排序,后交换3.2 先交换,后排序 1. 题目 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如,arr [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3…

C——找单身狗2

题目内容: 在一个数组中,室友两个数字出现了一次,其他所有数字都出现了两次。找出只出现一次的数字。 如:1,2,3,4,5,1,2,3,4&#xff…

交互设计师、UI设计师、视觉设计师面试作品集包装模板figma源文件

页面数量:19页 页面尺寸:1920*1080PX 交付格式:figma 赠送文件:24款高质量样机 交付文件:作品集模板源文件、作品集包装psd源文件、作品集所用字体文件 该作品集虽然只有19页,但可根据需求复制作品集里已有…

SpringBoot3整合RabbitMQ之三_工作队列模型案例

SpringBoot3整合RabbitMQ之三_工作队列模型案例 文章目录 SpringBoot3整合RabbitMQ之三_工作队列模型案例2. 工作队列模型1. 消息发布者1. 创建工作队列的配置类2. 发布消费Controller 2. 消息消费者One3. 消息消费者Two4. 消息消费者Three5. 输出结果 2. 工作队列模型 1. 消息…

金融企业区域集中库的设计构想和测试验证

导读 本文探讨了金融企业区域集中库的设计构想和测试验证,包括架构设想、数据库整合场景测试及优势和使用设想。作者提出利用 TiDB 数据库产品集中建设区域集中库,解决 MySQL 存量节点的整合问题,实现部署的标准化、按需扩展和统一运维管理。…

【绩效管理】帮助零售企业建立分层分类绩效考核体系项目纪实

购物中心张经理评价:“员工的绩效管理一直是困扰我公司的难题,我们只懂得怎么经营,至于怎么做人力资源管理,真是一点都不懂。这次华恒智信为我们提供的服务对我们的帮助很大。基于企业实际调研情况,华恒智信专家明确指…

蓝桥杯单片机第十四届省赛模拟考试一

一、基本要求 使用大赛组委会提供的国信长天单片机竞赛实训平台,完成本试题的程序设计与调试。程序编写、调试完成后,选手需通过考试系统提交以准考证号命名的hex文件。不符合以上文件提交要求的作品将被评为零分或者被酌情扣分。 硬件设置: …

SpringBoot3整合RabbitMQ之四_发布订阅模型中的fanout模型

SpringBoot3整合RabbitMQ之四_发布订阅模型中的fanout模型 文章目录 SpringBoot3整合RabbitMQ之四_发布订阅模型中的fanout模型3. 发布/订阅模型之fanout模型1. 说明1. 消息发布者1. 创建工作队列的配置类2. 发布消费Controller 2. 消息消费者One3. 消息消费者Two4. 消息消费者…

windows上使用influx2.7学习

参考 官方文档:https://docs.influxdata.com/influxdb/v2/ 下载 需要下载两样东西:influxd.exe和influx.exe influxd:influx数据库的服务端。下载地址:https://dl.influxdata.com/influxdb/releases/influxdb2-2.7.5-windows.zipinflux:连…

C# 实现子进程跟随主进程关闭

文章目录 前言一、如何实现?1、创建作业对象(1)、创建对象(2)、设置销毁作业时,关闭拥有的进程 2、子进程加入作业对象3、销毁作业对象(1)、手动销毁(2)、所在…

算法设计与分析实验报告c++实现(连续邮资问题、卫兵布置问题、圆排列问题、求解填字游戏问题、分支限界法求解旅行售货员(TSP)问题)

一、 实验目的 1.加深学生对算法设计方法的基本思想、基本步骤、基本方法的理解与掌握; 2.提高学生利用课堂所学知识解决实际问题的能力; 3.提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1.连续邮资…

最好用的安卓按钮(3)

属性解释 按钮文字 app:text“床前明月光” 按钮文字颜色 app:textColor“color/color_white” 按钮文字大小 app:textSize“22sp” 按钮背景颜色 app:color_normal“color/color_accent” 0x2 单独设置每个圆角 效果 代码 <top.androidman.SuperButton android:layo…

软考 系统架构设计师系列知识点之数据库基本概念(4)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之数据库基本概念&#xff08;3&#xff09; 所属章节&#xff1a; 第6章. 数据库设计基础知识 第1节 数据库基本概念 6.1.3 数据库管理系统 DBMS&#xff08;DataBase Management System&#xff0c;数据库管理系统&am…

【Linux】SSH协议应用

SSH协议 SSH简介实现OpenSSH ssh中的四个文件~/.ssh文件路径实验解析 SSH 简介 SSH&#xff08;secure shell&#xff09;只是一种协议&#xff0c;存在多种实现&#xff0c;既有商业实现&#xff0c;也有开源实现。本文针对的实现是OpenSSH&#xff0c;它是自由软件&#xf…

ZYNQ实验--CIC插值滤波器实验

一、CIC滤波器介绍 CIC (Cascaded Integrator-Comb) 滤波器是一种常用的数字信号处理滤波器&#xff0c;主要用于降采样&#xff08;decimation&#xff09;和升采样&#xff08;interpolation&#xff09;操作。它具有简单的硬件实现、高效的运算速度以及适用于需要快速处理的…

应届生选导师的创业公司,还是去中厂?

点击上方&#xff0c;选择“置顶/星标公众号” 福利干货&#xff0c;第一时间送达 导师是做自动化设备的控制软件&#xff0c;公司做纯软件。导师能给一样的工资&#xff0c;五人左右团队&#xff0c;潜在收入可能要高一些。纠结哪个对今后的发展更好一些。 大家好&#xff0…