<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>http://wiki.foofun.cn//index.php?action=history&amp;feed=atom&amp;title=NVMe</id>
	<title>NVMe - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.foofun.cn//index.php?action=history&amp;feed=atom&amp;title=NVMe"/>
	<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=NVMe&amp;action=history"/>
	<updated>2026-04-04T06:27:48Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>http://wiki.foofun.cn//index.php?title=NVMe&amp;diff=1021&amp;oldid=prev</id>
		<title>Zhang3：创建页面，内容为“[https://nvmexpress.org/resources/specifications/ NVMe规范] 可以在这里找到。  关于这个页面的论坛帖子[https://forum.osdev.org/viewtopic.php?f=8&amp;t=36366 在这里]。  ==概述==  * NVMe控制器可以作为具有类代码1和子类代码8的PCI设备找到。 * 它的寄存器可以通过BAR 0进行访问（应该是64位内存IO）。 * 控制器处理从“submission queues（提交队列）”提交给它的命令（commands）。 驱动…”</title>
		<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=NVMe&amp;diff=1021&amp;oldid=prev"/>
		<updated>2022-03-24T12:07:20Z</updated>

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