查看“NVMe”的源代码
←
NVMe
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[https://nvmexpress.org/resources/specifications/ NVMe规范] 可以在这里找到。 关于这个页面的论坛帖子[https://forum.osdev.org/viewtopic.php?f=8&t=36366 在这里]。 ==概述== * NVMe控制器可以作为具有类代码1和子类代码8的PCI设备找到。 * 它的寄存器可以通过BAR 0进行访问(应该是64位内存IO)。 * 控制器处理从“submission queues(提交队列)”提交给它的命令(commands)。 驱动程序在内存中的队列的循环缓冲区中准备命令,然后更新队列的尾部指针寄存器。 * 控制器可以按照自己喜欢的任何顺序处理命令。 * 当控制器完成命令处理后,它会将一个条目附加到“完成队列(completion queue)”。 创建submission队列时指定要使用的completion队列。 当completion队列有可用命令时,控制器发送一个中断。 驱动程序处理队列的循环缓冲区中的所有新条目,然后更新队列的头指针寄存器。 * 重置时,存在仅一个submission队列和仅一个completion队列。 其它一些是管理队列(admin queues)。 驱动器在ASQ和ACQ寄存器中设置它们的基址。 * admin队列可以处理管理命令,例如创建IO队列 (用于提交IO命令,如读/写扇区),以及查询与之相连的控制器和驱动器 (称为 “命名空间(namespaces)”) 的信息。 * admin队列的标识符为0。 == BAR0 寄存器== {| border="1" cellpadding="5" |- !style="white-space:nowrap;" | 偏移 !style="white-space:nowrap;" | 名称 !style="white-space:nowrap;" | 说明 |- |align="center" |0x00-0x07 |align="center" |CAP | 控制器功能。(Controller capabilities) |- |align="center" |0x08-0x0B |align="center" |VS |Version. |- |align="center" |0x0C-0x0F |align="center" |INTMS |Interrupt mask set. |- |align="center" |0x10-0x13 |align="center" |INTMC |Interrupt mask clear. |- |align="center" |0x14-0x17 |align="center" |CC |Controller configuration. |- |align="center" |0x1C-0x1F |align="center" |CSTS |Controller status. |- |align="center" |0x24-0x27 |align="center" |AQA |Admin queue attributes. |- |align="center" |0x28-0x2F |align="center" |ASQ |Admin submission queue. |- |align="center" |0x30-0x37 |align="center" |ACQ |Admin completion queue. |- |align="center" |0x1000+(2X)*Y |align="center" |SQxTDBL |Submission queue X tail doorbell. |- |align="center" |0x1000+(2X+1)*Y |align="center" |CQxHDBL |Completion queue X head doorbell. |} Y是doorbell stride,在控制器功能寄存器(controller capabilities register)中指定。 ==数据结构== === Submission队列条目 === Submission队列条目 - 命令- 是64字节,按16个DWORD排列。 {| border="1" cellpadding="5" |- !style="white-space:nowrap;" |DWORD !style="white-space:nowrap;" |Contents |- |align="center" |0 |Command DWORD 0 (see below) |- |align="center" |1 |NSID (namespace identifier). If n/a, set to 0. |- |align="center" |2-3 |保留。 |- |align="center" |4-5 |Metadata pointer. |- |align="center" |6-9 |Data pointer. 2 PRPs (see next section). |- |align="center" |10-15 |Command specific. |} 命令DWORD 0的格式: {| border="1" cellpadding="5" |- !style="white-space:nowrap;" |Bits !style="white-space:nowrap;" |Contents |- |align="center" |0-7 |Opcode. |- |align="center" |8-9 |Fused operation. 0 indicates normal operation. |- |align="center" |10-13 |Reserved. |- |align="center" |14-15 |PRP or SGL selection. 0 indicates PRPs. |- |align="center" |16-31 |Command identifier. This is put in the completion queue entry. |} === PRP === 一个PRP (物理区域页-physical region page) 是一个64位的物理内存地址。 它必须与DWORD对齐。 PRP列表用于将数据传送到存储器中的特定位置,其中数据从存储器传送到存储器中/从存储器传送到存储器中。 PRP列表受以下规则约束: * 给定PRP指定的区域大小至少为: 可以在不越过页边界的情况下传输的数据量;以及剩余要传输的数据量。 * 只有PRP列表中的第一个条目可以是页面未对齐的。 * 如果PRP列表的长度不足以覆盖整个传输,则最后一个条目链接到包含更多PRP条目的页面。 === 完成队列条目(Completion queue entry) === completion队列条目为16字节。 {| border="1" cellpadding="5" |- !style="white-space:nowrap;" |Bits !style="white-space:nowrap;" |Contents |- |align="center" |0-31 |Command specific. |- |align="center" |32-63 |Reserved. |- |align="center" |64-79 |Submission queue head pointer. |- |align="center" |80-95 |Submission queue identifier. |- |align="center" |96-111 |Command identifier. |- |align="center" |112 |Phase bit. Toggled when entry written. |- |align="center" |113-127 |Status field. 0 on success. |} 可以通过检查阶段(phase)位来确定completion队列中新条目结束的位置。 == 命令(Commands) == === 管理命令(Admin commands) === ==== 创建IO submission队列 ==== * 操作码是0x01。 * 队列的基地址应放在命令的DWORD6和7中。 * 命令DWORD 10包含低字中的队列标识符,以及高字(high word)中的队列大小。 队列大小应小于实际值。 * 命令DWORD11在低位字中包含标志,在高位字中包含完成队列标识符(将发布此submission队列的完成条目)。 标志 (1 << 0) 表示队列在物理上是连续的 (推荐; 非连续不被所有控制器支持)。 ====创建IO completion队列==== * 操作码为0x05。 * 队列的基地址应放在命令的DWORDs 6和7中。 * 命令DWORD 10在低位字中包含队列标识符,在高位字中包含队列大小。 队列大小应该比实际值小1。 * 命令DWORD 11在低字中包含标志,在高字中包含中断向量。 标志(1<<0)表示队列在物理上是连续的(建议使用;并非所有控制器都支持非连续),标志(1<<1)启用中断。 ==== 标识(Identify) ==== * 操作码为0x06。 * 输出的基址(单页)应该放在命令的DWORD6和7中。 * 命令DWORD 10的低位字节指示要标识的内容:0-命名空间,1-控制器,2-命名空间列表。 * 如果标识名称空间,请将DWORD 1设置为名称空间ID。 === 输入输出命令(IO commands) === ==== 读取 ==== * 操作码为0x02。 * DWORD 1包含NSID。 * DWORD 6-9包含数据传输的PRP列表。 * DWORD 10-11包含起始LBA。 * DWORD 12的低字包含要传输的块数。这应该比实际值少一个。 ====写入==== * 操作码为0x01。 * DWORD 1包含NSID。 * DWORD6-9包含数据传输的PRP列表。 * DWORD 10-11包含起始LBA。 * DWORD 12的低字包含要传输的块数。这应该比实际值少一个。 == 检查表 == === 初始化 === * 找到具有类代码0x01和子类代码0x08的PCI函数。 * 为函数在PCI配置空间中启用中断、总线主控DMA和内存空间访问。 * 映射BAR0。 * 检查控制器版本是否支持。 * 检查功能寄存器是否支持NVMe命令集。 * 检查能力寄存器以获取对主机页面大小的支持。 * 重置控制器。 * 设置控制器配置和管理队列基本地址。 * 启动控制器。 * 启用中断并注册处理程序。 * 向控制器发送标识命令(identify command)。检查它是一个IO控制器。记录最大传输大小。 * 如果已实现,重置软件进度标记(software progress marker)。 * 创建第一个IO completion队列和第一个IO Submission队列。 * 识别活动名称空间ID,然后识别各个名称空间。记录它们的块大小、容量以及它们是否为只读。 ===关机=== * 删除IO队列。 * 通知控制器关机。 * 等待CSTS.SHST更新。 === 提交命令 === * 建立PRP列表。 * 在submission队列等候空间控制器在completion队列条目中指示其内部头指针。 * 设置命令。 * 更新队列tail doorbell寄存器。 === IRQ处理程序 === * 对于每个completion队列,读取已切换相位位的所有条目。 * 检查命令的状态。 * 使用submission队列ID和命令ID计算出该补全条目对应的是哪个提交的命令。 * 更新completion队列head doorbell寄存器。
返回至“
NVMe
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
变体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息