从零开始讲PCIe(4)——PCI总线的地址空间分配

一、概述

        PCI架构支持三种地址空间,如图1-10所示:内存地址空间(Memory Map)I/O地址空间(I/O Map)配置地址空间(PCI Configure Space)。在x86处理器中,处理器可以直接访问内存I/O空间。PCI设备可以映射到处理器的内存地址空间,支持32位或64位内存寻址。在I/O地址空间中,PCI支持32位地址,但由于x86 CPU只使用16位I/O地址空间,许多平台将I/O空间限制为64KB(16位地址的容量)。

        此外,PCI引入了第三种地址空间,称为配置空间,该空间只能通过间接方式访问。每个功能包含内部寄存器,用于配置空间的管理。这些寄存器为软件提供了标准化的地址和资源控制,使得PC真正实现了“即插即用”环境。每个PCI功能最多有256字节的配置地址空间。PCI支持每个设备最多8个功能、每条总线最多32个设备、每个系统最多256条总线,因此系统的总配置空间量为:
256 Bytes/function x 8 functions/device x 32 devices/bus x 256 buses/system = 16MB

        由于x86 CPU无法直接访问配置空间,必须通过I/O寄存器间接访问。传统模型中,如图1-10所示,使用位于地址CF8h-CFBh的I/O端口,称为配置地址端口,以及映射到地址CFCh-CFFh的配置数据端口

        需要注意的是,在PCI Express中,引入了一种新的方法,通过将配置空间映射到内存地址空间来访问配置空间,从而简化了访问过程。

二、PCI内存地址空间

        内存地址空间是最常用的地址空间之一,PCI设备可以将其资源(如设备寄存器、缓冲区等)映射到处理器的内存地址空间中,使CPU可以通过标准的内存读写指令访问PCI设备。PCI设备可以支持32位或64位内存寻址,这意味着它们可以使用32位或64位地址来映射设备资源。

        在这种情况下,PCI设备的内存区域会映射到处理器的内存空间,CPU可以通过访问这些内存地址与PCI设备交互。例如,显卡或网络接口卡(NIC)等设备会将其寄存器或内存缓冲区映射到系统内存地址空间中,CPU可以直接读写这些地址来控制设备或传输数据。

三、PCI I/O地址空间

        I/O地址空间是专门为I/O设备保留的空间,用于与外部设备通信。在x86系统中,CPU可以直接访问I/O地址空间,并使用特殊的INOUT指令与设备进行交互。PCI支持32位I/O地址空间,但由于x86 CPU的I/O空间通常限制为16位,这导致许多平台上的I/O地址空间被限制为64KB(即16位地址范围)。

        I/O空间通常用于较小的外围设备,例如串口、并口等传统设备,它们只需要少量的I/O寄存器。由于I/O空间有限,现代的大多数PCI设备倾向于使用内存映射的方式,而不是依赖于有限的I/O地址空间。

四、PCI配置地址空间

        每个PCI功能都有一个256字节的配置空间,用于存储设备的配置信息,如设备ID、供应商ID、状态、命令寄存器等。为了访问这个配置空间,操作系统和驱动程序会通过I/O地址0xCF8~0xCFF与PCI设备进行交互。

2.1 地址和数据寄存器

2.1.1 地址寄存器

        0xCF8~0xCFB(地址寄存器):总共4个字节(32位),用于指定要访问的PCI设备的配置空间的地址。这个地址寄存器会包含PCI总线号、设备号、功能号和配置空间中的寄存器偏移。

  • 第31位:启用位,必须设置为1。
  • 第30~24位:保留,通常为0。
  • 第23~16位:总线号,用于指定哪个PCI总线上的设备。
  • 第15~11位:设备号,用于指定总线上的设备(一个PCI总线上最多可以有32个设备)。
  • 第10~8位:功能号,用于选择设备的不同功能(每个设备最多有8个功能)。
  • 第7~2位:寄存器号,指定配置空间中的寄存器(以4字节为单位,每个寄存器占用4个字节)。
  • 第1~0位:保留位,必须为0。

2.1.2 数据寄存器

        0xCFC~0xCFF(数据寄存器):同样是4个字节(32位),用于读取或写入之前通过地址寄存器指定的PCI配置空间的数据。用于读取或写入通过地址寄存器(0xCF8)指定的PCI配置空间数据。读取或写入时,数据会被放置到这里。

        虽然在0xCF8~0xCFF范围内确实只有4个字节用作地址寄存器和4个字节用作数据寄存器,但它们是用于与设备的整个PCI配置空间交互的“窗口”,通过改变地址寄存器中的内容,可以访问PCI设备的256字节配置空间的任何部分。

2.2 工作机制

        由于I/O地址空间有限,传统模型在使用地址时非常保守。常见的做法是使用两个寄存器:一个用于指向设备内部的位置,另一个用于读取或写入数据。在PCI配置过程中,这涉及到两个步骤:

第一步:通过I/O写入配置地址

        CPU生成一次I/O写操作,将数据写入北桥的地址端口(I/O地址CF8h),以提供要访问的配置寄存器地址。这个地址包含三个主要部分,用于在PCI拓扑中定位特定的PCI功能(function):除此之外,还需要指定要访问该功能的配置空间中的哪一个64个双字(256字节)寄存器。

  • 总线号(Bus Number):用于访问256条可能的总线中的一条。
  • 设备号(Device Number):用于选择该总线上32个设备中的一个。
  • 功能号(Function Number):用于选择该设备中的8个功能中的一个。

第二步:通过I/O读或写访问数据

        CPU生成一个I/O读或I/O写操作,指向北桥的数据端口(I/O地址CFCh)。根据这一操作,北桥将生成一个配置读取或配置写入事务,发送到在地址端口中指定的PCI总线上。

        这种两步访问方法允许CPU通过I/O地址空间与PCI设备的配置空间进行交互。虽然I/O地址空间有限,但通过这种机制,系统可以有效地管理和配置多达256条总线、每条总线32个设备、每个设备最多8个功能的复杂系统。

2.3 举例说明

        假设我们要读取PCI总线0上的设备5的功能0的供应商ID(Vendor ID),它位于配置空间的偏移地址0x00~0x01

2.3.1 计算地址寄存器的值

        根据PCI配置空间的地址格式,我们需要构造要写入0xCF8的地址:

        将这些位组合起来,我们得到地址寄存器的值为:

  • 启用位:第31位 = 1
  • 总线号:总线0,所以第23~16位 = 0x00
  • 设备号:设备5,所以第15~11位 = 0x05
  • 功能号:功能0,所以第10~8位 = 0x00
  • 寄存器号:我们要读取供应商ID,它在寄存器偏移0x00,所以第7~2位 = 0x00
  • 保留位:第1~0位 = 0
0x80000000 | (0x00 << 16) | (0x05 << 11) | (0x00 << 8) | (0x00 << 2) = 0x80002000

2.3.2 向地址寄存器写入地址

        使用outl指令将0x80002000写入0xCF8,指定要访问的PCI设备地址:

outl(0x80002000, 0xCF8);

2.3.3 从数据寄存器读取数据

        供应商ID占用配置空间的前两个字节,因此我们可以从0xCFC读取前两字节:

uint16_t vendor_id = inl(0xCFC) & 0xFFFF;

        这个操作会从PCI配置空间的偏移地址0x00读取4字节数据,但我们只需要前两字节,所以我们通过& 0xFFFF只获取低16位,这就是供应商ID。

        假设读取到的数据是0x8086,那么供应商ID就是0x8086,这表明该设备由Intel制造,因为0x8086是Intel的供应商ID。

2.4 总结

        通过上述操作,我们成功从PCI总线0、设备5、功能0的配置空间读取了供应商ID。类似地,我们可以访问其他配置空间寄存器,比如设备ID、命令寄存器等。每次访问不同的寄存器时,只需调整地址寄存器中的寄存器号和其他位。

        这个机制使得操作系统和驱动程序能够方便地与PCI设备交互,从而进行设备初始化、配置和控制。

五、PCI Function配置寄存器空间

        每个PCI功能包含最多256字节的配置空间。在这256字节中,前64字节包含一个称为Header的结构体,而剩下的192字节用于支持可选功能。系统的配置首先由Boot ROM固件完成。当操作系统加载后,它可能会重新配置系统并重新分配资源,因此系统配置过程可能会被执行两次。

        根据Header类型的不同,PCI功能可以分为两大类:

  • Type 1 Header:用于标识一个桥接设备(bridge),这种功能创建了拓扑中的另一条总线,如图1-12所示。
  • Type 0 Header:用于标识一个非桥接设备(non-bridge function),如图1-13所示。

 

        这种Header类型信息存储在dword 3字节2中,是软件在系统中发现功能时(即进行枚举的过程)首先要检查的字段之一。枚举过程帮助操作系统或固件识别系统中存在的所有PCI功能,并根据其类型进行进一步的资源分配和管理。

        通过这种区分,系统可以识别出哪些设备是普通的I/O设备,哪些设备是桥接器,从而构建完整的总线拓扑结构并有效地进行设备配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

apple_ttt

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

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

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

打赏作者

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

抵扣说明:

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

余额充值