mirror of
https://git.missingno.dev/kolibrios-nvme-driver/
synced 2025-01-09 06:25:55 +01:00
245 lines
6.6 KiB
NASM
245 lines
6.6 KiB
NASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;;
|
|
;; Distributed under terms of the GNU General Public License ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;driver sceletone
|
|
|
|
format PE DLL native
|
|
entry START
|
|
|
|
API_VERSION equ 0 ;debug
|
|
SRV_GETVERSION equ 0
|
|
__DEBUG__ = 1
|
|
__DEBUG_LEVEL__ = 1
|
|
DRIVER_VERSION = 1
|
|
DBG_INFO = 1
|
|
|
|
; 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
|
|
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
|
|
|
|
; scope is all attached namespaces or all namespaces in NVM subsystem
|
|
NSID_BROADCAST = 0xFFFFFFFF
|
|
|
|
section ".flat" code readable writable executable
|
|
include "../proc32.inc"
|
|
include "../struct.inc"
|
|
include "../macros.inc"
|
|
include "../fdo.inc"
|
|
include "../pci.inc"
|
|
include "../peimport.inc"
|
|
|
|
struc ns_identify {
|
|
.nsze dd ?
|
|
.ncap dd ?
|
|
.nuse dd ?
|
|
}
|
|
|
|
; Submission Queue Entry (64 bytes)
|
|
struc cq_entry {
|
|
.cdw0 dd ?
|
|
.nsid dd ?
|
|
.reserved dq ?
|
|
.mptr dq ?
|
|
.prp1 dq ?
|
|
.prp2 dq ?
|
|
.cdw10 dd ?
|
|
.cdw11 dd ?
|
|
.cdw12 dd ?
|
|
.cdw13 dd ?
|
|
.cdw14 dd ?
|
|
.cdw15 dd ?
|
|
}
|
|
SUBMISSION_QUEUE_ENTRY_SIZE = 64
|
|
|
|
struc nvme_dev {
|
|
.pci_bus dd ?
|
|
.pci_devfn dd ?
|
|
.bar0 dd ?
|
|
.serial db 20
|
|
.model db 40
|
|
}
|
|
|
|
proc START c, reason:dword
|
|
cmp [reason], DRV_ENTRY
|
|
jne .exit
|
|
.entry:
|
|
push esi
|
|
DEBUGF DBG_INFO,"Detecting NVMe hardware...\n"
|
|
call detect
|
|
pop esi
|
|
test eax, eax
|
|
jz .exit
|
|
|
|
invoke RegService, my_service, service_proc
|
|
ret
|
|
.exit:
|
|
xor eax, eax
|
|
ret
|
|
endp
|
|
|
|
proc service_proc stdcall, ioctl:dword
|
|
|
|
mov ebx, [ioctl]
|
|
mov eax, [ebx+IOCTL.io_code]
|
|
cmp eax, SRV_GETVERSION
|
|
jne @F
|
|
|
|
mov eax, [ebx+IOCTL.output]
|
|
cmp [ebx+IOCTL.out_size], 4
|
|
jne .fail
|
|
mov dword [eax], API_VERSION
|
|
xor eax, eax
|
|
ret
|
|
@@:
|
|
.fail:
|
|
or eax, -1
|
|
ret
|
|
endp
|
|
|
|
proc nvme_identify stdcall, p_dev:dword, nsid:dword, cns:byte, cntid:word
|
|
sub esp, SUBMISSION_QUEUE_ENTRY_SIZE
|
|
push SUBMISSION_QUEUE_ENTRY_SIZE
|
|
push 0
|
|
push esp
|
|
call memset
|
|
pop esp
|
|
add esp, 8
|
|
mov eax, dword [nsid]
|
|
mov [esp + cq_entry.nsid], eax
|
|
mov eax, dword [prp1]
|
|
mov [esp + cq_entry.prp1], eax
|
|
mov eax, dword [prp2]
|
|
mov [esp + cq_entry.prp2], eax
|
|
; TODO: setting CID to 1 for now but later on keep a list of unique list of identifiers
|
|
mov [esp + cq_entry.cdw0], ADM_CMD_IDENTIFY or (1 shl 16)
|
|
; the Controller or Namespace structure (CNS) specifies the information to be returned to the host
|
|
mov eax, dword [cntid]
|
|
shl eax, 16
|
|
or eax, dword [cns]
|
|
mov [esp + cq_entry.cdw10], eax
|
|
add esp, SUBMISSION_QUEUE_ENTRY_SIZE
|
|
endp
|
|
|
|
proc detect
|
|
push ebx
|
|
invoke GetPCIList
|
|
mov edx, eax
|
|
.check_dev:
|
|
mov ecx, [eax+PCIDEV.class]
|
|
and ecx, 0x00ffff00 ; retrieve class/subclass code only
|
|
cmp ecx, 0x00010800 ; Mass Storage Controller - Non-Volatile Memory Controller
|
|
je .check_cap
|
|
.next_dev:
|
|
mov eax, [eax + PCIDEV.fd]
|
|
cmp eax, edx
|
|
jne .check_dev
|
|
|
|
; no more PCI devices to enumerate?
|
|
xor eax, eax
|
|
pop ebx
|
|
ret
|
|
.check_cap:
|
|
push eax
|
|
movzx ebx, [eax + PCIDEV.bus]
|
|
mov [pcidev_bus], ebx
|
|
movzx ebx, [eax + PCIDEV.devfn]
|
|
mov [pcidev_devfn], ebx
|
|
invoke PciRead16, [pcidev_bus], [pcidev_devfn], PCI_header00.status
|
|
test ax, 0x10 ; check capabilities list bit
|
|
jnz .got_cap
|
|
pop eax
|
|
.got_cap:
|
|
DEBUGF DBG_INFO,"Found NVMe device with capabilities\n"
|
|
invoke PciRead8, [pcidev_bus], [pcidev_devfn], PCI_header00.cap_ptr
|
|
and eax, 11111100b
|
|
mov edi, eax
|
|
@@:
|
|
invoke PciRead32, [pcidev_bus], [pcidev_devfn], edi
|
|
mov ecx, eax
|
|
;and ecx, 0xff
|
|
mov eax, ecx
|
|
movzx edi, ah
|
|
test edi, edi
|
|
jnz @b
|
|
|
|
; read BAR0
|
|
invoke PciRead32, [pcidev_bus], [pcidev_devfn], PCI_header00.base_addr_0
|
|
mov [bar0], eax
|
|
|
|
stdcall nvme_identify, 0, 0, 0, 0
|
|
|
|
; return successfully
|
|
pop eax
|
|
xor eax, eax
|
|
inc eax
|
|
pop ebx
|
|
ret
|
|
endp
|
|
|
|
; uninitialized data
|
|
pcidev_bus dd ?
|
|
pcidev_devfn dd ?
|
|
bar0 dd ?
|
|
|
|
;all initialized data place here
|
|
|
|
align 4
|
|
my_service db "NVMe Service",0 ;max 16 chars include zero
|
|
include_debug_strings
|
|
|
|
align 4
|
|
data fixups
|
|
end data
|