2
0
mirror of https://git.missingno.dev/kolibrios-nvme-driver/ synced 2025-01-09 06:25:55 +01:00
kolibrios-nvme-driver/drivers/nvme/nvme.asm
2024-03-29 23:42:01 -04:00

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