1、 4种空间迷魂阵
PCIe架构下定义了4种地址空间:Memory空间、IO空间、配置空间和message空间。
我们先看一下PCIe spec关于这四种空间的定义:
(1)配置空间 Configuration Space
One of the four address spaces within the PCI Express architecture. Packets with a Configuration Space address are used to configure Functions。
(2)IO空间 I/O Space
One of the four address spaces of the PCI Express architecture. Identical to the I/O Space defined in the PCI Local Bus Specification
(3)memory空间 Memory Space
One of the four address spaces of the PCI Express architecture. Identical to the Memory Space defined in PCI 3.0
(4)message空间 Message Space
One of the four address spaces of the PCI Express architecture
看完介绍是不是更懵X了,如果没有懵X说明这篇文章不适合你了,如果懵X了就继续。
message空间其实就是用来report带内的message和event的(比如error message、电源管理消息等),其实就是通过带内message transaction的方式来代替带外信号,用message的好处就是可以省去许多带外信号。
下面是PCIe spec中关于message space描述的原话:
The transaction Layer supports four address spaces: it includes the three PCI address spaces (memory, I/O, and configuration) and adds Message Space. This specification uses Message Space to support all prior sideband signals, such as interrupts, power-management requests, and so on, as in-band Message transactions. You could think of PCI Express Message transactions as “virtual wires” since their effect is to eliminate the wide array of sideband signals currently used in a platform implementation.
抛开第4种message空间先不谈,先看其他三种PCI 架构就定义的地址空间:Memory空间、IO空间、配置空间。说到memory空间和IO空间就不得不说统一编址和独立编址的问题。
2、 统一编址和独立编址导致IO空间和Memory空间分离
X86采用独立编址的方式,将memory操作与外设IO操作分开了,才有了memory空间和IO空间的区分。X86平台CPU内部对内存和外设寄存器访问的指令也是不同的。
IO空间:
访问外部设备寄存器的地址区域,(PCI支持4GB的IO空间,但是x86平台为64KB)。
memory空间:
访问memory的地址空间,32位平台为4G。 此memory空间和main memory(平时常说的内存或者主存)是两个概念,32bit平台下CPU memory地址总线只能寻址到4G,这4G空间包括main memory、外设IO空间映射(MMIO)等,不能全给main memory,因此32bit的CPU是无法配置4G内存的。
PCIe 配置空间:
PCIe spec规定了所有PCIe设备(除了host bus bridge外)必须实现配置空间,说白了就是PCI-SIG规定了一种独立于memory空间的PCIe设备访问(读写、配置)机制(说白了就是一堆按规则排列的reg)。PCI-SIG详细规定了PCIe设备reg的排列(每个capability id reg的后面4Byte会保存next capability的起始地址,这样方便芯片厂商扩展reg,并且PCIe驱动软件天然可以使用list来管理这些reg)。
3、 三角关系
X86的CPU可以直接访问memory空间和IO空间,但是不能直接访问PCIe配置空间(原因很简单,X86的CPU只有memory指令和IO指令,没有配置指令)。因此,需要把PCIe配置空间映射到memory空间或者IO空间(一般不推荐映射到IO空间)。或者说CPU访问PCIe配置空间需要一个翻译官(RC)。这个翻译官是干好事的(帮CPU的memory访问或者IO访问转换成PCIe域的请求),不是给鬼子带路的汉奸。
在X86系统中,IO方式的翻译官就是CONFIG_DATA和CONFIG_ADDRESS,这个两个位于IO空间的端口。这就是所谓的PCI的CAM方式,只能访问PCI兼容的配置空间(前面256Byte,从下图中也可看到register number只有8bit,所有只能访问256Byte。但是有些芯片比较鸡贼,会把reserved的bit24到30用起来,使用bit24-27作为extended register number,和bit0-7的register number组合这样可以扩展到4K,不过这种扩展属于芯片的私有行为,并不是所有芯片都支持)。
在X86系统中,Memroy方式的翻译官就是MCFG(memory mapped configuration space base address description table可以通过cat /proc/iomem查看PCI MMCONFIG得到在memory空间的映射)其实就是bus 0 dev 0 function 0 的BASE地址。这就是所谓的PCIe的ECAM方式,可以访问PCIe全部4K配置空间。
可以通过cat/proc/iomem | grep MMCONFIG得到MMCONFIG的base地址,然后使用busbox下面的devmem按照表格7-1的规则访问配置空间(下面程序找了0:3.1和0:0.0做了下实验),但是这种方式存在顺序问题,见PCIe Spec。
ECAM把memory事务从host CPU转换成PCIe fabric配置请求。这种转换对应软件来说存在潜在的顺序问题,因为写memory地址是典型的post事务(不需要completion),但是写配置空间是non post的请求(需要completion)。
软件无法知道什么时候,完成者完成了post事务。这种场景下(ECAM访问),软件必须要知道完成者已经完成了post请求,软件通常用回读刚写过location的这种方式来确定完成者是否完成。对于遵守PCI order规则的系统,read 事务必须要在post写完成后才能完成。然而,由于PCI order规则允许non post写事务和read事务进行乱序处理,CPU必须等待PCIe fabric上non-post写请求完成来确保完成者完成了这个事务(也就说,由于允许乱序non post写事务插了read的队)。
举个例子,软件期望通过ECAM的方式写device的Base address reg,然后读取memory-map的区域的base address reg的位置。如果软件发出memory-map读请求乱序了,在配置写请求达到前就达到,将会引起不可预知的结果。
为了阻止这个问题,处理器和主桥必须确保有一种方式可以让软件确定什么时候使用ECAM的写请求被完成者完成。
下面我们通过0:3.1和0:0.0两个设备,验证下config read和ECAM的read是否一致。我们可以看到系统中mmconfig的地址是0xf800_0000,0:3.1按照table 7-1算出的offset是0x1_9000,我们访问0xf801_9000的地址就是0:3.1的配置空间的0地址,也就是devieid和vendorid。可以看出ECAM方式访问和配置方式访问的值是一致的。
BAR(base address registers)就是为了把设备的内部各种资源映射(芯片内部寄存器或者DDR)到IO空间(IO BAR)或者memory 空间(memory BAR)。