由于X86采用了独立编址的方式,将memory操作与外设IO操作分开了,所以才有了memory空间和IO空间的区分。
X86平台CPU内部对内存和外设寄存器访问的指令也是不同的。
1 Memory空间
即访问内存的地址空间,32-bit平台为4G。 此memory空间和main memory(平时常说的内存或者主存)是两个概念,32-bit平台下CPU 内存地址总线只能寻址到4G,这4G空间包括main memory、外设IO空间映射(MMIO)等,不能全给main memory,因此32-bit的CPU是无法配置4G内存的。
2 IO空间
即访问外部设备寄存器的地址空间,PCI支持4GB的IO空间,但是x86平台仅支持64KB。
3 Configuration空间
即配置寄存器地址空间,是一个与Memory空间和IO空间并列的独立的空间。
对Legacy PCI来讲,Configuration 空间有256 Bytes,对于PCIe来讲, Configuration 空间4096 Bytes。
在一个具体的处理器应用中,PCI设备通常将PCI配置信息存放在E2PROM中。PCI设备进行上电初始化时,将E2PROM中的信息读到PCI设备的Configuration 空间中作为初始值。这个过程又硬件逻辑完成,绝大多数PCI设备使用这种方式初始化其Configuration 空间。
在X86系统中,系统软件会使用CONFIG_ADDR和CONFIG_DATA寄存器,读取PCI设备Configuration空间的这些初始化信息,然后根据处理器系统的实际情况使用DFS算法,初始化处理器系统中所有PCI设备的Configuration 空间。
在系统上电的过程中,在枚举整个PCI Bus之后,CPU会将所有的BDF的Configuration空间读到Host内存中。
在Host内存中有一个大小为256MB的Memory Block,专门用来存放所有的Configuration空间。
为什么是256MB? 我们可以计算一下:
因为PCIe最多支持256个Bus,每条Bus最多支持32个Device,每个Device又最多支持8个Function。而每个Function又对应4K空间大小的Configuration空间,所以可以得到下列公式:
256(Bus)*32(Dev)*8(Func)*4KB=64*1024*4KB=256MB
在台式机上我们很多时候觉得占用256MB空间太浪费(造成4G以下memory可用空间变少,虽然实际memory可以映射到4G以上,但对32位OS影响很大),PCI Bus也没有那么多,所以可以设置成最低64MB,即最多64个Bus。
4KB 的配置空间主要有3部分构成:
- 64B configure header
- 192B capability structure
- 3840B extend configuration space
如下图所示:
PCI配置Header(64B,00h~3Ch)具有固定的格式,主要有两种:Type0和Type1。
- Type0主要用于获取Endpoint(Agent)的Configuration Space
- Type1主要用于获取Switch或者Bridge的Configuration Space。
其中Type0 Header最多有6个BAR,而Type1 Header最多有两个BAR。这就意味着,对于Endpoint来说,最多可以拥有6个不同的地址空间。但在实际应用中基本上不会用到6个,通常使用1~3个BAR比较常见。
需要注意的是,通过BAR计算得到的系统空间是指该桥下挂的所有设备的系统空间的总和。
另外,在处理器系统中,一般会为所有的PCI/PCIe设备留足足够的空间,但是几乎没有系统会满配,所以很多的配置空间实际上是空的,此时如果去访问,就会得到全FF的返回值,表示设备不存在。