[虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(五)
目的:
1. 了解PCI的基本知识,为完成watchdog的设备做准备。
准备知识:
简单的说,PCI 设备分3个空间。 配置空间,IO空间,内存地址空间。
PCI设备厂家决定了外设是使用IO空间还是IO内存空间。 我们通过读取配置空间的bar寄存器的最低位bit0来决定是该设备使用的是IO空间还是内存地址空间。
计算机一启动,bois或者linux会根据域,总线号、设备号和功能号,按照一定的算法,扫描PCI设备,读取设备配置空间的信息。最主要的包括厂商ID,设备ID和bar寄存器中设备的外设地址。
以下的这些内容可以不看。
具体的PCI的知识可参考: http://tldp.org/HOWTO/PCI-HOWTO.html
PCI硬件结构(PCI系统示意图):
了解的概念
domain, bus,devcie,function(PCI设备 = "域号, 总线号, 设备号, 和功能号"的组合)
image: http://blog.sina.com.cn/s/blog_6472c4cc0100qg5i.html
image: http://blog.sina.com.cn/s/blog_6472c4cc0100qtdo.html
详情: http://blog.sina.com.cn/s/blog_6472c4cc0100qbvs.html
PCI 寻址
每个 PCI 外设有一个总线号, 一个设备号, 一个功能号标识. PCI 规范允许单个系统占用多达 256 个总线, 但是因为 256 个总线对许多大系统是不够的, Linux 现在支持 PCI 域. 每个 PCI 域可以占用多达 256 个总线. 每个总线占用 32 个设备, 每个设备可以是一个多功能卡(例如一个声音设备, 带有一个附加的 CD-ROM 驱动)有最多 8 个功能. 因此, 每个功能可在硬件层次被一个 16-位地址或者 key , 标识. Linux 的设备驱动编写者, 然而, 不需要处理这些二进制地址, 因为它们使用一个特定的数据结构, 称为 pci_dev, 来在设备上操作.
$ lspci | cut -d: -f1-3
0000:00:00.0 Host bridge
0000:00:00.1 RAM memory
....
0000:00:14.0 VGA compatible controller
$ cat /proc/bus/pci/devices | cut -f1
0000
0001
...
00a0
0x00a0(0b10100000) 意思是 0000:00:14.0 当划分为域(16位), 总线(8位), 设备(5位)和功能(3位).
驱动概念
与USB驱动总线型的驱动类似, Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分。
就是Linux PCI驱动是内核自带的,或者说内核帮你写好了!而我们需要完成的就是设备本身的驱动,比如网卡驱动等。
这个与windows的驱动也是类似的。比如说我们需要开发一个基于USB的windows的打印机,我们只需要关心打印机的驱动即可。
三种地址空间
PCI I/O空间、PCI内存地址空间和PCI配置空间。
PCI I/O空间和PCI内存地址空间由设备驱动程序(即上面提到的设备本身驱动)使用。
PCI配置空间由Linux PCI初始化代码使用,这些代码用于配置PCI设备,比如中断号以及I/O或内存基地址。
枚举过程
熟悉USB开发的工程师知道USB host跟device的第一次通信都是采用0x00相同的地址,然后进行配置,分配给一个新的唯一地址。
PCI的枚举过程类似。 http://blog.csdn.net/linuxdrivers/article/details/5849698
对于i386结构的处理器,PCI总线的设计者在I/O地址空间保留了8个字节用于枚举过程中的配置,那就是0xCF8~0xCFF。这8个字节构成了两个32位 的寄存器,第一个是“地址寄存器”0xCF8,第二个是“数据寄存器”0xCFC。要访问某个设备中的某个配置寄存器时,CPU先往地址寄存器中写入目标 地址,然后通过数据寄存器读写数据。不过,写入地址寄存器的目标地址是一种总线号、设备号、功能号以及设备寄存器地址在内的综合地址。
这里的总线号、设备号和功能号是对配置寄存器地址的扩充,就是上面提到的附加的其他条件。首先每个PCI总线都有个总线号,主总线的总线号为0,其余的则 由CPU在枚举阶段每当探测到一个PCI桥时便为其指定一个,依次递增。设备号一般代表着一块PCI接口卡(更确切的说是PCI总线接口芯片),通常取决 于插槽的位置。每块PCI接口卡上可以有若干个功能模块,这些功能模块共用一个PCI总线接口芯片,包括其中用于地址映射的电子线路,以降低成本。从逻辑 的角度说,每个“功能”实际上就是一个设备(看过USB设备驱动的人很眼熟吧 呵呵),所以设备号和功能号合在一起又可以称作“逻辑设备号”,而每块卡上最多可以容纳8个设备。显然,这些字段(指整个32bit)结合在一起就惟一确 定了系统中的一项PCI逻辑设备。开始时,只有0号总线可以访问,在扫描0号总线时如果发现上面某个设备是PCI桥,就为之指定一个新的总线号,例如1, 这样1号总线就可以访问了,这就是枚举阶段的任务之一。
IO/MMIO
IO作为CPU和外设交流的一个渠道,主要分为两种,一种是Port I/O,一种是MMIO(Memory mapping I/O)。
I/O地址空间
我们常说的I/O端口,它实际上的应该被称为I/O地址空间。
对于x86架构来说,通过IN/OUT指令访问。PC架构一共有65536个8bit的I/O端口,组成64KI/O地址空间,编号从0~0xFFFF。 连续两个8bit的端口可以组成一个16bit的端口,连续4个组成一个32bit的端口。I/O地址空间和CPU的物理地址空间是两个不同的概念,例如 I/O地址空间为64K,一个32bit的CPU物理地址空间是4G。
MMIO
MMIO我们可以想访问内存一样访问IO端口。
MMIO占用CPU的物理地址空间,对它的访问可以使用CPU访问内存的指令进行。一个形象的比喻是把文件用mmap()后,可以像访问内存一样访问文
件、同样,MMIO是用访问内存一样的方式访问I/O资源,如设备上的内存。MMIO不能被cache,(有特殊情
况,如VGA)。
Port I/O和MMIO的主要区别在于
1)前者不占用CPU的物理地址空间,后者占有(这是对x86架构说的,一些架构,如IA64,port
I/O占用物理地址空间)。
2)前者是顺序访问。也就是说在一条I/O指令完成前,下一条指令不会执行。例如通过Port
I/O对设备发起了操作,造成了设备寄存器状态变化,这个变化在下一条指令执行前生效。uncache的MMIO通过uncahce
memory的特性保证顺序性。
3)使用方式不同
由于port
I/O有独立的64KI/O地址空间,但CPU的地址线只有一套,所以必须区分地址属于物理地址空间还是I/O地址空间。早期的CPU有一个M/I针脚来
表示当前地址的类型,后来似乎改了。刚才查了一下,叫request command line,不知道是不是一个针脚。
IBM
PC架构规定了一些固定的I/O端口,ISA设备通常也有固定的I/O端口,这些可以通过ICH(南桥)的规范查到。PCI设备的I/O端口和MMIO基
地址通过设备的PCI configure space报告给操作系统,这些内容以前的帖子都很多,可以查阅一下。
通常遇到写死在I/O指令中的I/O端口,如果不是ISA设备,一般都是架构规定死的端口号,可查阅规范。
BAR (http://blog.sina.com.cn/s/blog_6472c4cc0100qnht.html)
PCI bar(PCI base address register)。PCI bar就是该设备可用port i/o和mmio访问的寄存器的基地址。除了寄存器,还有一种称为扩展rom,也是通过mmio访问的。而ioremap的作用就是把pci bar(pci bar是物理地址)映射到虚拟地址空间中。所以它和pci_read_config_dword是下列关系:
pci_read_config_dword得到PCI bar ----> ioremap映射PCI bar到虚拟地址空间, 返回一个虚拟地址,以后通过该地址读写设备的寄存器。
可以通过读取配置空间的bar寄存器{bar0: 0x10, bar1: 0x14, bar2: 0x18, bar3: 0x1C, bar4: 0x20, bar5: 0x24} 来获取基地址。 如果读到的地址为是奇数,那么这个地址是个IO端口地址。 如果读到的是偶数,那么这个地址是IO内存地址。
见这个帖子 http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=1061711&page=1
iommu相关的PCI内容
http://download.intel.com/technology/computing/vptech/Intel%28r%29_VT_for_Direct_IO.pdf
这个对于我们完成watchdog的驱动没有关系,可以略过。这个对于深入虚拟化需要了解。
在没有IOMMU的情况下,设备(指32bit或64bit设备,老的16bit的不提)的DMA操作可以访问整个物理地址空间,所以理论上设备可以向操 作系统的代码段、数据段等内存区域做DMA,从而破坏整个系统。当然,通常来说不会有这样的设备。IOMMU的出现,可以实现地址空间上的隔离,使设备只 能访问规定的内存区域。
简要说一下intel的IOMMU怎么做到这点的:
目前PC架构最多有256PCI总线,于是IOMMU用一个称为root entry的数据结构描述PCI总线,总共256个root
entry构成一张表。每条PCI总线最多允许256个设备,IOMMU用context
entry描述一个PCI设备(或者是PCI桥),256个context entry构成一张表。所以就有了如图的关系。我们知道,PCI设备用
{BUS:DEV:FUNC}
(当然,还有个segment,不过似乎PC架构都只有一个segment,这个暂时忽略)描述一个设备。所以对于一个特定设备,用bus号做索引
root entry表,用dev号索引context entry表可以找到描述该设备的的context entry。context
entry中有一个指针指向一章I/O页表,当设备发起DMA操作时,IOMMU会根据该页表把设备的DMA地址转换成该设备可以访问内存区域的地址。
所以只要为设备建一张I/O页表,就可以使设备只能访问规定的内存区域了。当然,也可以把该页表当成跳板,让只能寻址32bit地址空间的设备访问到64bit地址空间中去。
IOMMU是对于设备发起DMA操作来说的,你可以理解成设备用于做DMA的地址是一个虚拟地址(这个虚拟地址和我们平时说的那个不一样,是指设备DMA 用的地址不是真实的物理地址,没有IOMMU的情况下用的是物理地址)。也有称这个虚拟地址为总线地址的,但我认为不恰当,容易让人误解。
对于eeprom的访问,它又和mmio还有iommu都没有关系。eeprom是个串行设备,它不是被ioremap到虚拟地址空间访问,而是通过
PCI bar报告一个eeprom寄存器给操作系统,这个寄存器有一个bit的di(data in)、一个bit的do(data
out)、一个bit的clk(时钟)。时钟在下降沿生效,写的时候通过di一个bit一个bit的写,读的时候通过do一个bit一个bit的读。
SRIOV相关的知识
http://docs.oracle.com/cd/E38902_01/html/E38873/glbzi.html
REF:
2. Linux 设备驱动 Edition 3 第 12 章 PCI 驱动
[虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(五)的更多相关文章
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(九)
目的 1. 使用verilog/vhdl设计一个PCI的watchdog设备. 2. 通过systemverilog 写testbench. 很久之前研究过AC97的verilog代码.但是很久没用v ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(四)
通过前面的操作,我们已经可以创建一个带有我们自己的PCI的watchdog外设qemu 虚拟机了. 目的: 1. 了解我们的外设情况. 2. 为在guest中开发我们自己的linux PCI驱动程序做 ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(一)
目的: 结合现在比较流行的技术,通过一个demo 展示一个全栈式设计的各种技能. 一个全栈式的工程师,应该能设计通过verilog/VHDL做logical设计.能写内核驱动,能架站. 要熟悉veri ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(六)
目的: 1. 为我们自己的watchdog写一个驱动 步骤: 通过之前的介绍,我们很容易猜想到写我们基于PCI的watchdog驱动,可以分2个步骤. 1. 探测加载PCI设备 这部分代码跟我们的设备 ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(二)
这篇文章的理解,需要一些专业知识了. 我们可以创建模拟自己的外设吗? 我们已经知道什么是qemu了,我们可以通过qmeu的提供的外设,DIY一个计算机了. 但是我们可能还不满足,我们可以自己制造一个外 ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(八)
目的: 1. 通过网页读取watchdog的信息 2. 通过网页设置watchdog 准备工作: 1. 选择一个web框架,选用 cherrypy $ sudo apt-get install pyt ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(七)
目标: 1. 完成最终的设备驱动,增加具体的watchdog设备操作的代码. 测试代码: 代码最终实现见cwd_demo.c 代码只实现了read与write. 没有实现ioctl. 因此,我们可以 ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(三)
我们已经设计了一个基于qemu的watchdog了.下一步工作就是创建一个含有我们的watchdog的虚拟计算机器了. 准备工作: 1. 使用virt-manager或者virsh创建一个虚拟机器. ...
- 从零开始的全栈工程师——利用CSS3画一个正方体 ( css3 )
transform属性 CSS3的变形(transform)属性让元素在一个坐标系统中变形.transform属性的基本语法如下: transform:none | <transform-fun ...
随机推荐
- java 和 Python 的互调
http://www.cnblogs.com/lmyhao/p/3363385.html http://blog.csdn.net/wxiaow9000/article/details/5166029 ...
- 欧几里得求最大公约数--JAVA递归实现
欧几里得算法求最大公约数算法思想: 求p和q的最大公约数,如果q=0,最大公约数就是p:否则,p除以q余数为r,p和q的最大公约数即q和r的最大公约数. java实现代码: public class ...
- (1)html初步--表格的使用
用表格标签制作简历 html代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" " ...
- ISO和UDF光盘格式、扩展ISO9660----Joliet和Romeo文件系统
ISO和UDF光盘格式.扩展ISO9660----Joliet和Romeo文件系统 刻录DVD盘,当文件大于2G的时候,Nero会提示NERO选文件时提示无法刻录这么大的文件,请转换格式.这到底是怎么 ...
- logstash indexer和shipper的配置
[elk@zjtest7-frontend config]$ cat logstash_agent.conf input { file { type => "zj_nginx_acce ...
- Google的兼容包问题【转】
转自:http://blog.sina.com.cn/s/blog_3e28c8a50101g14g.html 项目之前好好的,今天开Eclipse,,出错. 错误Error retrieving p ...
- css3教程:弹性盒模型
Css3引入了新的盒模型——弹性盒模型,该模型决定一个盒子在其他盒子中的分布方式以及如何处理可用的空间.这与XUL(火狐使用的用户交互语言)相似,其它语言也使用相同的盒模型,如XAML .GladeX ...
- Microsoft.Jet.OLEDB.4.0和Microsoft.ACE.OLEDB.12.0的适用版本
Jet 可以访问 Office 97-2003,但不能访问 Office 2007. ACE 既可以访问 Office 2007,也可以访问 Office 97-2003. 另外:Microsof ...
- some knowledge t
NSNumber static 看下面例子 gCount可以在Person 文件中使用 在main 中不行 @property()括号中可以填的属性 国际化 OC中的快捷键操作 operation ...
- C# 懒人常用异步方法
Winform this.Invoke(new Action(() => { })); Wpf this.Dispatcher.Invoke(DispatcherPriority.Normal, ...