From 06c0abac6a8e8b3396f5ee73c9b2a9f3620f0b69 Mon Sep 17 00:00:00 2001 From: Abdur-Rahman Mansoor Date: Wed, 3 Jul 2024 15:53:32 -0400 Subject: [PATCH] wip: implement code for allocating PRP entries and lists --- drivers/nvme/nvme.asm | 130 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 6 deletions(-) diff --git a/drivers/nvme/nvme.asm b/drivers/nvme/nvme.asm index b86273c..5b906bd 100644 --- a/drivers/nvme/nvme.asm +++ b/drivers/nvme/nvme.asm @@ -19,6 +19,12 @@ DRIVER_VERSION = 1 DBG_INFO = 1 NULLPTR = 0 +; flags for alloc_dptr +PRP1_ENTRY_ALLOCATED = 1 +PRP1_LIST_ALLOCATED = 2 +PRP2_ENTRY_ALLOCATED = 4 +PRP2_LIST_ALLOCATED = 8 + section ".flat" code readable writable executable include "../proc32.inc" include "../struct.inc" @@ -445,18 +451,125 @@ proc get_log_page stdcall, pci:dword, dptr:dword, lid:byte endp ; Returns a PRP/PRP List -proc alloc_dptr stdcall, start_sector:qword, numsectors_ptr:dword +; ---------------------------------------------------------------------------------------------------- +; flags (used for identifying how to free the memory used by the PRPs after the command is completed): +; BIT 1 SET: allocated PRP1 entry +; BIT 2 SET: allocated PRP1 list +; BIT 3 SET: allocated PRP2 entry +; BIT 4 SET: allocated PRP2 list +proc alloc_dptr stdcall, ns:dword, prps_ptr:dword, start_sector:qword, numsectors:dword, flags_ptr:dword - ; WIP: Todo + push esi edi ebx + mov esi, [ns] + mov edi, [flags_ptr] + mov ebx, dword [esi + NSINFO.pg_sectors] + mov esi, [prps_ptr] + mov dword [esi + 4], 0 ; set PRP #2 to reserved by default + cmp ebx, dword [edi] + ja .numsectors_over_1pg + +.alloc_prp1_entry: invoke KernelAlloc, 0x1000 + test eax, eax + jz .err + or byte [edi], PRP1_ENTRY_ALLOCATED + mov dword [esi], eax + pop ebx edi esi + ret + +.numsectors_over_1pg: + ; check if offset portion of PRP1 is 0 + mov eax, dword [esi] + and eax, (PAGE_SIZE shl NVM_MPS) - 1 + test eax, eax + jz .check_prp2_cond_bii + + ; check PRP2 condition b) i. + cmp ebx, [numsectors] + jne .check_prp2_cond_cici + invoke KernelAlloc, 0x1000 + test eax, eax + jz .err + mov dword [esi + 4], eax + or byte [edi], PRP2_ENTRY_ALLOCATED + jmp .alloc_prp1_entry + + +.check_prp2_cond_bii: + mov eax, ebx + shl eax, 1 + cmp eax, [numsectors] + ja .check_prp2_cond_c + invoke KernelAlloc, 0x1000 + test eax, eax + jz .err + mov dword [esi + 4], eax + or byte [edi], PRP2_ENTRY_ALLOCATED + jmp .alloc_prp1_entry + +.check_prp2_cond_c: + ; is the command data transfer length greater or equal to two memory pages? + mov eax, ebx + shl eax, 1 + cmp eax, [numsectors_ptr] + jb .alloc_prp1_entry + mov ecx, 4 + +; note that ECX should be set to the offset (0 for PRP1, 4 for PRP2) before +; jumping here +; TODO: Move to separate function +.build_prp_list: + or byte [edi], PRP2_LIST_ALLOCATED + ; insert number of PRPS to build in edx later + mov edx, 4 + shl edx, 2 + push edx + invoke KernelAlloc, edx + pop edx + test eax, eax + jz .err + push edi ecx + mov edi, eax + xor ecx, ecx + +.build_prp_list.loop: + ; TODO: handle cleaning up if an error occurs later + invoke KernelAlloc, 0x1000 + test eax, eax + jz .build_prp_list.loop.cleanup + mov dword [edi + ecx], eax + add ecx, 4 + cmp ecx, edx + jne .build_prp_list.loop + pop ecx + mov dword [esi + ecx], edi + pop edi + ;ret + +.build_prp_list.loop.cleanup: + push ecx ebx + xor ecx, ecx + +@@: + invoke KernelFree, dword [edi + ecx] + add ecx, 4 + cmp ecx, edx + jne @b + pop ebx ecx + pop ecx edi + +.err: + xor eax, eax + pop ebx edi esi ret endp -proc nvme_read stdcall, ns:dword, dst:dword, start_sector:qword, numsectors_ptr:dword +proc nvme_read ns:dword, dst:dword, start_sector:qword, numsectors_ptr:dword push ebx esi edi - stdcall alloc_dptr, [start_sector], [numsectors_ptr] + sub esp, 8 + stdcall alloc_dptr, [ns], dword [esp], dword [esp + 4], [start_sector], [numsectors_ptr] test eax, eax jz .end mov edi, eax @@ -484,12 +597,15 @@ proc nvme_read stdcall, ns:dword, dst:dword, start_sector:qword, numsectors_ptr: mov esi, edi mov edi, [dst] rep movsd + add esp, 8 + pop edi esi ebx xor eax, eax ; TODO: add proper return value later ret .is_prp_list: .end: + add esp, 8 pop edi esi ebx xor eax, eax ret @@ -497,7 +613,7 @@ proc nvme_read stdcall, ns:dword, dst:dword, start_sector:qword, numsectors_ptr: 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, dptr:dword, slba_lo:dword, slba_hi:dword, nlb:word, opcode:dword +proc nvme_io_rw stdcall, pci:dword, qid:word, nsid:dword, prp1:dword, prp2:dword, slba_lo:dword, slba_hi:dword, nlb:word, opcode:dword ; TODO: Use IDENTC.NOIOB to construct read/write commands that don't ; cross the I/O boundary to achieve optimal performance @@ -508,8 +624,10 @@ proc nvme_io_rw stdcall, pci:dword, qid:word, nsid:dword, dptr:dword, slba_lo:dw movzx ecx, [qid] stdcall set_cdw0, [pci], ecx, [opcode] mov dword [esp + SQ_ENTRY.cdw0], eax ; CDW0 - mov eax, [dptr] + mov eax, [prp1] mov dword [esp + SQ_ENTRY.dptr], eax + mov eax, [prp2] + mov dword [esp + SQ_ENTRY.dptr + 8], eax mov eax, [nsid] mov dword [esp + SQ_ENTRY.nsid], eax mov eax, [slba_lo]