#ifndef HW_NVME_H #define HW_NVME_H #include "block/nvme.h" typedef struct NvmeParams { char *serial; uint32_t num_queues; /* deprecated since 5.1 */ uint32_t max_ioqpairs; uint16_t msix_qsize; uint32_t cmb_size_mb; uint8_t aerl; uint32_t aer_max_queued; uint8_t mdts; } NvmeParams; typedef struct NvmeAsyncEvent { QTAILQ_ENTRY(NvmeAsyncEvent) entry; NvmeAerResult result; } NvmeAsyncEvent; typedef struct NvmeRequest { struct NvmeSQueue *sq; struct NvmeNamespace *ns; BlockAIOCB *aiocb; uint16_t status; NvmeCqe cqe; NvmeCmd cmd; BlockAcctCookie acct; QEMUSGList qsg; QEMUIOVector iov; QTAILQ_ENTRY(NvmeRequest)entry; } NvmeRequest; static inline const char *nvme_adm_opc_str(uint8_t opc) { switch (opc) { case NVME_ADM_CMD_DELETE_SQ: return "NVME_ADM_CMD_DELETE_SQ"; case NVME_ADM_CMD_CREATE_SQ: return "NVME_ADM_CMD_CREATE_SQ"; case NVME_ADM_CMD_GET_LOG_PAGE: return "NVME_ADM_CMD_GET_LOG_PAGE"; case NVME_ADM_CMD_DELETE_CQ: return "NVME_ADM_CMD_DELETE_CQ"; case NVME_ADM_CMD_CREATE_CQ: return "NVME_ADM_CMD_CREATE_CQ"; case NVME_ADM_CMD_IDENTIFY: return "NVME_ADM_CMD_IDENTIFY"; case NVME_ADM_CMD_ABORT: return "NVME_ADM_CMD_ABORT"; case NVME_ADM_CMD_SET_FEATURES: return "NVME_ADM_CMD_SET_FEATURES"; case NVME_ADM_CMD_GET_FEATURES: return "NVME_ADM_CMD_GET_FEATURES"; case NVME_ADM_CMD_ASYNC_EV_REQ: return "NVME_ADM_CMD_ASYNC_EV_REQ"; default: return "NVME_ADM_CMD_UNKNOWN"; } } static inline const char *nvme_io_opc_str(uint8_t opc) { switch (opc) { case NVME_CMD_FLUSH: return "NVME_NVM_CMD_FLUSH"; case NVME_CMD_WRITE: return "NVME_NVM_CMD_WRITE"; case NVME_CMD_READ: return "NVME_NVM_CMD_READ"; case NVME_CMD_WRITE_ZEROES: return "NVME_NVM_CMD_WRITE_ZEROES"; default: return "NVME_NVM_CMD_UNKNOWN"; } } typedef struct NvmeSQueue { struct NvmeCtrl *ctrl; uint16_t sqid; uint16_t cqid; uint32_t head; uint32_t tail; uint32_t size; uint64_t dma_addr; QEMUTimer *timer; NvmeRequest *io_req; QTAILQ_HEAD(, NvmeRequest) req_list; QTAILQ_HEAD(, NvmeRequest) out_req_list; QTAILQ_ENTRY(NvmeSQueue) entry; } NvmeSQueue; typedef struct NvmeCQueue { struct NvmeCtrl *ctrl; uint8_t phase; uint16_t cqid; uint16_t irq_enabled; uint32_t head; uint32_t tail; uint32_t vector; uint32_t size; uint64_t dma_addr; QEMUTimer *timer; QTAILQ_HEAD(, NvmeSQueue) sq_list; QTAILQ_HEAD(, NvmeRequest) req_list; } NvmeCQueue; typedef struct NvmeNamespace { NvmeIdNs id_ns; } NvmeNamespace; static inline NvmeLBAF *nvme_ns_lbaf(NvmeNamespace *ns) { NvmeIdNs *id_ns = &ns->id_ns; return &id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(id_ns->flbas)]; } static inline uint8_t nvme_ns_lbads(NvmeNamespace *ns) { return nvme_ns_lbaf(ns)->ds; } /* convert an LBA to the equivalent in bytes */ static inline size_t nvme_l2b(NvmeNamespace *ns, uint64_t lba) { return lba << nvme_ns_lbads(ns); } #define TYPE_NVME "nvme" #define NVME(obj) \ OBJECT_CHECK(NvmeCtrl, (obj), TYPE_NVME) typedef struct NvmeFeatureVal { struct { uint16_t temp_thresh_hi; uint16_t temp_thresh_low; }; uint32_t async_config; } NvmeFeatureVal; typedef struct NvmeCtrl { PCIDevice parent_obj; MemoryRegion iomem; MemoryRegion ctrl_mem; NvmeBar bar; BlockConf conf; NvmeParams params; bool qs_created; uint32_t page_size; uint16_t page_bits; uint16_t max_prp_ents; uint16_t cqe_size; uint16_t sqe_size; uint32_t reg_size; uint32_t num_namespaces; uint32_t max_q_ents; uint64_t ns_size; uint8_t outstanding_aers; uint8_t *cmbuf; uint32_t irq_status; uint64_t host_timestamp; /* Timestamp sent by the host */ uint64_t timestamp_set_qemu_clock_ms; /* QEMU clock time */ uint64_t starttime_ms; uint16_t temperature; HostMemoryBackend *pmrdev; uint8_t aer_mask; NvmeRequest **aer_reqs; QTAILQ_HEAD(, NvmeAsyncEvent) aer_queue; int aer_queued; NvmeNamespace *namespaces; NvmeSQueue **sq; NvmeCQueue **cq; NvmeSQueue admin_sq; NvmeCQueue admin_cq; NvmeIdCtrl id_ctrl; NvmeFeatureVal features; } NvmeCtrl; /* calculate the number of LBAs that the namespace can accomodate */ static inline uint64_t nvme_ns_nlbas(NvmeCtrl *n, NvmeNamespace *ns) { return n->ns_size >> nvme_ns_lbads(ns); } static inline NvmeCQueue *nvme_cq(NvmeRequest *req) { NvmeSQueue *sq = req->sq; NvmeCtrl *n = sq->ctrl; return n->cq[sq->cqid]; } static inline NvmeCtrl *nvme_ctrl(NvmeRequest *req) { NvmeSQueue *sq = req->sq; return sq->ctrl; } #endif /* HW_NVME_H */