【14】PCIe架构下memory空间、IO空间、PCIe配置空间简介

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),因为X86平台只支持64KB的IO空间,Endpoint为了能在X86上使用,只能把自己的消耗的IO资源限制在64KB以内。由于Endpoint纷纷把IO资源的消耗限制在64KB,因此,大部分其他架构的CPU也把IO空间限制到64KB以内了(不是无法支持,而是根本用不着这么大IO空间)。

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域的请求),不是给鬼子带路的汉奸。
  下面是PCIe system architecture对PCIe 三种空间的描述:
(1)32bit memory address系统中,memory address space是4GB,64bit memory address系统中是16EB
(2)PCI支持多达4GB的IO address space,但是很多平台会限制IO space到64KB(16bit),因为X86只能支持64KB IO address space。为什么要这么做呢?因为endpoint设备做出来肯定要在大部分CPU系统中都可以使用,为了在X86上使用,所有的endpoint设备消耗的IO BAR(如果是legacy的endpoint,其IO BAR也就是一两百个bytes)都非常小,那么其他的CPU平台也就没有必须把自己支持的IO space做得超过64KB。
在这里插入图片描述

在这里插入图片描述
  PCI device configuration reg被map到 一个space,这个space就是configuration address space。每个PCI function有256Byte的configuration address space,256Byte x 256bus x 32device x 8function=16MByte(注意是PCI,不是PCIe)。
  X86 CPU可以访问memory空间或者IO空间但是不能直接访问configuration space。CPU需要通过host bridge(North bridge)里面的IO mapped address portdata port来访问PCI configuration space。Address Port在IO address的CF8-CFB,Data Port在IO address的CFC-CFF。
在这里插入图片描述
  step1: CPU向Address Port(IO address 0xCF8)发起一个IO wirte。
  step2: CPU向Data Port(IO address 0xCF8)发起一个IO read/IO wirte。North bridge 会在PCI bus上产生一个configuration read或者configuration write。
在这里插入图片描述
  在X86系统中,IO方式的翻译官就是CONFIG_DATA和CONFIG_ADDRESS,这个两个位于IO空间的端口。这就是所谓的PCI的CAM方式,只能访问PCI兼容的配置空间(前面256Byte,从下图中也可看到register number只有6bit,但是可以访问256Byte(下面会详细讲为啥6bit可以访问256Byte)。但是有些CPU芯片比较鸡贼,会把reserved的bit24到30用起来,使用bit24-27作为extended register number,和bit2-7的register number组合这样可以扩展到4K,不过这种扩展属于芯片的私有行为,并不是所有芯片都支持)。
在这里插入图片描述
在这里插入图片描述
  Configuration Address Port的register number(bit7-2)只有6bit,为什么能访问256Byte的PCI compatible space空间?
原因是,256Byte的PCI compatible space空间被分割成很多reg段每个reg段的大小是4Byte,这样就有了256/4=64个reg段。Configuration Address Port的register number(bit7-2)的value就是register index,register index需要乘以4才是register offset,这样6bit的register number(bit7-2)就访问了256Byte的PCI compatible space。
寄存器index 0对应configuration space的字节0-3。
寄存器index 1对应configuration space的字节4-7。

寄存器index 63对应字节configuration space的252-255。

  至于为啥每个reg字段的大小是4Byte,是因为Configuration Data Port是32bit reg,每次IO read或者IO write只能发送4Byte的data。
  举个例子比如你想访问配置空的register offset为0x4的偏移(command and status reg),那么你需要的register index就是0x4/4=0x1,你需要往Configuration Address Port的bit7-2写0x1,0x1<<2=0x4,也就是bit7-0写0x4
  更简单一种理解方式是把Configuration Address Port的低bit7-0理解成register offset(只是这个offset必须4byte对齐的,Configuration Address Port的最后2bit是hardware成0的),这样比把bit7-2理解成register index,再通过register index*4计算出register offset更简单。
在这里插入图片描述
  下面是写了一个test 代码来说明,代码中的offset参数就是bit7-0
在这里插入图片描述
下面是源码

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/io.h>

#define PCI_CONFIG_ADDRESS		(0xCF8)
#define PCI_CONFIG_DATA			(0xCFC)

// 生成PCI配置空间地址的宏
#define PCI_CONF_ADDRESS(bus, device, function, offset) \
    (0x80000000 | ((bus) << 16) | ((device) << 11) | ((function) << 8) | ((offset) & 0xFC))

// 读取PCI配置空间的函数
uint32_t pci_read_config_dword(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
    uint32_t address = PCI_CONF_ADDRESS(bus, device, function, offset);
	printf("bus: 0x%02X, device: 0x%02X, funtion: 0x%02X, offset: 0x%02X: generate IO address: 0x%08X\n", bus, device, function, offset, address);
    outl(address, PCI_CONFIG_ADDRESS);    // 向地址端口写入地址
    return inl(PCI_CONFIG_DATA);          // 从数据端口读取数据
}

// 获取用户输入的函数
void get_pci_parameters(uint8_t *bus, uint8_t *device, uint8_t *function, uint8_t *offset) {
    unsigned int bus_temp, device_temp, function_temp, offset_temp;
    
    printf("Please input (bus),(device),(function) and (offset: must align with 4.\n");
    scanf("%x %x %x %x", &bus_temp, &device_temp, &function_temp, &offset_temp);
    if (offset_temp % 4 != 0) {
        fprintf(stderr, "offset must align with 4.\n");
        exit(1);
    }
    *bus = (uint8_t)bus_temp;
    *device = (uint8_t)device_temp;
    *function = (uint8_t)function_temp;
    *offset = (uint8_t)offset_temp;
}

int main() {
    // 检查I/O端口访问权限
    if (iopl(3) != 0) {
        perror("iopl");
        return 1;
    }

    uint8_t bus, device, function, offset;

    // 获取用户输入的PCI参数
    get_pci_parameters(&bus, &device, &function, &offset);

    // 读取并打印配置空间的内容
    uint32_t value = pci_read_config_dword(bus, device, function, offset);
    printf("bus: 0x%02X, device: 0x%02X, funtion: 0x%02X, offset: 0x%02X: value: 0x%08X\n", bus, device, function, offset, value);

    return 0;
}

  在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配置空间。
在这里插入图片描述
可以看到从0xe0000000-0xefffffff,总共256M大小的memory空间被映射成了配置空间,大小是256M的原因是PCIe spec规定bus占用8bit(256个bug),device占用5bit(232个device),function占用3bit(8个function),每个function有4KB的配置空间。总共=256bus32device8function*4KB=256M

可以通过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)。
在这里插入图片描述
关于IO方式和memory方式访问PCIe配置空间的具体实现,参考
https://blog.csdn.net/huangkangying/article/details/50570612
https://blog.csdn.net/mao0514/article/details/26072229
https://blog.csdn.net/xingqingly/article/details/45695739
https://zhuanlan.zhihu.com/p/34047690
http://developer.amd.com/wordpress/media/2012/10/pci%20-%20pci%20express%20configuration%20space%20access.pdf

  • 88
    点赞
  • 414
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linjiasen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值