From e137d20e7dfff00d0662b3d2d256e9c92f7a7f4a Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Sun, 23 Feb 2020 08:38:22 -0800 Subject: hw/block/nvme: add check for mdts Add 'mdts' device parameter to control the Maximum Data Transfer Size of the controller and check that it is respected. Signed-off-by: Klaus Jensen Reviewed-by: Maxim Levitsky Reviewed-by: Minwoo Im --- hw/block/nvme.c | 30 +++++++++++++++++++++++++++++- hw/block/nvme.h | 1 + hw/block/trace-events | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'hw/block') diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 8dade3e223..30b42e2492 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -21,7 +21,8 @@ * cmb_size_mb=, \ * [pmrdev=,] \ * max_ioqpairs=, \ - * aerl=, aer_max_queued= + * aerl=, aer_max_queued=, \ + * mdts= * * Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at * offset 0 in BAR2 and supports only WDS, RDS and SQS for now. @@ -570,6 +571,17 @@ static void nvme_clear_events(NvmeCtrl *n, uint8_t event_type) } } +static inline uint16_t nvme_check_mdts(NvmeCtrl *n, size_t len) +{ + uint8_t mdts = n->params.mdts; + + if (mdts && len > n->page_size << mdts) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + return NVME_SUCCESS; +} + static inline uint16_t nvme_check_bounds(NvmeCtrl *n, NvmeNamespace *ns, uint64_t slba, uint32_t nlb) { @@ -663,6 +675,13 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, trace_pci_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba); + status = nvme_check_mdts(n, data_size); + if (status) { + trace_pci_nvme_err_mdts(nvme_cid(req), data_size); + block_acct_invalid(blk_get_stats(n->conf.blk), acct); + return status; + } + status = nvme_check_bounds(n, ns, slba, nlb); if (status) { trace_pci_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze); @@ -956,6 +975,7 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) uint32_t numdl, numdu; uint64_t off, lpol, lpou; size_t len; + uint16_t status; numdl = (dw10 >> 16); numdu = (dw11 & 0xffff); @@ -971,6 +991,12 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) trace_pci_nvme_get_log(nvme_cid(req), lid, lsp, rae, len, off); + status = nvme_check_mdts(n, len); + if (status) { + trace_pci_nvme_err_mdts(nvme_cid(req), len); + return status; + } + switch (lid) { case NVME_LOG_ERROR_INFO: return nvme_error_info(n, cmd, rae, len, off, req); @@ -2299,6 +2325,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->ieee[0] = 0x00; id->ieee[1] = 0x02; id->ieee[2] = 0xb3; + id->mdts = n->params.mdts; id->ver = cpu_to_le32(NVME_SPEC_VER); id->oacs = cpu_to_le16(0); @@ -2418,6 +2445,7 @@ static Property nvme_props[] = { DEFINE_PROP_UINT16("msix_qsize", NvmeCtrl, params.msix_qsize, 65), DEFINE_PROP_UINT8("aerl", NvmeCtrl, params.aerl, 3), DEFINE_PROP_UINT32("aer_max_queued", NvmeCtrl, params.aer_max_queued, 64), + DEFINE_PROP_UINT8("mdts", NvmeCtrl, params.mdts, 7), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/nvme.h b/hw/block/nvme.h index 5519b5cc76..137cd8c2bf 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -11,6 +11,7 @@ typedef struct NvmeParams { uint32_t cmb_size_mb; uint8_t aerl; uint32_t aer_max_queued; + uint8_t mdts; } NvmeParams; typedef struct NvmeAsyncEvent { diff --git a/hw/block/trace-events b/hw/block/trace-events index f20c59a4b5..72cf2d15cb 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -85,6 +85,7 @@ pci_nvme_mmio_shutdown_set(void) "shutdown bit set" pci_nvme_mmio_shutdown_cleared(void) "shutdown bit cleared" # nvme traces for error conditions +pci_nvme_err_mdts(uint16_t cid, size_t len) "cid %"PRIu16" len %zu" pci_nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size" pci_nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64"" pci_nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64"" -- cgit v1.2.3