目的:

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

imagehttp://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:

1.  Linux核心  第六章 PCI

2. Linux 设备驱动 Edition 3  第 12 章 PCI 驱动

[虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(五)的更多相关文章

  1. [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(九)

    目的 1. 使用verilog/vhdl设计一个PCI的watchdog设备. 2. 通过systemverilog 写testbench. 很久之前研究过AC97的verilog代码.但是很久没用v ...

  2. [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(四)

    通过前面的操作,我们已经可以创建一个带有我们自己的PCI的watchdog外设qemu 虚拟机了. 目的: 1. 了解我们的外设情况. 2. 为在guest中开发我们自己的linux PCI驱动程序做 ...

  3. [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(一)

    目的: 结合现在比较流行的技术,通过一个demo 展示一个全栈式设计的各种技能. 一个全栈式的工程师,应该能设计通过verilog/VHDL做logical设计.能写内核驱动,能架站. 要熟悉veri ...

  4. [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(六)

    目的: 1. 为我们自己的watchdog写一个驱动 步骤: 通过之前的介绍,我们很容易猜想到写我们基于PCI的watchdog驱动,可以分2个步骤. 1. 探测加载PCI设备 这部分代码跟我们的设备 ...

  5. [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(二)

    这篇文章的理解,需要一些专业知识了. 我们可以创建模拟自己的外设吗? 我们已经知道什么是qemu了,我们可以通过qmeu的提供的外设,DIY一个计算机了. 但是我们可能还不满足,我们可以自己制造一个外 ...

  6. [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(八)

    目的: 1. 通过网页读取watchdog的信息 2. 通过网页设置watchdog 准备工作: 1. 选择一个web框架,选用 cherrypy $ sudo apt-get install pyt ...

  7. [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(七)

    目标: 1. 完成最终的设备驱动,增加具体的watchdog设备操作的代码. 测试代码: 代码最终实现见cwd_demo.c 代码只实现了read与write.  没有实现ioctl. 因此,我们可以 ...

  8. [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(三)

    我们已经设计了一个基于qemu的watchdog了.下一步工作就是创建一个含有我们的watchdog的虚拟计算机器了. 准备工作: 1. 使用virt-manager或者virsh创建一个虚拟机器. ...

  9. 从零开始的全栈工程师——利用CSS3画一个正方体 ( css3 )

    transform属性 CSS3的变形(transform)属性让元素在一个坐标系统中变形.transform属性的基本语法如下: transform:none | <transform-fun ...

随机推荐

  1. Messenger类的使用

    一.Messenger类 作用:类似Message类,但是是跨进程使用的. 解析:它的底层是由AIDL实现的,从构造方法可以看出 //Service使用 public Messenger(Handle ...

  2. PHP文件访问技术

    <?php $file=fopen("test.txt","r"); //以只读方式打开test.txt $char=fgetc($file); echo ...

  3. Almost Prime

    Description Almost Prime time limit per test: 2 seconds memory limit per test: 256 megabytes input: ...

  4. Linux01--文件管理,常用命令 权限管理

    一.Ø文件系统 1.Linux文件系统特点  • Linux文件系统为单根的树状结构  •文件系统根为”/”  •文件名大小写敏感,除了”/”都是可用字符文件名以”.”开始的为隐藏文件  •文件路径使 ...

  5. bash快捷建-光标移到行首、行尾等

    转自:http://digdeeply.org/archives/12131599.html ctrl键组合ctrl+a:光标移到行首.ctrl+b:光标左移一个字母ctrl+c:杀死当前进程.ctr ...

  6. WPF Bug清单之(13)——应该出现却没有出现的ListView水平滚动条

    转载地址:http://www.cnblogs.com/nankezhishi/archive/2010/03/17/wpfbug13.html 我们知道ListView在内容超出控件本身范围时,默认 ...

  7. Hibernate 、多表关联映射 - 一对一关系映射(one- to-one)

    hibernate.cfg.xml: <hibernate-configuration> <session-factory name="sessionFactory&quo ...

  8. Struts2 自定义拦截器实例—登陆权限验证

    实现一个登陆权限验证的功能 message.jsp: <body> message:${message } </body> login.jsp: <% request.g ...

  9. nginx启动关闭

    [root@localhost sbin]# ./nginx -s reload [root@localhost sbin]# ./nginx -s stop [root@localhost sbin ...

  10. wormhole提升hivereader读取速度方案

    背景: 最近dw用户反馈wormhole传输速度很慢,有些作业甚至需要3-4个小时才能完成,会影响每天线上报表的及时推送.我看了下,基本都是从Hive到其他数据目的地,也就是使用的是hivereade ...