NTB有两个重要的应用
(1)PCI于隔离,即可以隔离出两个或者多个PCI系统
(2)实现地址翻译。
这篇文章注重说一下地址翻译,关于NTB一些介绍和应用见:
https://blog.csdn.net/linjiasen/article/details/104532342
https://mp.weixin.qq.com/s/0Ais2S-GnFfsWRnoWhjRzw
https://mp.weixin.qq.com/s/-AAxGslLt0nZkVCn3KWI7Q
https://mp.weixin.qq.com/s/VBRDnpJG5EmI8O2Ple1Xeg
下图就是用PCIe switch NTB+DMA组建的NTB back to back的框图
对于NTB的来说,可以做地址映射的BAR0、BAR1、BAR2/3、BAR4/5(看芯片设计到底用哪些BAR做reg映射,哪些BAR做address映射)在Primary端(virtual端)和Secondary端(link端)各有3个寄存器(有些芯片是2个寄存器,其实BAR CTRL和BAR LMT可以合成一个reg,没有道理说你弄一个很大的BAR,只开一个小的窗口,不用也是浪费)
(1)BAR CTRL reg决定了BAR SIZE。和base address reg共同组成NTB BARx的memory窗口。
(2)BAR LMT reg决定了NTB的转发窗口,一般转发窗口和大小和BAR SIZE一样大。
(3)BAR XLAT reg决定往对端映射时的起始地址。
这样DMA或者CPU写NTB BARx对应的offset,就会被NTB转发到对控映射的起始地址+offset的位置,这样就构成了一个简单存储系统的镜像模型了。当然实际工程中存储的镜像远比这个复杂,不过原理是一样的,只是有很多可靠性的问题需要解决。
举个例子吧,我使用的单NTB系统,即一控提供NTB,一控提供TB,为了方便区分,提供NTB的我们叫主控,提供TB的我们叫从控。逻辑上主控的CPU只能枚举到NTB primary端,NTB的secondary端是从控的CPU枚举的(单NT在实际产品中有很多问题,因为需要主控起来了,从控再启动,这样从控才能枚举到NTB secondary端,或者自己写driver来枚举NTB 设备)
NTB PRI的NTB BAR的地址如下:
Region 0: Memory at dce00000 (32-bit, non-prefetchable) [size=32K]
Region 1: Memory at dcd00000 (32-bit, non-prefetchable) [size=1M]
Region 2: Memory at 1be00000000 (64-bit, prefetchable) [size=4G]
Region 4: Memory at 1bc00000000 (64-bit, prefetchable) [size=8G]
我把primary端NTB BAR23(1be00000000~1be00000000+4G-1)映射到从控的0x5cd00000这个地址(下图的XLAT23),这地址其实是NTB SEC端的NTB BAR0(dcd00000)减2G的位置。也就是说我们把primary端的NTB BAR23映射到secondary端的BAR0-2G的位置(这个地址可以随意改)。 这样映射完毕后,主端NTB BAR23就映射到从控NTB BAR0减2G的位置了。
NTB SEC的NTB BAR的地址如下:
Region 0: Memory at dcd00000 (32-bit, non-prefetchable) [size=32K]
Region 1: Memory at dcc00000 (32-bit, non-prefetchable) [size=1M]
Region 2: Memory at fc00000000 (64-bit, prefetchable) [size=4G]
Region 4: Memory at fa00000000 (64-bit, prefetchable) [size=8G]
注意关闭IOMMU功能,目前linux 中对于AMD和intel的IOMMU的支持都有点问题,开了IOMMU访问NTB是有问题的。
这里为啥0x438寄出器的值修改成0x123456后读取出来是0x123000呢,是因为这个reg的低12bit是RO的,默认值就是0.
上面主从控的值是对应的,也证明我们成功把主端NTB BAR23映射到从控NTB BAR0减2G的位置了。
NTB的地址转换其实就是这么简单,你可以把对控任何一个地址映射过来,只要这个地址是可以访问的。如果是X86系统下,其实最简单的方式是把对控的0地址映射过来测试一下NTB是否正常,因为X86前面的4K的地址是reserved,是可以正常读写的。
如果对控的地址是host的main memory地址,你去对控用busybox devmem访问main memory则需要hack系统把限制访问给拿掉。hack系统有两种方法,一种是你写个ko把busybox devmem在内核态的那个限制给override掉,一种是重新编译内核把限制给拿掉
hack系统的方法https://blog.csdn.net/linjiasen/article/details/103408631?ops_request_misc=&request_id=91b4390998ce4875a5daa6a57a380fce&biz_id=&utm_medium=distribute.pc_search_result.none-task-blog-2blogkoosearch~default-1-103408631-null-null.268v1control&utm_term=%E6%89%80%E6%9C%89%E7%9A%84&spm=1018.2226.3001.4450
如果NTB转到对控的的地址是个MMIO,则需要用setpci -s bus:dev.fun 0x4=0x7把对应设备的memory access enable给打开
你可以通过NTB访问对控任意你映射的地址,如果做过工程,你就会懂得这句话杀伤力有多大了。NTB可以映射任何一个地址,是典型性的双刃剑,DMA+NTB的形式就可以访问对控的任何地址,用的好的话可以实现很多需求,用的不好,可能把对控搞死,比如踩内存啥的,由于是对控DMA发起的,到时候非常难定位。