The code to create those queues (with structures defined as on the spec) is:
Code: Select all
u64 io_complete_addr = kmalloc_page();
asq[admin_submission_tail] = (const struct SubmissionEntry) {0};
asq[admin_submission_tail].opcode = 5;
asq[admin_submission_tail].identifier = 1;
asq[admin_submission_tail].nsid = 0;
asq[admin_submission_tail].dword10 = (63 << 16) | 1; // size 64 identifier 1
asq[admin_submission_tail].dword11 = 1; // disable interrupts, physically contiguous
asq[admin_submission_tail].prp1 = page_virt_to_phys_addr(io_complete_addr);
(*admin_tail_doorbell)++;
admin_submission_tail++;
// wait for phase change / new entry in completion queue
while (!(acq[admin_completion_head].dword3 & 1))
asm volatile("nop");
vga_printf("completion ok\n");
serial_info("ACQ: dword 0: 0x%x", acq[admin_completion_head].dword0);
serial_info("ACQ: dword 1: 0x%x", acq[admin_completion_head].dword1);
serial_info("ACQ: dword 2: 0x%x", acq[admin_completion_head].dword2);
serial_info("ACQ: dword 3: 0x%x", acq[admin_completion_head].dword3);
admin_completion_head++;
// create io submission queue (base spec 102-103)
u64 io_submit_addr = kmalloc_page();
asq[admin_submission_tail] = (const struct SubmissionEntry) {0};
asq[admin_submission_tail].opcode = 1;
asq[admin_submission_tail].identifier = 2;
asq[admin_submission_tail].nsid = 0;
asq[admin_submission_tail].dword10 = (63 << 16) | 1; // size 64 identifier 1
asq[admin_submission_tail].dword11 = (1 << 16) | 1; // completion identifier 1, physically contiguous
asq[admin_submission_tail].prp1 = page_virt_to_phys_addr(io_submit_addr);
(*admin_tail_doorbell)++;
admin_submission_tail++;
while (!(acq[admin_completion_head].dword3 & 1)) {
vga_printf("waiting %u\n", acq[admin_completion_head].dword3);
asm volatile("nop");
}
vga_printf("submission ok");
serial_info("ACQ: dword 0: 0x%x", acq[admin_completion_head].dword0);
serial_info("ACQ: dword 1: 0x%x", acq[admin_completion_head].dword1);
serial_info("ACQ: dword 2: 0x%x", acq[admin_completion_head].dword2);
serial_info("ACQ: dword 3: 0x%x", acq[admin_completion_head].dword3);
admin_completion_head++;
Code: Select all
INFO: ACQ: dword 0: 0x0
INFO: ACQ: dword 1: 0x0
INFO: ACQ: dword 2: 0x1
INFO: ACQ: dword 3: 0x82050001
Full code is at https://github.com/glolichen/os. The lines above are src/pci/nvme.c lines 179-221.
Is there something wrong with the commands I am sending, for both completion and submission creation? I based this on the wiki page and the base spec, and I'm sorry that everything is a mess since the NVMe specs are quite hard to understand. Thanks all.