作者 谭荣
在上电的过程中,每个Endpoint的内部地址空间都会通过内存映射(memory map)的方式映射到Host的内存中,SSD 内部与协议相关的所有寄存器都会被映射到Host的内存中。Host访问的address只需为该寄存器在Host内存中映射的地址,就能准确地访问ssd的寄存器。注意Host不是往自己内存的那个物理地址读取或写入东西,而是用那个物理地址作为寻址用,最后访问SSD controller的寄存器。
按NVME Spec对Host端到device端内存映射(memory map)的说明,SSD内部所有相关寄存器的映射都是在这些模块的基地址上进行偏移的,如BAR0、BAR1、MTAB、MPBA等。
那么具体是如何实现的呢?如图1以一个read命令举例,看懂Host如何访问device的SQ Tail Doorbell寄存器。
图1:
结合图1(SQID 4)与图2我们可以知道,Host要访问的地址是submission queue 4 tail doorbell。
图2:
Doorbeel Reg模块在Host端内存的首地址是{BAR0,BAR1}+1000,图2可以得出在该模块内部寄存器的偏移是1000h+ (2y * (4 <<CAP.DSTRD),其中y=4,那CAP.DSTRD的值是多少呢?
图3为DSTRD的Spec解释,不同厂商的sdk在Initialize the NVMe Controller Capabilities (CAP) register时,会对CAP.DSTRD进行初始化。这个值也可以通过抓取device上电行为得到,如图4。
图3:
图4:
最后一个问题,bar0和bar1是多少?NVME device在上电的时候会做一堆操作。如图5,Host通过Configuration Write向寄存器0x10写了一个地址,即BAR0=0XD1100000。
图5:
如图6,Host通过Configuration Write向寄存器0x14写了一个地址,即BAR1=0X00000000。
图6:
从图7可以看出0x10与0x14实际上就是bar0与bar1的偏移。
图7:
最后结合上面的参数做一次验证:submission queue 4 tail doorbel=
{BAR0,BAR1}+1000+Doorbell(寄存器内部偏移)={0XD1100000}+1000h+(2y * (4 <<CAP.DSTRD)
=0XD1101000+(2*4* (4 <<0)= 0XD1101000+32=0XD1101000+20h=0XD1101020。与图8完全吻合!
图8: