Compare commits

...
Author SHA1 Message Date
Leency 8a49573178 drivers/nvme: third review - LBA format, MSE, doorbells, timeouts
Test PR / Build (ru_RU) (pull_request) Successful in 1m55s
Test PR / Build (en_US) (pull_request) Successful in 2m1s
Test PR / Build (es_ES) (pull_request) Successful in 2m4s
- use the LBA format FLBAS selects, reject formats with metadata
- enable memory space before the first MMIO read
- write doorbells as dwords
- reset the controller on an I/O timeout, stop the NSID scan on one
- DiskDel and release PCIDEV.owner on DRV_EXIT
- NVMe prog IF only, decimal disk names, Delay while waiting on I/O
2026-09-30 12:45:47 +03:00
Leency 856cf6ad8d Merge remote-tracking branch 'origin/main' into nvme 2026-09-30 08:35:48 +03:00
Leency 1be5829c3f Merge remote-tracking branch 'origin/main' into nvme-clean 2026-09-26 06:23:21 +03:00
Leency af09303518 drivers/nvme: note the VirtualBox run in the README
Test PR / Build (ru_RU) (pull_request) Failing after 1m39s
Test PR / Build (es_ES) (pull_request) Successful in 2m18s
Test PR / Build (en_US) (pull_request) Successful in 2m21s
VirtualBox 7.2, controller 1.2.0, MDTS 0: disk tests pass, image correct on the
host, guest powers off.

Assisted-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-26 06:22:53 +03:00
Leency 33f6870b9a drivers/nvme: second review - polling only, serialised I/O, init fixes
A fresh pass over the driver, checked against how the kernel actually calls a
disk driver, found more than the first import had.

The interrupt is gone. The completion path is synchronous anyway (one command in
flight, the submitting thread drains the completion queue by phase tag), so INTx
bought nothing, and it froze the machine under QEMU: the kernel's shared-IRQ
heuristic relinks a handler that has not acknowledged an interrupt yet onto
whatever IRQ just fired unclaimed, if the handler says the interrupt is its own -
which an NVMe handler does whenever a completion is pending. Relinked to IRQ15
by an IDE interrupt during boot, the real level-triggered IRQ11 had no claimant
and stormed. INTx is now disabled in the PCI command register, every vector is
masked in INTMS, and no handler is registered. MSI-X is the interrupt to add
once the completion path becomes asynchronous.

Fixed along the way:
- START returned whatever eax held on failure; the kernel takes anything but 0
  for a service handle and writes into it, so a machine without an NVMe
  controller had four bytes of LOADDRV's image overwritten.
- The "anything loaded" flag reflected only the last controller, so a failing
  second controller unloaded a working first one whose disk was already
  registered.
- determine_active_nsids restored the wrong registers.
- The kernel calls read/write from any thread without serialising them; a
  per-controller mutex now does, since the driver keeps one command in flight.
- The Set Features check for the number of I/O queues asked for two of each and
  tested the answer with shl instead of shr; a controller allocating a single
  queue (VMware does) failed initialisation. One of each is requested now and
  any answer is accepted.
- CNTRLTYPE 0 ("not reported") was rejected on 1.4 controllers.
- The chained PRP list code was unreachable and wrong (a full list must end
  with the pointer to the next one); one list of up to 512 entries remains.
- Doorbell offsets were computed in 16-bit arithmetic; the MMIO mapping did not
  account for CAP.DSTRD; the high half of the 64-bit BAR was ignored.
- Capacity is now NSZE, not NCAP.
- A Flush command is issued when the kernel has written out its cache.
- A controller whose initialisation failed is disabled at once and skipped by
  the cleanup, which no longer bails out of its loop at the first such device.
- 512 KiB of per-command mutex pages that nothing used are not allocated.

Verified under QEMU (three boots, both disk images checked byte for byte on the
host, flush path traced), a boot without any NVMe controller, and on VMware
Workstation including the power-off.

Assisted-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-26 06:22:53 +03:00
Leency f489ab0e98 drivers/nvme: record that both upstream bugs were reproduced and fixed
Ran the last upstream build and this one against the same VMware
Workstation 17.6 machine, which is where issue #5 was reported:

  upstream  page fault inside the driver during the 2 MB transfer, then
            the machine never powers off - still running, and not a byte
            more on the debug serial line, 90 s after the shutdown
            request
  this      every disk test passes, the image is byte for byte what was
            written, and the machine powers off

VMware reports MDTS 8, so 2 MB is more than a single command may carry,
which the upstream build did not check.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-26 06:22:53 +03:00
Leency 5e9c156e35 drivers/nvme: fix the file corruption of GSoC issue #7
What decides whether PRP2 is a plain data pointer or a PRP List pointer is
how many memory pages the transfer touches, not how many sectors it
carries. The controller reads PRP2 as a data pointer whenever one page
holds everything PRP1 could not.

alloc_dptr decided by sector count instead, which agreed with the page
count only for a page aligned buffer. Give it an unaligned one carrying
more than a page worth of sectors while still landing inside two pages,
and it built a PRP List - which the controller then read as the data
itself, so everything past the first page went to or came from the list
page. That is the corruption reported as

  GSoC/kolibrios-nvme-driver#7

and it is why Burer asked for changes on pull request #91. Count pages
and decide on that.

Reproduced under QEMU by writing a 4608-byte file, which the kernel
handed over from a buffer 1536 bytes into a page: 2040 of its 4608 bytes
came out wrong on the disk image, starting exactly where the first page
ends, while the same file written over IDE was byte for byte correct.
With this change both buses produce identical, correct images, for nine
file sizes from 4608 to 20480 bytes.

Note that a round trip performed inside KolibriOS does not catch this:
the kernel buffer the file is read back into can still hold the correct
bytes from the write. What a transfer really left on the medium has to be
checked from outside.

Also addressed from the review of pull request #91: dropped the vim
modelines, removed get_log_page (which never submitted the command it
built) and create_namespace (which fell straight through into its own
error label), neither of which was called from anywhere, and wrote down
why the completion path is still a spinlock and what replacing it would
involve.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-26 06:22:52 +03:00
Leency 09398f0819 drivers: add NVMe driver
Imported from the Google Summer of Code 2024 project by Abdur-Rahman
Mansoor (https://git.kolibrios.org/GSoC/kolibrios-nvme-driver), at that
repository's commit 53c04b9, which is newer than pull request #91 and
already carries two PRP fixes the pull request does not have.

Fixed on the way in:

- PciWrite32 was called with three arguments instead of four when
  disabling MSI/MSI-X, writing a garbage value into PCI config space and
  unbalancing the caller's stack. Use the 16-bit accessors on the Message
  Control register, and walk the whole capability list instead of
  stopping at the first match, since a controller may expose both.
- CAP.MPSMIN and CAP.MPSMAX were masked one bit too high, so any
  controller reporting MPSMAX > 0 was rejected as unsupported.
- AQA.ASQS/ACQS and CDW10.QSIZE were written as plain sizes, but all
  three are 0's based, so the controller believed each queue held one
  entry more than was allocated and could fetch a command from past the
  end of the submission queue page.
- build_prp_list zeroed twice the memory it had allocated.
- The on-stack submission queue entry was zeroed four bytes too low:
  `esp` is the first argument of memsetdz, so `stdcall` pushes it last,
  after the size has already moved the stack pointer. CDW15 was left
  holding whatever the stack had.
- Reads and writes ignored IDENTC.MDTS. Split a request into chunks the
  controller accepts, which also keeps one plain PRP list enough to
  describe any transfer.
- The wait for shutdown processing spun while the completion bit was set
  rather than until it was, and reset the controller before waiting.
- No wait on the controller had a timeout, so a controller that never
  answers hung the boot. All of them are now bounded, enable and disable
  report failure, and their callers act on it.
- detect_nvme handed out uninitialised pcidev slots which the interrupt
  handler then walked.

Completions are now recognised by the phase tag instead of guessing from
the submission tail, and their status field is checked, so a failed
command is reported as a disk error instead of quietly returning stale
data. The driver still asks for a pin interrupt, but no longer depends on
one: if a completion does not arrive quickly the waiting thread drains
the queues itself, which is what makes the driver usable on a machine
whose controller is MSI-X only or whose firmware left the interrupt line
at 0xFF. Interrupts stay masked around a drain so the two sides cannot
consume the same entry, and so a pin nobody is going to service cannot
re-enter the handler forever.

Verified under QEMU against a 1.4.0 controller with the same FAT32 image
attached over both NVMe and IDE, the IDE run acting as the control:
partition detection, small and multi-megabyte file round trips, the same
with INTx generation forced off so only the polling path can complete a
command, and a boot on a machine with no NVMe controller at all. Real
hardware is still untested.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 02:47:48 +03:00
8 changed files with 2645 additions and 0 deletions

No files matched your search

+1
View File
@@ -593,6 +593,7 @@ tup.append_table(img_files, {
{"DRIVERS/SIS.SYS", VAR_DRVS .. "/audio/sis.sys"},
{"DRIVERS/SDHCI.SYS", "../drivers/sdhci/sdhci.sys"},
{"DRIVERS/I2CHID.SYS", VAR_DRVS .. "/i2chid/i2chid.sys"},
{"DRIVERS/NVME.SYS", VAR_DRVS .. "/nvme/nvme.sys"},
})
tup.append_table(extra_files, {
{"HD_Load/9x2klbr/", VAR_PROGS .. "/hd_load/9x2klbr/9x2klbr.exe"},
+1
View File
@@ -15,6 +15,7 @@
/SYS/TMPDISK A0 0 # Add virtual RAM disk /tmp0/1
/SYS/@RESHARE "" 0 # Shared resources
/SYS/LOADDRV SDHCI 0 # Start SDHCI driver
/SYS/LOADDRV NVME 0 # Start NVMe driver
/SYS/@DOCKY "" 0 # Start docky
/SYS/NETWORK/NETCFG A 0 # Init network driver
/SYS/NETWORK/@ZEROCONF "" 0 # Network configuration
+95
View File
@@ -0,0 +1,95 @@
# NVMe driver
Driver for NVM Express controllers. Registers the first active namespace of every
NVMe controller it finds as a KolibriOS disk named `nvme<controller>n<namespace>`, so
that of the first controller usually shows up as `/nvme0n1/`.
## Origin
Written by Abdur-Rahman Mansoor for Google Summer of Code 2024, developed at
<https://git.kolibrios.org/GSoC/kolibrios-nvme-driver> and proposed for the main tree
as <https://git.kolibrios.org/KolibriOS/kolibrios/pulls/91>. Imported here from that
repository's last commit (53c04b9, September 2024), which is newer than the pull
request itself.
Licensed under the GNU General Public License, version 2.
## State
| Feature | Implemented | Notes |
|------------------------------------|-------------|--------------------------------------------------------------------|
| Mandatory administrator commands | Yes | |
| Mandatory I/O commands | Yes | Reading and writing; requests are split to respect IDENTC.MDTS. |
| Namespace identification | Yes | Across all controller versions. |
| Multiple NVMe controllers | Yes | Up to `TOTAL_PCIDEVS`. |
| Multiple namespaces per controller | No | Only the first active namespace is registered. |
| Flush | Yes | Issued once the kernel has written out its own cache. |
| Interrupts | No | Completions are polled; INTx, MSI and MSI-X are all disabled. |
| Asynchronous API | No | Every command blocks the calling thread until it completes. |
| SMART/health reporting | No | |
Sector sizes other than 512 bytes are rejected, because that is what KolibriOS
supports, and so are LBA formats with metadata. The format checked is the one in use
(FLBAS), not the first in the table.
## Interrupts
The driver does not use one. A thread that submits a command spins briefly on its
completion and then reads the completion queues itself, using the phase tag; the
controller's interrupt vectors are masked and INTx is disabled in the PCI command
register, so the controller never signals anything. Since every command blocks its
caller anyway, an interrupt would not make a completion arrive any sooner, while
asking for one has real costs: many machines give an NVMe controller no usable INTx
at all (MSI-X only, or firmware leaving the interrupt line at 0xFF), and the kernel's
shared-IRQ heuristic can relink a handler onto the wrong line the first time an
unrelated IRQ fires while a completion is pending - after which a level-triggered
interrupt nobody services storms and freezes the machine. That last case was seen
under QEMU while the driver still registered a handler.
Once the completion path becomes asynchronous, an MSI-X vector is the interrupt to
add; there is nothing to gain from INTx before then.
Every wait on the controller (reset, enable, shutdown, command completion) is bounded,
so a controller that never answers makes the driver give up rather than hang the boot.
An I/O command that times out gets the controller reset and taken out of service, so
that it cannot DMA into freed memory or complete a stale command later.
Calls into the driver are serialised per controller, since the kernel may issue disk
requests from several threads at once and the driver keeps one command in flight.
## Testing
Verified under QEMU (`-device nvme`) against controller version 1.4.0: driver load,
partition detection, small and multi-megabyte file round trips, transfers from
unaligned buffers checked against what actually landed on the disk image, and a boot
of the same image with no NVMe controller present, where the driver has to back off
without disturbing anything else.
Also verified on VMware Workstation 17.6 (its controller reports firmware 1.3 and
MDTS 8): the disk tests pass and the machine powers off cleanly, where the last
upstream build faults during the 2 MB transfer and then hangs on shutdown. And on
VirtualBox 7.2 (controller version 1.2.0, MDTS 0, i.e. no limit of its own): the same
tests pass, the image is correct on the host, and the machine powers off. Real
hardware is still untested.
Watch out when writing tests for this: a round trip done from inside KolibriOS can pass
over corrupt data, because the kernel buffer the file was read back into may still hold
the correct bytes from the write. What a transfer really left on the medium has to be
checked from outside.
Two problems reported upstream are addressed here, both reproduced first and then
confirmed fixed by running the last upstream build and this one against the same
machine. The file corruption of
[issue #7](https://git.kolibrios.org/GSoC/kolibrios-nvme-driver/issues/7) was PRP2
being built as a list for transfers that fit in two memory pages, which the controller
reads as a plain data pointer. The shutdown hang of
[issue #5](https://git.kolibrios.org/GSoC/kolibrios-nvme-driver/issues/5) is visible on
VMware Workstation, where the upstream build also faults inside the driver during a
multi-megabyte transfer - VMware reports MDTS 8, so a 2 MB request is more than one
command may carry, and the request was not being split.
## Building
The driver builds with the tree (`drivers/nvme/Tupfile.lua`) and is copied into the
image as `DRIVERS/NVME.SYS`. To build it by hand:
fasm nvme.asm nvme.sys
kpack nvme.sys
+3
View File
@@ -0,0 +1,3 @@
if tup.getconfig("NO_FASM") ~= "" then return end
ROOT = "../.."
tup.rule("nvme.asm", "fasm %f %o " .. tup.getconfig("PESTRIP_CMD") .. tup.getconfig("KPACK_CMD"), "%B.sys")
+243
View File
@@ -0,0 +1,243 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc set_cdw0 stdcall, pci:dword, y:dword, opcode:byte
stdcall get_new_cid, [pci], [y]
shl eax, 16
or al, [opcode]
ret
endp
; See pages 161-205 of the NVMe 1.4 specification for reference
proc nvme_identify stdcall, pci:dword, nsid:dword, prp1:dword, cns:byte
push esi
mov esi, [pci]
mov dword [esi + pcidev.spinlock], 1
sub esp, sizeof.SQ_ENTRY
mov eax, esp
stdcall memsetdz, eax, sizeof.SQ_ENTRY / 4
mov eax, [nsid]
mov dword [esp + SQ_ENTRY.nsid], eax
mov eax, [prp1]
mov dword [esp + SQ_ENTRY.prp1], eax
stdcall set_cdw0, esi, ADMIN_QUEUE, ADM_CMD_IDENTIFY
mov dword [esp + SQ_ENTRY.cdw0], eax
mov al, [cns]
mov byte [esp + SQ_ENTRY.cdw10], al
stdcall sqytdbl_write, esi, ADMIN_QUEUE, esp
add esp, sizeof.SQ_ENTRY
stdcall nvme_poll, esi
pop esi
ret
endp
; See page 101 of the NVMe 1.4 specification for reference
proc create_io_completion_queue stdcall, pci:dword, prp1:dword, qid:dword, ien:byte
push esi
mov esi, [pci]
mov dword [esi + pcidev.spinlock], 1
sub esp, sizeof.SQ_ENTRY
mov eax, esp
stdcall memsetdz, eax, sizeof.SQ_ENTRY / 4
stdcall set_cdw0, esi, ADMIN_QUEUE, ADM_CMD_CRE_IO_COMPLETION_QUEUE
mov dword [esp + SQ_ENTRY.cdw0], eax
mov eax, [prp1]
mov dword [esp + SQ_ENTRY.prp1], eax
mov eax, (CQ_ENTRIES - 1) shl 16 ; CDW10.QSIZE (0's based)
or eax, [qid] ; CDW10.QID
mov dword [esp + SQ_ENTRY.cdw10], eax
movzx eax, [ien] ; CDW11.IEN
or eax, 0x1 ; CDW11.PC
; Don't set CDW11.IV since we're not using MSI-X or MSI vector
mov dword [esp + SQ_ENTRY.cdw11], eax
stdcall sqytdbl_write, esi, ADMIN_QUEUE, esp
add esp, sizeof.SQ_ENTRY
stdcall nvme_poll, esi
pop esi
ret
endp
; See page 103-104 of the NVMe 1.4 specification for reference
proc create_io_submission_queue stdcall, pci:dword, prp1:dword, qid:dword, cqid:word
push esi
mov esi, [pci]
mov dword [esi + pcidev.spinlock], 1
sub esp, sizeof.SQ_ENTRY
mov eax, esp
stdcall memsetdz, eax, sizeof.SQ_ENTRY / 4
stdcall set_cdw0, esi, ADMIN_QUEUE, ADM_CMD_CRE_IO_SUBMISSION_QUEUE
mov dword [esp + SQ_ENTRY.cdw0], eax
mov eax, [prp1]
mov dword [esp + SQ_ENTRY.prp1], eax
mov eax, (SQ_ENTRIES - 1) shl 16 ; CDW10.QSIZE (0's based)
or eax, [qid]
mov dword [esp + SQ_ENTRY.cdw10], eax
movzx eax, [cqid]
shl eax, 16 ; CDW11.CQID
or eax, 0x1 ; CDW11.PC (always set this to 1 as some devices may not support non-contiguous pages)
; TODO: Set CDW10.QPRIO
mov dword [esp + SQ_ENTRY.cdw11], eax
stdcall sqytdbl_write, esi, ADMIN_QUEUE, esp
add esp, sizeof.SQ_ENTRY
stdcall nvme_poll, esi
pop esi
ret
endp
; See page 95-96 of the NVMe 1.4 specification for reference
proc abort stdcall, pci:dword, cid:word, sqid:word
push esi
mov esi, [pci]
mov dword [esi + pcidev.spinlock], 1
sub esp, sizeof.SQ_ENTRY
mov eax, esp
stdcall memsetdz, eax, sizeof.SQ_ENTRY / 4
stdcall set_cdw0, esi, ADMIN_QUEUE, ADM_CMD_ABORT
mov dword [esp + SQ_ENTRY.cdw0], eax
movzx eax, [cid]
shl eax, 16
mov ax, [sqid]
mov dword [esp + SQ_ENTRY.cdw10], eax
stdcall sqytdbl_write, esi, ADMIN_QUEUE, esp
add esp, sizeof.SQ_ENTRY
stdcall nvme_poll, esi
pop esi
ret
endp
; See page 205 of the NVMe 1.4 specification for reference
proc set_features stdcall, pci:dword, prp1:dword, fid:byte, cdw11:dword
sub esp, sizeof.SQ_ENTRY
mov eax, esp
stdcall memsetdz, eax, sizeof.SQ_ENTRY / 4
stdcall set_cdw0, [pci], ADMIN_QUEUE, ADM_CMD_SET_FEATURES
mov dword [esp + SQ_ENTRY.cdw0], eax
mov eax, [prp1]
mov dword [esp + SQ_ENTRY.prp1], eax
movzx eax, [fid]
mov dword [esp + SQ_ENTRY.cdw10], eax
mov eax, [cdw11]
mov dword [esp + SQ_ENTRY.cdw11], eax
stdcall sqytdbl_write, [pci], ADMIN_QUEUE, esp
add esp, sizeof.SQ_ENTRY
ret
endp
; See page 105 of the NVMe 1.4 specification for reference
proc delete_io_completion_queue stdcall, pci:dword, qid:word
push esi
mov esi, [pci]
mov dword [esi + pcidev.spinlock], 1
sub esp, sizeof.SQ_ENTRY
mov eax, esp
stdcall memsetdz, eax, sizeof.SQ_ENTRY / 4
stdcall set_cdw0, esi, ADMIN_QUEUE, ADM_CMD_DEL_IO_COMPLETION_QUEUE
mov dword [esp + SQ_ENTRY.cdw0], eax
mov ax, [qid]
mov word [esp + SQ_ENTRY.cdw10], ax
stdcall sqytdbl_write, esi, ADMIN_QUEUE, esp
add esp, sizeof.SQ_ENTRY
stdcall nvme_poll, esi
pop esi
ret
endp
; See page 114-116 of the NVMe 1.4 specification for reference
proc get_features stdcall, pci:dword, prp1:dword, sel:byte, fid:byte
sub esp, sizeof.SQ_ENTRY
mov eax, esp
stdcall memsetdz, eax, sizeof.SQ_ENTRY / 4
stdcall set_cdw0, [pci], ADMIN_QUEUE, ADM_CMD_GET_FEATURES
mov dword [esp + SQ_ENTRY.cdw0], eax
movzx eax, [sel]
and eax, 111b
shl eax, 8 ; CDW10.SEL
mov al, [fid] ; CDW10.FID
mov dword [esp + SQ_ENTRY.cdw10], eax
mov eax, [prp1]
mov dword [esp + SQ_ENTRY.prp1], eax
; TODO: Implement CDW14.UUID?
stdcall sqytdbl_write, [pci], ADMIN_QUEUE, esp
add esp, sizeof.SQ_ENTRY
ret
endp
; See page 105-106 of the NVMe 1.4 specification for reference
proc delete_io_submission_queue stdcall, pci:dword, qid:word
push esi
mov esi, [pci]
mov dword [esi + pcidev.spinlock], 1
sub esp, sizeof.SQ_ENTRY
mov eax, esp
stdcall memsetdz, eax, sizeof.SQ_ENTRY / 4
stdcall set_cdw0, esi, ADMIN_QUEUE, ADM_CMD_DEL_IO_SUBMISSION_QUEUE
mov dword [esp + SQ_ENTRY.cdw0], eax
mov ax, [qid]
mov word [esp + SQ_ENTRY.cdw10], ax
stdcall sqytdbl_write, esi, ADMIN_QUEUE, esp
add esp, sizeof.SQ_ENTRY
stdcall nvme_poll, esi
pop esi
ret
endp
; See page 258-261 (read) and 269-271 (write) of the NVMe 1.4 specification for reference
proc nvme_io_rw stdcall, pci:dword, qid:word, nsid:dword, prps:qword, slba:qword, nlb:dword, opcode:dword
; TODO: Use IDENTC.NOIOB to construct read/write commands that don't
; cross the I/O boundary to achieve optimal performance
;
; TODO: Read AWUN/NAWUN
sub esp, sizeof.SQ_ENTRY
mov eax, esp
stdcall memsetdz, eax, sizeof.SQ_ENTRY / 4
movzx ecx, [qid]
stdcall set_cdw0, [pci], ecx, [opcode]
mov dword [esp + SQ_ENTRY.cdw0], eax ; CDW0
mov eax, dword [prps]
mov dword [esp + SQ_ENTRY.prp1], eax
mov eax, dword [prps + 4]
mov dword [esp + SQ_ENTRY.prp2], eax
mov eax, [nsid]
mov dword [esp + SQ_ENTRY.nsid], eax
mov eax, dword [slba] ; slba_lo
mov dword [esp + SQ_ENTRY.cdw10], eax
mov eax, dword [slba + 4] ; slba_hi
mov dword [esp + SQ_ENTRY.cdw11], eax
mov eax, [nlb]
mov word [esp + SQ_ENTRY.cdw12], ax
movzx ecx, [qid]
stdcall sqytdbl_write, [pci], ecx, esp
add esp, sizeof.SQ_ENTRY
ret
endp
+34
View File
@@ -0,0 +1,34 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc memsetdz stdcall, dest:dword, sz:dword
push edi
mov edi, [dest]
mov ecx, [sz]
xor eax, eax
rep stosd
pop edi
ret
endp
proc memcpyd stdcall, dest:dword, src:dword, sz:dword
push esi edi
mov esi, [src]
mov edi, [dest]
mov ecx, [sz]
rep movsd
pop edi esi
ret
endp
File diff suppressed because it is too large. Load diff
+602
View File
@@ -0,0 +1,602 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define LOG2 bsr
; NVMe Controller Versions
VS100 = 0x00010000 ; (v1.0.0)
VS110 = 0x00010100 ; (v1.1.0)
VS120 = 0x00010200 ; (V1.2.0)
VS121 = 0x00010201 ; (v1.2.1)
VS130 = 0x00010300 ; (v1.3.0)
VS140 = 0x00010400 ; (v1.4.0)
NVM_MPS = 0 ; Memory Page Size (2 ^ (12 + MPS))
NVM_ASQS = 64 ; Admin Submission Queue Size
NVM_ACQS = NVM_ASQS ; Admin Completion Queue Size
LAST_QUEUE_ID = 1 ; Index of the last queue
SQ_ENTRIES = NVM_ASQS ; I/O and Admin Submission Queue Size
CQ_ENTRIES = NVM_ACQS ; I/O and Admin Completion Queue Size
PAGE_SIZE = 4096 shl NVM_MPS ; Use 4KiB pages
SUPPORTED_LBADS = 9 ; KolibriOS only supports LBADS of 512, later on we may remove this restriction
SQ_ALLOC_SIZE = 0x1000
CQ_ALLOC_SIZE = 0x1000
QUEUE_ALLOC_SIZE = SQ_ALLOC_SIZE + CQ_ALLOC_SIZE
; Upper bound on the number of memory pages a single I/O command may span. A PRP
; list holds PAGE_SIZE/8 entries, so staying well below that keeps every transfer
; describable by one non-chained PRP list.
MAX_PRP_PAGES = 256
PCI_MAX_CAPS = 48 ; a config space holds at most 48 capabilities
; Registers take the first page of the BAR, the doorbells start at 0x1000 with a
; stride of 4 << CAP.DSTRD bytes; this much is mapped before CAP.DSTRD is known.
MMIO_MIN_MAP_SIZE = 0x2000
; How long to wait on the controller, in milliseconds.
CTRL_POLL_MS = 10
SHUTDOWN_WAIT_MS = 5000
IO_TIMEOUT_MS = 30000 ; how long a single I/O command may take
POLL_SPINS = 64 ; lock checks between two looks at the completion queue
IO_BUSY_ROUNDS = 20000 ; rounds to spend spinning before falling back to sleeping
MSIXCAP_CID = 0x11
MSIXCAP_MXE = 1 shl 15 ; MSI-X Enable bit
MSICAP_CID = 0x05
MSICAP_MSIE = 1 ; MSI Enable bit
ADMIN_QUEUE = 0 ; Admin Queue ID
IEN_ON = 2
IEN_OFF = 0
; Opcodes for NVM commands
NVM_CMD_FLUSH = 0x00
NVM_CMD_WRITE = 0x01
NVM_CMD_READ = 0x02
NVM_CMD_WRITE_UNCORRECTABLE = 0x04
NVM_CMD_COMPARE = 0x05
NVM_CMD_WRITE_ZEROES = 0x08
NVM_CMD_DATASET_MANAGEMENT = 0x09
NVM_CMD_VERIFY = 0x0C
NVM_CMD_RESERVATION_REG = 0x0D
NVM_CMD_RESERVATION_REPORT = 0x0E
NVM_CMD_RESERVATION_ACQUIRE = 0x11
NVM_CMD_RESERVATION_RELEASE = 0x15
NVM_CMD_COPY = 0x19
; Opcodes for admin commands (Page 94 of NVMe 1.4 spec)
ADM_CMD_DEL_IO_SUBMISSION_QUEUE = 0x00
ADM_CMD_CRE_IO_SUBMISSION_QUEUE = 0x01
ADM_CMD_GET_LOG_PAGE = 0x02
ADM_CMD_DEL_IO_COMPLETION_QUEUE = 0x04
ADM_CMD_CRE_IO_COMPLETION_QUEUE = 0x05
ADM_CMD_IDENTIFY = 0x06
ADM_CMD_ABORT = 0x08
ADM_CMD_SET_FEATURES = 0x09
ADM_CMD_GET_FEATURES = 0x0A
; fuse (fused operation): In a fused operation, a complex command is created by 'fusing' together
; two simpler commands. This field specifies whether this command is part
; of a fused operation, and if so, which command it is in the sequence:
; 00b -> Normal operation
; 01b -> Fused operation, first command
; 10b -> Fused operation, second command
; 11b -> Reserved
NO_FUSE = 0
FUSE_OP_FIRST_CMD = 1 shl 8
FUSE_OP_SECOND_CMD = 2 shl 8
; sel (PRP or SGL for data transfer): This field specifies whether PRPs or SGLs are used for any
; data transfer associated with the command. PRPs shall be
; used for all Admin commands for NVMe over PCIe implementations.
; SGLs shall be used for all Admin and I/O commands for NVMe over
; Fabrics implementations (i.e., field set to 01b):
; 00b -> PRPs are used for this transfer
; 01b -> SGLs are used for this transfer, MPTR will contain address of
; a single contiguous physical buffer that is byte aligned
; 10b -> SGLs are used for this transfer. MPTR will contain address of
; an SGL segment containing exactly one SGL descriptor that is
; QWORD aligned
; 11b -> Reserved
SEL_PRP = 0
SEL_SGL = 1 shl 14
; Controller or Namespace Structure (CNS) specifies the information to be returned to the host.
CNS_IDNS = 0x0 ; Namespace data structure (NSID)
CNS_IDCS = 0x1 ; Controller data structure
CNS_ANIDL = 0x2 ; Active namespace ID list (NSID)
CNS_NIDL = 0x3 ; Namespace identification descriptor list (NSID)
CNS_NVM_SL = 0x4 ; NVM Set List
; Optional Admin Command Support (OACS) values
OACS_SEC_SEN_RECV_SUPPORTED = 1 shl 0
OACS_FMT_NVM_SUPPORTED = 1 shl 1
OACS_FIRM_COMDL_SUPPORTED = 1 shl 2
OACS_NSMAN_SUPPORTED = 1 shl 3
; scope is all attached namespaces or all namespaces in NVM subsystem
NSID_BROADCAST = 0xFFFFFFFF
NSSRC_RESET = 0x4E564D65 ; "NVMe" (initiates a NVMe subsystem reset)
; NVMe Capabilities
CAP_MQES = 0xffff
CAP_CQR = 1 shl 16
CAP_AMS = (1 shl 17) or (1 shl 18)
CAP_TO = 0xff000000
CAP_DSTRD = 1 or (1 shl 1) or (1 shl 2) or (1 shl 3)
CAP_NSSRS = 1 shl 4
CAP_CSS_NVM_CMDSET = 1 shl 5
CAP_CSS_NOIO = 1 shl 12
CAP_BPS = 1 shl 13
CAP_CPS_COSCOP = 1 shl 15
CAP_CPS_DOSCOP = 1 shl 16
CAP_CPS_NVMSCOP = CAP_CPS_COSCOP or CAP_CPS_DOSCOP
CAP_MPSMIN = 0xf shl 16 ; CAP bits 51:48
CAP_MPSMAX = 0xf shl 20 ; CAP bits 55:52
CAP_PMRS = 1 shl 24
CAP_CMBS = 1 shl 25
CAP_NSSS = 1 shl 27
CAP_CRMS_CRWMS = 1 shl 28
CAP_CRMS_CRIMS = 1 shl 29
; Controller Configuration Bits
CC_EN = 1
CC_CSS = (1 shl 4) or (1 shl 5) or (1 shl 6)
CC_MPS = (1 shl 7) or (1 shl 8) or (1 shl 9) or (1 shl 10)
CC_AMS = (1 shl 11) or (1 shl 12) or (1 shl 13)
CC_SHN = (1 shl 14) or (1 shl 15)
CC_IOSQES = (1 shl 16) or (1 shl 17) or (1 shl 18) or (1 shl 19)
CC_IOCQES = (1 shl 20) or (1 shl 21) or (1 shl 22) or (1 shl 23)
CC_CRIME = 1 shl 24
CC_SHN_NORMAL_SHUTDOWN = 1 shl 14
CC_SHN_ABRUPT_SHUTDOWN = 1 shl 15
CC_DEFAULT_IOSQES = LOG2 sizeof.SQ_ENTRY shl 16
CC_DEFAULT_IOCQES = LOG2 sizeof.CQ_ENTRY shl 20
; Completion Queue Entry Status Field Values
CQ_PHASE_TAG = 1 shl 0
CQ_STATUS_SC = 0xfe
CQ_STATUS_SCT = (1 shl 9) or (1 shl 10) or (1 shl 11)
CQ_STATUS_CRD = (1 shl 12) or (1 shl 13)
CQ_STATUS_M = 1 shl 14
CQ_STATUS_DNR = 1 shl 15
; Completion Queue Entry Status Field - Status Code Type Values
CQ_STATUS_SCT_GCS = 0x0 ; Generic Command Status
CQ_STATUS_SCT_CSS = 0x1 ; Command Specific Status
CQ_STATUS_SCT_MADIE = 0x2 ; Media and Data Integrity Errors
CQ_STATUS_SCT_PRS = 0x3 ; Path Related Status
; Completion Queue Entry Status Field - Status Code Generic Command Values
CQ_STATUS_SC_GCS_SUCCESS = 0x00 ; Successful Completion
CQ_STATUS_SC_GCS_ICOP = 0x01 ; Invalid Command Opcode
CQ_STATUS_SC_GCS_IFIC = 0x02 ; Invalid Field in Command
CQ_STATUS_SC_GCS_CIDC = 0x03 ; Command ID Conflict
CQ_STATUS_SC_GCS_DTE = 0x04 ; Data Transfer Error
CQ_STATUS_SC_GCS_CAPLN = 0x05 ; Commands Aborted due to Power Loss Notification
CQ_STATUS_SC_GCS_INERR = 0x06 ; Internal Error
CQ_STATUS_SC_GCS_CAR = 0x07 ; Command Abort Requested
CQ_STATUS_SC_GCS_CASQD = 0x08 ; Command Aborted due to SQ Deletion
CQ_STATUS_SC_GCS_CAFFC = 0x09 ; Command Aborted due to Failed Fused Command
CQ_STATUS_SC_GCS_CAMFC = 0x0A ; Command Aborted due to Missing Fused Command
CQ_STATUS_SC_GCS_INNOF = 0x0B ; Invalid Namespace or Format
CQ_STATUS_SC_GCS_CSE = 0x0C ; Command Sequence Error
CQ_STATUS_SC_GCS_INSGL = 0x0D ; Invalid SGL Segment Descriptor
CQ_STATUS_SC_GCS_INNSGL = 0x0E ; Invalid Number of SGL Descriptors
CQ_STATUS_SC_GCS_OPDEN = 0x15 ; Operation Denied
CQ_STATUS_SC_GCS_NSIWP = 0x20 ; Namespace is Write Protected
CQ_STATUS_SC_GCS_CINT = 0x21 ; Command Interrupted
CQ_STATUS_SC_GCS_TTE = 0x22 ; Transient Transport Error
; Completion Queue Entry Status Field - Status Code Media and Data Integrity Errors
CQ_STATUS_SC_MADIE_WF = 0x80 ; Write Fault
CQ_STATUS_SC_MADIE_URE = 0x81 ; Unrecovered Read Error
CQ_STATUS_SC_MADIE_ACDEN = 0x86 ; Access Denied
CQ_STATUS_SC_MADIE_DOULB = 0x87 ; Deallocated or Unwritten Logical Block
; Controller Status (CSTS) Values
CSTS_RDY = 1
CSTS_CFS = 1 shl 1
CSTS_SHST = (1 shl 2) or (1 shl 3)
CSTS_NSSRO = 1 shl 4
CSTS_PP = 1 shl 5
CSTS_SHST_SHUTDOWN_OCCURRING = 1 shl 2
CSTS_SHST_SHUTDOWN_COMPLETE = 1 shl 3
; Admin Queue Attributes (AQA) Values
AQA_ASQS = 0xfff
AQA_ACQS = 0xfff shl 16
; CDW10.SEL Values (Page 115 of NVMe 1.4 specification)
CDW10_SEL_CURRENT = 000b
CDW10_SEL_DEFAULT = 001b
CDW10_SEL_SAVED = 010b
CDW10_SEL_SUPPORTED_CAPABILITIES = 011b
; Feature Identifiers (FID) Values (Page 206 of NVMe 1.4 specification)
; Used in Get/Set Features Commands
FID_ARBITRATION = 0x01
FID_POWER_MANAGEMENT = 0x02
FID_LBA_RANGE_TYPE = 0x03
FID_TEMPERATURE_THRESHOLD = 0x04
FID_ERROR_RECOVERY = 0x05
FID_VOLATILE_WRITE_CACHE = 0x06
FID_NUMBER_OF_QUEUES = 0x07
FID_INTERRUPT_COALESCING = 0x08
FID_INTERRUPT_VECTOR_CONFIGURATION = 0x09
FID_WRITE_ATOMICITY_NORMAL = 0x0A
FID_ASYNCHRONOUS_EVENT_CONFIGURATION = 0x0B
FID_AUTONOMOUS_POWER_STATE_TRANSITION = 0x0C
FID_HOST_MEMORY_BUFFER = 0x0D
FID_TIMESTAMP = 0x0E
FID_KEEP_ALIVE_TIMER = 0x0F
FID_HOST_CONTROLLED_THERMAL_MANAGEMENT = 0x10
FID_NON_OPERATIONAL_POWER_STATE_CONFIG = 0x11
FID_READ_RECOVERY_LEVEL_CONFIG = 0x12
FID_PREDICTABLE_LATENCY_MODE_CONFIG = 0x13
FID_PREDICTABLE_LATENCY_MODE_WINDOW = 0x14
FID_LBA_STATUS_INFORMATION_REPORT_INTERVAL = 0x15
FID_HOST_BEHAVIOR_SUPPORT = 0x16
FID_SANITIZE_CONFIG = 0x17
FID_ENDURANCE_GROUP_EVENT_CONFIGURATION = 0x18
; NVM Command Set Specific - FID
FID_SOFTWARE_PROGRESS_MARKER = 0x80
FID_HOST_IDENTIFIER = 0x81
FID_RESERVATION_NOTIFICATION_MASK = 0x82
FID_RESERVATION_PERSISTENCE = 0x83
FID_NAMESPACE_WRITE_PROTECTION_CONFIG = 0x84
; Get Log Page - Log Page Identifiers (Page 118-119 of NVMe 1.4 specification)
LID_ERROR_INFORMATION = 0x01
LID_SMARTHEALTH_INFORMATION = 0x02
LID_FIRMWARE_SLOT_INFORMATION = 0x03
LID_CHANGED_NAMESPACE_LIST = 0x04
LID_COMMANDS_SUPPORTED_AND_EFFECTS = 0x05
LID_DEVICE_SELF_TEST = 0x06
LID_TELEMETRY_HOST_INITIATED = 0x07
LID_TELEMETRY_CONTROLLER_INITIATED = 0x08
LID_ENDURANCE_GROUP_INFORMATION = 0x09
LID_PREDICTABLE_LATENCY_PER_NVM_SET = 0x0A
LID_PREDICTABLE_LATENCY_EVENT_AGGREGATE = 0x0B
LID_ASYMMETRIC_NAMESPACE_ACCESS = 0x0C
LID_PERSISTENT_EVENT_LOG = 0x0D
LID_LBA_STATUS_INFORMATION = 0x0E
LID_ENDURANCE_GROUP_EVENT_AGGREGATE = 0x0F
; I/O Command Set Specific - Log Page Identifiers
LID_RESERVATION_NOTIFICATION = 0x80
LID_SANITIZE_STATUS = 0x81
; Controller Type Values
CNTRLTYPE_IO_CONTROLLER = 0x1
CNTRLTYPE_DISCOVERY_CONTROLLER = 0x2
CNTRLTYPE_ADMIN_CONTROLLER = 0x3
struct NVME_MMIO
CAP dq ? ; Controller Capabilities
VS dd ? ; Version
INTMS dd ? ; Interrupt Mask Set
INTMC dd ? ; Interrupt Mask Clear
CC dd ? ; Controller Configuration
rd 1 ; Reserved
CSTS dd ? ; Controller Status
NSSR dd ? ; NVM Subsystem Reset
AQA dd ? ; Admin Queue Attributes
ASQ dq ? ; Admin Submission Queue Base Address
ACQ dq ? ; Admin Completion Queue Base Address
CMBLOC dd ? ; Controller Memory Buffer Location
CMBSZ dd ? ; Controller Memory Buffer Size
BPINFO dd ? ; Boot Partition Information
BPRSEL dd ? ; Boot Partition Read Select
BPMBL dq ? ; Boot Partition Memory Buffer Location
CMBMSC dd ? ; Controller Memory Buffer Memory Space
CMBSTS dd ? ; Controller Memory Buffer Status
rb 3492 ; Reserved
PMRCAP dd ? ; Persistent Memory Capabilities
PMRCTL dd ? ; Persistent Memory Region Control
PMRSTS dd ? ; Persistent Memory Region Status
PMREBS dd ? ; Persistent Memory Region Elasticity Buffer Size
PMRSWTP dd ? ; Persistent Memory Region Sustained Write Throughput
PMRMSC dq ? ; Persistent Memory Region Controller Memory Space Control
rb 484 ; Reserved
SQ0TDBL dd ? ; Submission Queue 0 Tail Doorbell (Admin)
ends
; Submission Queue Entry (64 bytes)
struct SQ_ENTRY
cdw0 dd ?
nsid dd ?
cdw2 dd ?
cdw3 dd ?
mptr dq ?
prp1 dq ?
prp2 dq ?
cdw10 dd ?
cdw11 dd ?
cdw12 dd ?
cdw13 dd ?
cdw14 dd ?
cdw15 dd ?
ends
; Completion Queue Entry (16 bytes) - See page 77 of the NVMe 1.4 spec
struct CQ_ENTRY
cdw0 dd ?
rd 1 ; reserved
sqhd dw ?
sqid dw ?
cid dw ?
status dw ?
ends
struct NSINFO
capacity dq ?
size dq ?
nsid dd ?
pci dd ?
lbads db ?
features db ?
ends
struct pcidev
bus db ?
devfn db ?
rb 2 ; align
num dd ?
io_addr dd ?
queue_entries dd ?
version dd ?
nsid dd ?
spinlock dd ?
nsinfo dd ?
last_status dd ? ; status field of the last completion, 0 when all went well
nn dd ?
last_cdw0 dd ? ; command specific result of the last completion
max_sectors dd ? ; biggest transfer a single I/O command may carry
disk dd ? ; handle from DiskAdd, 0 if none
pcidev dd ? ; the kernel's PCIDEV, claimed through its owner field
iolock MUTEX ; taken by whoever is using the I/O queue
dstrd db ?
mdts db ? ; Maximum Data Transfer Size, in 2^(12+MPSMIN) byte units
ready db ? ; 1 while the controller serves I/O; 0 before, after cleanup or once it hung
stuck db ? ; 1 if it hung and would not even go idle, so it may still DMA
hung db ? ; 1 once any command timed out, init and cleanup included
rb 3 ; align
ends
TOTAL_PCIDEVS = 4
TOTAL_PCIDEVS_MALLOC_SZ = TOTAL_PCIDEVS * sizeof.pcidev
struct NVM_QUEUE_ENTRY
tail dw ?
head dw ?
sq_ptr dd ?
cq_ptr dd ?
phase db ? ; phase tag the next completion of this queue will carry
rb 19 ; pad to a power of two, the queue index is shifted by LOG2 of it
ends
; Identify Controller Data Structure
struct IDENTC
vid dw ?
ssvid dw ?
sn dt ?, ?
mn rt 4
fr dq ?
rab db ?
ieee db ?, ?, ?
cmic db ?
mdts db ?
cntlid dw ?
ver dd ?
rtd3r dd ?
rtd3e dd ?
oaes dd ?
ctratt dd ?
rrls dw ?
rb 9 ; reserved
cntrltype db ?
fguid dq ?, ?
crdt1 dw ?
crdt2 dw ?
crdt3 dw ?
rb 106 ; reserved
rb 16 ; reserved (NVMMI)
oacs dw ?
acl db ?
aerl db ?
frmw db ?
lpa db ?
elpe db ?
npss db ?
avscc db ?
apsta db ?
wctemp dw ?
cctemp dw ?
mtfa dw ?
hmpre dd ?
hmmin dd ?
tnvmcap dq ?, ?
unvmcap dq ?, ?
rpmbs dd ?
edstt dw ?
dsto db ?
fwug db ?
kas dw ?
hctma dw ?
mntmt dw ?
mxtmt dw ?
sanicap dd ?
hmminds dd ?
hmmaxd dw ?
nsetidmax dw ?
endgidmax dw ?
anatt db ?
anacap db ?
anagrpmax dd ?
nanagrpid dd ?
pels dd ?
rb 156
sqes db ?
cqes db ?
maxcmd dw ?
nn dd ?
oncs dw ?
fuses dw ?
fna db ?
vwc db ?
awun dw ?
awupf dw ?
nvscc db ?
nwpc db ?
acwu dw ?
rb 2
sgls dd ?
mnan dd ?
rb 224
subnqn rq 32
rb 768
rb 256
psd0 rq 4
psd1 rq 4
psd2 rq 4
psd3 rq 4
psd4 rq 4
psd5 rq 4
psd6 rq 4
psd7 rq 4
psd8 rq 4
psd9 rq 4
psd10 rq 4
psd11 rq 4
psd12 rq 4
psd13 rq 4
psd14 rq 4
psd15 rq 4
psd16 rq 4
psd17 rq 4
psd18 rq 4
psd19 rq 4
psd20 rq 4
psd21 rq 4
psd22 rq 4
psd23 rq 4
psd24 rq 4
psd25 rq 4
psd26 rq 4
psd27 rq 4
psd28 rq 4
psd29 rq 4
psd30 rq 4
psd31 rq 4
rb 1024
ends
; Identify Namespace Data Structure
struct IDENTN
nsze dq ?
ncap dq ?
nuse dq ?
nsfeat db ?
nlbaf db ?
flbas db ?
mc db ?
dpc db ?
dps db ?
nmic db ?
rescap db ?
fpi db ?
dlfeat db ?
nawun dw ?
nawupf dw ?
nacwu dw ?
nabsn dw ?
nabo dw ?
nabspf dw ?
noiob dw ?
nvmcap dq ?
dq ?
npwg dw ?
npwa dw ?
npdg dw ?
npda dw ?
nows dw ?
rb 18
anagrpid dd ?
rb 3
nsattr db ?
nvmsetid dw ?
endgid dw ?
nguid dq ?
dq ?
eui64 dq ?
lbaf0 dd ?
lbaf1 dd ?
lbaf2 dd ?
lbaf3 dd ?
lbaf4 dd ?
lbaf5 dd ?
lbaf6 dd ?
lbaf7 dd ?
lbaf8 dd ?
lbaf9 dd ?
lbaf10 dd ?
lbaf11 dd ?
lbaf12 dd ?
lbaf13 dd ?
lbaf14 dd ?
lbaf15 dd ?
rb 3904
ends
; Namespace Granularity List (CNS 16h - Page 199 of NVMe specification 1.4)
struct NSGRANLS
nga dd ?
nod db ?
rb 27 ; reserved
ngd0 dq ?, ?
ngd1 dq ?, ?
ngd2 dq ?, ?
ngd3 dq ?, ?
ngd4 dq ?, ?
ngd5 dq ?, ?
ngd6 dq ?, ?
ngd7 dq ?, ?
ngd8 dq ?, ?
ngd9 dq ?, ?
ngd10 dq ?, ?
ngd11 dq ?, ?
ngd12 dq ?, ?
ngd13 dq ?, ?
ngd14 dq ?, ?
ngd15 dq ?, ?
ends
assert NVM_ASQS = NVM_ACQS
assert SQ_ENTRIES = NVM_ASQS
assert CQ_ENTRIES = NVM_ACQS
assert NVM_MPS = 0
assert CTRL_POLL_MS mod 10 = 0 ; nvme_poll waits it out in 1/100 s ticks
assert PAGE_SIZE = 0x1000
assert sizeof.NVME_MMIO = 4096
assert sizeof.SQ_ENTRY = 64
assert sizeof.CQ_ENTRY = 16
assert sizeof.IDENTC = 4096
assert sizeof.IDENTN = 4096
assert sizeof.NSGRANLS = 288
assert sizeof.NVM_QUEUE_ENTRY = 32