diff --git a/drivers/nvme/nvme.asm b/drivers/nvme/nvme.asm index 8245d37..de4a43c 100644 --- a/drivers/nvme/nvme.asm +++ b/drivers/nvme/nvme.asm @@ -18,6 +18,8 @@ __DEBUG_LEVEL__ = 1 DRIVER_VERSION = 1 DBG_INFO = 1 NULLPTR = 0 +FALSE = 0 +TRUE = 1 ; flags for alloc_dptr PRP1_ENTRY_ALLOCATED = 1 @@ -506,7 +508,7 @@ proc build_prp_list stdcall, nprps:dword cmp ecx, PAGE_SIZE / 8 - 1 jne @f push ecx - stdcall build_prp_list, eax + stdcall build_prp_list, eax, TRUE pop ecx test eax, eax jz .cleanup_prp_list @@ -554,7 +556,8 @@ proc alloc_dptr stdcall, ns:dword, prps_ptr:dword, start_sector:qword, numsector push esi edi mov esi, [ns] mov edi, [prps_ptr] - mov qword [esi], 0 + mov dword [esi], 0 + mov dword [esi + 4], 0 invoke KernelAlloc, PAGE_SIZE test eax, eax jz .err @@ -587,7 +590,7 @@ proc alloc_dptr stdcall, ns:dword, prps_ptr:dword, start_sector:qword, numsector jmp .success .free_prp_list: - stdcall free_prp_list, edi, eax + stdcall free_prp_list, edi, eax, FALSE .err: mov eax, dword [edi] @@ -608,49 +611,113 @@ proc alloc_dptr stdcall, ns:dword, prps_ptr:dword, start_sector:qword, numsector endp -proc nvme_read ns:dword, dst:dword, start_sector:qword, numsectors_ptr:dword +; note that prp_list is required to be a physical address +proc write_prp_list_buf stdcall, prp_list:dword, dst:dword, nprps:dword - push ebx esi edi - sub esp, 8 - stdcall alloc_dptr, [ns], esp, [start_sector], [numsectors_ptr] + push esi edi + mov esi, [prp_list] + invoke KernelAlloc, 0x1000 test eax, eax - jz .end + jz .err mov edi, eax - invoke GetPhysAddr - mov esi, [ns] + push edi + invoke MapPage, edi, esi, PG_SW+PG_NOCACHE + mov esi, edi + mov edi, [dst] + mov ecx, [nprps] + cmp ecx, PAGE_SIZE + jae @f + shl ecx, 12 + NVM_MPS + rep stosd + pop edi + invoke KernelFree, edi + pop edi esi + xor eax, eax + inc eax + ret + +; more than or equal to 4096 PRP entries +@@: + mov ecx, PAGE_SIZE * PAGE_SIZE - 12 + rep stosd + sub ecx, PAGE_SIZE + stdcall write_prp_list_buf, [prp_list], edi, ecx + mov esi, eax + pop edi + invoke KernelFree, edi + mov eax, esi + +.err: + pop edi esi + ret + +endp + + +nvme_read: + mov edx, NVM_CMD_READ + jmp nvme_readwrite + +nvme_write: + mov edx, NVM_CMD_WRITE + +proc nvme_readwrite stdcall, ns:dword, dst:dword, start_sector:qword, numsectors_ptr:dword + push ebx esi edi + sub esp, 12 mov ebx, [numsectors_ptr] mov ebx, [ebx] - stdcall nvme_io_rw, [esi + NSINFO.pci], \ ; PCI device - 1, \ ; QID (1 for now) - [esi + NSINFO.nsid], \ ; NSID - eax, \ ; DPTR - dword [start_sector], \ ; SLBA_LO - dword [start_sector + 4], \ ; SLBA_HI - ebx, \ ; NLB - NVM_CMD_READ ; Command opcode + mov dword [esp + 8], edx + ; Note that [esp] will contain the value of PRP1 and [esp + 4] will + ; contain the value of PRP2 (after this call, if it completes successfully) + stdcall alloc_dptr, [ns], esp, dword [start_sector], dword [start_sector + 4], ebx + test eax, eax + jz .end + invoke GetPhysAddr + mov esi, [ns] + stdcall nvme_io_rw, [esi + NSINFO.pci], \ + 1, \ + [esi + NSINFO.nsid], \ + dword [esp], \ + dword [esp + 4], \ + dword [start_sector], \ + dword [start_sector + 4], \ + ebx, \ + dword [esp + 8] ; assume command completes successfully for now - mov ecx, dword [esi + NSINFO.pg_sectors] - cmp ebx, ecx - ja .is_prp_list ; only 1-2 pages are used, which makes our life easier mov ecx, dword [esi + NSINFO.lbads] imul ecx, ebx - mov esi, edi mov edi, [dst] + mov esi, dword [esp] rep movsd - add esp, 8 - pop edi esi ebx - xor eax, eax ; TODO: add proper return value later - ret + invoke KernelFree, dword [esp] + mov esi, dword [esp + 4] + mov ecx, dword [esi + NSINFO.pg_sectors] + cmp ebx, ecx + ja .is_prp_list + xor eax, eax + test esi, esi + jz .end + rep movsd + invoke KernelFree, dword [esp + 4] + xor eax, eax + jmp .end .is_prp_list: + xor edx, edx + mov eax, ebx + mov ecx, dword [esi + NSINFO.lbads] + div ecx + mov ebx, eax + stdcall write_prp_list_buf, dword [esp + 4], [dst], ebx + test eax, eax + stdcall free_prp_list, dword [esp + 4], ebx, FALSE .end: - add esp, 8 + add esp, 12 pop edi esi ebx - xor eax, eax ret endp @@ -661,7 +728,7 @@ proc nvme_io_rw stdcall, pci:dword, qid:word, nsid:dword, prps:qword, slba_lo:dw ; TODO: Use IDENTC.NOIOB to construct read/write commands that don't ; cross the I/O boundary to achieve optimal performance ; - ; Read AWUN/NAWUN + ; TODO: Read AWUN/NAWUN sub esp, sizeof.SQ_ENTRY stdcall memset, esp, 0, sizeof.SQ_ENTRY movzx ecx, [qid]