Update: so I know why the kernel is hanging when it enables the controller. Apparently Cc.CFS is being set. I'm not quite sure why this is happening. Looking at the QEMU source code for NVMe doesn't really help much, unfortunately. According to the NVMe specification, I am to do the following to initialize the controller:
1. Set PCIe configuration,
2. Wait for CSTS.RDY to be zero (I don't do this),
3. Configure the admin queue, including the AQA, ASQ, and ACQ registers,
4. Configure the controller settings, specifically the arbitration mechanism, the memory page size, and the I/O command set, and
5. Set CC.EN to one (this is where it all breaks).
There are more, but this is what works (and then dies).
Edit: So I know why its failing (thank you, qemu traces). I just have no idea how to actually fix it. The qemu traces say:
Quote:
[email protected]:pci_nvme_err_startfail_cqent_too_small nvme_start_ctrl failed because the completion queue entry size is too small: log2size=0, min=15
[email protected]:pci_nvme_err_startfail setting controller enable bit failed
The problem is that the NVMe specification says this for queue entry sizes:
Quote:
23:20 RW/RO 0h
I/O Completion Queue Entry Size (IOCQES): This field defines the I/O Completion Queue entry size that is used for the selected I/O Command Set. The required and maximum values for this field are specified in the CQES field in the Identify Controller data structure in Figure 247 for each I/O Command Set. The value is in bytes and is specified as a power of two (2^n). If any I/O Completion Queues exist, then write operations that change the value in this field produce undefined results. If the controller does not support I/O queues, then this field shall be read-only with a value of 0h.
19:16 RW/RO 0h
I/O Submission Queue Entry Size (IOSQES): This field defines the I/O Submission Queue entry size that is used for the selected I/O Command Set. The required and maximum values for this field are specified in the SQES field in the Identify Controller data structure in Figure 247 for each I/O Command Set. The value is in bytes and is specified as a power of two (2^n). If any I/O Submission Queues exist, then write operations that change the value in this field produce undefined results. If the controller does not support I/O queues, then this field shall be read-only with a value of 0h.
I can rely on cap.MQES, but the problem is that that's a 16-bit integer, whereas these only want four bits. SeaBIOS does this for its its initialization:
Code:
ctrl->reg->cc = NVME_CC_EN | (NVME_CQE_SIZE_LOG << 20)
| (NVME_SQE_SIZE_LOG << 16 /* IOSQES */);
NVME_CQE_SIZE_LOG is 4 and NVME_SQE_SIZE_LOG is 6, but this seems... really arbitrary and I don't understand how they get that.