【实操】由浅入深,理解PCIe BAR(Base Address Register)(内含工具)
你可能听到过“PCIe BAR 空间”之类的说法,那么,它到底是什么呢?今天我们就来深入了解一下,并且,博主会提供一个自制工具,你可以实际操作一下,加深理解。
通过几个简单的问题,我们来了解一下 PCIe Base Address Register(BAR)。
为什么需要 BAR?
大家都知道,PCIe 包含非常多的寄存器,这些寄存器位于 4KiB 的空间之内,Host 可以通过 Configure Read 、 Configure Write 来操作这些寄存器。
但是,如果是一个工作在 PCIe 上层的协议或者设备,比如显卡、网卡、NVMe SSD 等,那 Host 和 Device 之间如何沟通呢?不太可能使用 PCIe 寄存器,一来寄存器基本都是 PCIe 专用的,即使有一些 Vendor Specific 的,也很难被通用协议所使用;二来寄存器数量有限,在表达复杂协议时可能会捉襟见肘。
所以,需要一种区别于寄存器的方式,来为上层协议提供更灵活和多元的通信方式,这就是 Base Address Register 的用途,通过 BAR,Device 可以“借出”部分 memory 给 Host,而 Host 为这段 memory 分配地址空间,这样双方就可以通过这段 memory 实现更复杂的协议。
什么是 BAR?
BAR 是一组 PCIe 的寄存器,位于 Configuration Space Header 中,其 offset 010h - 024h 之间,对于 endpoint 而言,有 6 个 BAR,每一个 32bits。

BAR 可以表示 I/O space,也可以表示 Memory Space,I/O Space 基本上很少用,我们主要集中在 Memory Space,也就是说,BAR 传递和表达的是 Memory 类型的地址范围,对于 Memory Space 的 BAR,每一个寄存器的结构如下:

BAR 如何使用?
以 64 bit 地址,128KiB 大小,Prefetchable 类型的 memory 为例,看一下 host 如何为 device 分配地址空间。

自制实操工具
博主做了一个简单的工具,可以在 Linux 上操作 PCIe 设备的 BAR 空间,正好我的机器上有一个 NVMe SSD,我来操作一下:
展示地址范围
[ mingw@localhost pcix]$ ./pcix mem show -s 01:00.0
BAR0/1: 0x0000000085620000 - 0x000000008562ffff (64K, non-prefetchable, 64-bit)
ROM : 0x0000000085600000 - 0x000000008561ffff (128K, read-only)可以看到,这个设备申请了 128K,64bit 地址范围,并且支持 expansion rom(后续的文章我们会介绍,欢迎关注🙂)
读取 memory
我们读取前 128B,以 dw 格式展示:
[mingw@localhost pcix]$ sudo ./pcix mem read -s 01:00.0 --bar 0 --offset 0 --size 256 -d
# dword (32-bit), little-endian (CPU register value)
00000000 f0031fff 08004030 00020000 00000000 ....0@..........
00000010 00000000 00460001 00000000 00000001 ......F.........
00000020 00000000 001f001f 0dfa2000 00000001 ......... ......
00000030 7e3f5000 00000001 00000000 00000000 .P?~............
00000040 00000000 00000000 00000000 00000000 ................
00000050 00000000 00000000 00000000 00000000 ................
00000060 00000000 00000000 000000f0 00000000 ................
00000070 00000000 00000000 00000000 00000000 ................
上面通过自制工具读取到的数据,与 nvme cli 读取到的数据完全一致(第一个 DW 都是 0xF0031FFF),说明工具是可靠的。
[mingw@localhost pcix]$ sudo nvme show-regs /dev/nvme0
cap : 8004030f0031fff
version : 20000
cc : 460001
csts : 1
nssr : 0
intms : 0
intmc : 0
aqa : 1f001f
asq : 10dfa2000
acq : 17e3f5000
cmbloc : 0
cmbsz : 0
bpinfo : 0
bprsel : 0
bpmbl : 0
cmbmsc : 0
cmbsts : 0
pmrcap : 0
pmrctl : 0
pmrsts : 0
pmrebs : 0
pmrswtp : 0
pmrmscl : 0
pmrmscu : 0
写入数据
支持写入数据(高风险操作,需要使用者风险自担)
[mingw@localhost pcix]$ sudo ./pcix mem write -s 01:00.0 --bar 0 --offset 14 --value 00460000 -d
About to write 4 byte(s) to BAR0 of 0000:01:00.0 at offset 0x14:
data: 00 00 46 00
Continue? [y/N] y
Wrote 4 byte(s) to BAR0 at offset 0x14.
# dword (32-bit), little-endian (CPU register value)
项目地址
好的,以上就是这篇文章的全部内容,如果您觉得写得不错,欢迎点赞关注,另外,博主其他的文章也是用心写的,欢迎阅读~
更多文章,欢迎关注我的公众号:
