diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e65aab4c0..fba8dac5ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,11 @@ have been added. These functions accept `spdk_nvme_ns_cmd_ext_io_opts` structure options, e.g. DMA memory domain which describes data that may belong to another memory domain and can't be accessed directly. +Added a new function `spdk_nvme_ctrlr_get_regs_bpinfo` to get boot partition info of a controller. +Added new functions `spdk_nvme_ctrlr_write_boot_partition`, +`spdk_nvme_ctrlr_read_boot_partition_start` and `spdk_nvme_ctrlr_read_boot_partition_poll` +to write and read the boot partitions of a controller. + ### dpdk Updated DPDK submodule to DPDK 21.08. diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h index ca1e2cea4d..83f38f6858 100644 --- a/include/spdk/nvme.h +++ b/include/spdk/nvme.h @@ -1196,6 +1196,15 @@ union spdk_nvme_cmbsz_register spdk_nvme_ctrlr_get_regs_cmbsz(struct spdk_nvme_c */ union spdk_nvme_pmrcap_register spdk_nvme_ctrlr_get_regs_pmrcap(struct spdk_nvme_ctrlr *ctrlr); +/** + * Get the NVMe controller BPINFO (Boot Partition Information) register. + * + * \param ctrlr Opaque handle to NVMe controller. + * + * \return the NVMe controller BPINFO (Boot Partition Information) register. + */ +union spdk_nvme_bpinfo_register spdk_nvme_ctrlr_get_regs_bpinfo(struct spdk_nvme_ctrlr *ctrlr); + /** * Get the NVMe controller PMR size. * @@ -2301,6 +2310,78 @@ int spdk_nvme_ctrlr_update_firmware(struct spdk_nvme_ctrlr *ctrlr, void *payload int slot, enum spdk_nvme_fw_commit_action commit_action, struct spdk_nvme_status *completion_status); +/** + * Start the Read from a Boot Partition. + * + * This function is thread safe and can be called at any point after spdk_nvme_probe(). + * + * \param ctrlr NVMe controller to perform the Boot Partition read. + * \param payload The data buffer for Boot Partition read. + * \param bprsz Read size in multiples of 4 KiB to copy into the Boot Partition Memory Buffer. + * \param bprof Boot Partition offset to read from in 4 KiB units. + * \param bpid Boot Partition identifier for the Boot Partition read operation. + * + * \return 0 if Boot Partition read is successful. Negated errno on the following error conditions: + * -ENOMEM: if resources could not be allocated. + * -ENOTSUP: Boot Partition is not supported by the Controller. + * -EIO: Registers access failure. + * -EINVAL: Parameters are invalid. + * -EFAULT: Invalid address was specified as part of payload. + * -EALREADY: Boot Partition read already initiated. + */ +int spdk_nvme_ctrlr_read_boot_partition_start(struct spdk_nvme_ctrlr *ctrlr, void *payload, + uint32_t bprsz, uint32_t bprof, uint32_t bpid); + +/** + * Poll the status of the Read from a Boot Partition. + * + * This function is thread safe and can be called at any point after spdk_nvme_probe(). + * + * \param ctrlr NVMe controller to perform the Boot Partition read. + * + * \return 0 if Boot Partition read is successful. Negated errno on the following error conditions: + * -EIO: Registers access failure. + * -EINVAL: Invalid read status or the Boot Partition read is not initiated yet. + * -EAGAIN: If the read is still in progress; users must call + * spdk_nvme_ctrlr_read_boot_partition_poll again to check the read status. + */ +int spdk_nvme_ctrlr_read_boot_partition_poll(struct spdk_nvme_ctrlr *ctrlr); + +/** + * Write to a Boot Partition. + * + * This function is thread safe and can be called at any point after spdk_nvme_probe(). + * Users will get the completion after the data is downloaded, image is replaced and + * Boot Partition is activated or when the sequence encounters an error. + * + * \param ctrlr NVMe controller to perform the Boot Partition write. + * \param payload The data buffer for Boot Partition write. + * \param size Data size to write to the Boot Partition. + * \param bpid Boot Partition identifier for the Boot Partition write operation. + * \param cb_fn Callback function to invoke when the operation is completed. + * \param cb_arg Argument to pass to the callback function. + * + * \return 0 if Boot Partition write submit is successful. Negated errno on the following error conditions: + * -ENOMEM: if resources could not be allocated. + * -ENOTSUP: Boot Partition is not supported by the Controller. + * -EIO: Registers access failure. + * -EINVAL: Parameters are invalid. + */ +int spdk_nvme_ctrlr_write_boot_partition(struct spdk_nvme_ctrlr *ctrlr, void *payload, + uint32_t size, uint32_t bpid, spdk_nvme_cmd_cb cb_fn, void *cb_arg); + +/** + * Return virtual address of PCIe NVM I/O registers + * + * This function returns a pointer to the PCIe I/O registers for a controller + * or NULL if unsupported for this transport. + * + * \param ctrlr Controller whose registers are to be accessed. + * + * \return Pointer to virtual address of register bank, or NULL. + */ +volatile struct spdk_nvme_registers *spdk_nvme_ctrlr_get_registers(struct spdk_nvme_ctrlr *ctrlr); + /** * Reserve the controller memory buffer for data transfer use. * diff --git a/lib/nvme/nvme_ctrlr.c b/lib/nvme/nvme_ctrlr.c index 54ee102d93..0b067c5649 100644 --- a/lib/nvme/nvme_ctrlr.c +++ b/lib/nvme/nvme_ctrlr.c @@ -124,6 +124,27 @@ nvme_ctrlr_get_pmrcap(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_pmrcap_regi &pmrcap->raw); } +int +nvme_ctrlr_get_bpinfo(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_bpinfo_register *bpinfo) +{ + return nvme_transport_ctrlr_get_reg_4(ctrlr, offsetof(struct spdk_nvme_registers, bpinfo.raw), + &bpinfo->raw); +} + +int +nvme_ctrlr_set_bprsel(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_bprsel_register *bprsel) +{ + return nvme_transport_ctrlr_set_reg_4(ctrlr, offsetof(struct spdk_nvme_registers, bprsel.raw), + bprsel->raw); +} + +int +nvme_ctrlr_set_bpmbl(struct spdk_nvme_ctrlr *ctrlr, uint64_t bpmbl_value) +{ + return nvme_transport_ctrlr_set_reg_8(ctrlr, offsetof(struct spdk_nvme_registers, bpmbl), + bpmbl_value); +} + static int nvme_ctrlr_set_nssr(struct spdk_nvme_ctrlr *ctrlr, uint32_t nssr_value) { @@ -3959,6 +3980,17 @@ union spdk_nvme_pmrcap_register spdk_nvme_ctrlr_get_regs_pmrcap(struct spdk_nvme return pmrcap; } +union spdk_nvme_bpinfo_register spdk_nvme_ctrlr_get_regs_bpinfo(struct spdk_nvme_ctrlr *ctrlr) +{ + union spdk_nvme_bpinfo_register bpinfo; + + if (nvme_ctrlr_get_bpinfo(ctrlr, &bpinfo)) { + bpinfo.raw = 0; + } + + return bpinfo; +} + uint64_t spdk_nvme_ctrlr_get_pmrsz(struct spdk_nvme_ctrlr *ctrlr) { @@ -4513,6 +4545,194 @@ spdk_nvme_ctrlr_unmap_pmr(struct spdk_nvme_ctrlr *ctrlr) return rc; } +int spdk_nvme_ctrlr_read_boot_partition_start(struct spdk_nvme_ctrlr *ctrlr, void *payload, + uint32_t bprsz, uint32_t bprof, uint32_t bpid) +{ + union spdk_nvme_bprsel_register bprsel; + union spdk_nvme_bpinfo_register bpinfo; + uint64_t bpmbl, bpmb_size; + + if (ctrlr->cap.bits.bps == 0) { + return -ENOTSUP; + } + + if (nvme_ctrlr_get_bpinfo(ctrlr, &bpinfo)) { + NVME_CTRLR_ERRLOG(ctrlr, "get bpinfo failed\n"); + return -EIO; + } + + if (bpinfo.bits.brs == SPDK_NVME_BRS_READ_IN_PROGRESS) { + NVME_CTRLR_ERRLOG(ctrlr, "Boot Partition read already initiated\n"); + return -EALREADY; + } + + nvme_robust_mutex_lock(&ctrlr->ctrlr_lock); + + bpmb_size = bprsz * 4096; + bpmbl = spdk_vtophys(payload, &bpmb_size); + if (bpmbl == SPDK_VTOPHYS_ERROR) { + NVME_CTRLR_ERRLOG(ctrlr, "spdk_vtophys of bpmbl failed\n"); + nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); + return -EFAULT; + } + + if (bpmb_size != bprsz * 4096) { + NVME_CTRLR_ERRLOG(ctrlr, "Boot Partition buffer is not physically contiguous\n"); + nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); + return -EFAULT; + } + + if (nvme_ctrlr_set_bpmbl(ctrlr, bpmbl)) { + NVME_CTRLR_ERRLOG(ctrlr, "set_bpmbl() failed\n"); + nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); + return -EIO; + } + + bprsel.bits.bpid = bpid; + bprsel.bits.bprof = bprof; + bprsel.bits.bprsz = bprsz; + + if (nvme_ctrlr_set_bprsel(ctrlr, &bprsel)) { + NVME_CTRLR_ERRLOG(ctrlr, "set_bprsel() failed\n"); + nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); + return -EIO; + } + + nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); + return 0; +} + +int spdk_nvme_ctrlr_read_boot_partition_poll(struct spdk_nvme_ctrlr *ctrlr) +{ + int rc = 0; + union spdk_nvme_bpinfo_register bpinfo; + + if (nvme_ctrlr_get_bpinfo(ctrlr, &bpinfo)) { + NVME_CTRLR_ERRLOG(ctrlr, "get bpinfo failed\n"); + return -EIO; + } + + switch (bpinfo.bits.brs) { + case SPDK_NVME_BRS_NO_READ: + NVME_CTRLR_ERRLOG(ctrlr, "Boot Partition read not initiated\n"); + rc = -EINVAL; + break; + case SPDK_NVME_BRS_READ_IN_PROGRESS: + NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition read in progress\n"); + rc = -EAGAIN; + break; + case SPDK_NVME_BRS_READ_ERROR: + NVME_CTRLR_ERRLOG(ctrlr, "Error completing Boot Partition read\n"); + rc = -EIO; + break; + case SPDK_NVME_BRS_READ_SUCCESS: + NVME_CTRLR_INFOLOG(ctrlr, "Boot Partition read completed successfully\n"); + break; + default: + NVME_CTRLR_ERRLOG(ctrlr, "Invalid Boot Partition read status\n"); + rc = -EINVAL; + } + + return rc; +} + +static void +nvme_write_boot_partition_cb(void *arg, const struct spdk_nvme_cpl *cpl) +{ + int res; + struct spdk_nvme_ctrlr *ctrlr = arg; + struct spdk_nvme_fw_commit fw_commit; + struct spdk_nvme_cpl err_cpl = + {.status = {.sct = SPDK_NVME_SCT_GENERIC, .sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR }}; + + if (spdk_nvme_cpl_is_error(cpl)) { + NVME_CTRLR_ERRLOG(ctrlr, "Write Boot Partition failed\n"); + ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, cpl); + return; + } + + if (ctrlr->bp_ws == SPDK_NVME_BP_WS_DOWNLOADING) { + NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition Downloading at Offset %d Success\n", ctrlr->fw_offset); + ctrlr->fw_payload += ctrlr->fw_transfer_size; + ctrlr->fw_offset += ctrlr->fw_transfer_size; + ctrlr->fw_size_remaining -= ctrlr->fw_transfer_size; + ctrlr->fw_transfer_size = spdk_min(ctrlr->fw_size_remaining, ctrlr->min_page_size); + res = nvme_ctrlr_cmd_fw_image_download(ctrlr, ctrlr->fw_transfer_size, ctrlr->fw_offset, + ctrlr->fw_payload, nvme_write_boot_partition_cb, ctrlr); + if (res) { + NVME_CTRLR_ERRLOG(ctrlr, "nvme_ctrlr_cmd_fw_image_download failed!\n"); + ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, &err_cpl); + return; + } + + if (ctrlr->fw_transfer_size < ctrlr->min_page_size) { + ctrlr->bp_ws = SPDK_NVME_BP_WS_DOWNLOADED; + } + } else if (ctrlr->bp_ws == SPDK_NVME_BP_WS_DOWNLOADED) { + NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition Download Success\n"); + memset(&fw_commit, 0, sizeof(struct spdk_nvme_fw_commit)); + fw_commit.bpid = ctrlr->bpid; + fw_commit.ca = SPDK_NVME_FW_COMMIT_REPLACE_BOOT_PARTITION; + res = nvme_ctrlr_cmd_fw_commit(ctrlr, &fw_commit, + nvme_write_boot_partition_cb, ctrlr); + if (res) { + NVME_CTRLR_ERRLOG(ctrlr, "nvme_ctrlr_cmd_fw_commit failed!\n"); + NVME_CTRLR_ERRLOG(ctrlr, "commit action: %d\n", fw_commit.ca); + ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, &err_cpl); + return; + } + + ctrlr->bp_ws = SPDK_NVME_BP_WS_REPLACE; + } else if (ctrlr->bp_ws == SPDK_NVME_BP_WS_REPLACE) { + NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition Replacement Success\n"); + memset(&fw_commit, 0, sizeof(struct spdk_nvme_fw_commit)); + fw_commit.bpid = ctrlr->bpid; + fw_commit.ca = SPDK_NVME_FW_COMMIT_ACTIVATE_BOOT_PARTITION; + res = nvme_ctrlr_cmd_fw_commit(ctrlr, &fw_commit, + nvme_write_boot_partition_cb, ctrlr); + if (res) { + NVME_CTRLR_ERRLOG(ctrlr, "nvme_ctrlr_cmd_fw_commit failed!\n"); + NVME_CTRLR_ERRLOG(ctrlr, "commit action: %d\n", fw_commit.ca); + ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, &err_cpl); + return; + } + + ctrlr->bp_ws = SPDK_NVME_BP_WS_ACTIVATE; + } else if (ctrlr->bp_ws == SPDK_NVME_BP_WS_ACTIVATE) { + NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition Activation Success\n"); + ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, cpl); + } else { + NVME_CTRLR_ERRLOG(ctrlr, "Invalid Boot Partition write state\n"); + ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, &err_cpl); + return; + } +} + +int spdk_nvme_ctrlr_write_boot_partition(struct spdk_nvme_ctrlr *ctrlr, + void *payload, uint32_t size, uint32_t bpid, + spdk_nvme_cmd_cb cb_fn, void *cb_arg) +{ + int res; + + if (ctrlr->cap.bits.bps == 0) { + return -ENOTSUP; + } + + ctrlr->bp_ws = SPDK_NVME_BP_WS_DOWNLOADING; + ctrlr->bpid = bpid; + ctrlr->bp_write_cb_fn = cb_fn; + ctrlr->bp_write_cb_arg = cb_arg; + ctrlr->fw_offset = 0; + ctrlr->fw_size_remaining = size; + ctrlr->fw_payload = payload; + ctrlr->fw_transfer_size = spdk_min(ctrlr->fw_size_remaining, ctrlr->min_page_size); + + res = nvme_ctrlr_cmd_fw_image_download(ctrlr, ctrlr->fw_transfer_size, ctrlr->fw_offset, + ctrlr->fw_payload, nvme_write_boot_partition_cb, ctrlr); + + return res; +} + bool spdk_nvme_ctrlr_is_discovery(struct spdk_nvme_ctrlr *ctrlr) { diff --git a/lib/nvme/nvme_internal.h b/lib/nvme/nvme_internal.h index d7a7f7b0b7..f1b37e7819 100644 --- a/lib/nvme/nvme_internal.h +++ b/lib/nvme/nvme_internal.h @@ -216,6 +216,14 @@ enum nvme_payload_type { NVME_PAYLOAD_TYPE_SGL, }; +/** Boot partition write states */ +enum nvme_bp_write_state { + SPDK_NVME_BP_WS_DOWNLOADING = 0x0, + SPDK_NVME_BP_WS_DOWNLOADED = 0x1, + SPDK_NVME_BP_WS_REPLACE = 0x2, + SPDK_NVME_BP_WS_ACTIVATE = 0x3, +}; + /** * Descriptor for a request data payload. */ @@ -918,6 +926,18 @@ struct spdk_nvme_ctrlr { /* PMR size in bytes */ uint64_t pmr_size; + + /* Boot Partition Info */ + enum nvme_bp_write_state bp_ws; + uint32_t bpid; + spdk_nvme_cmd_cb bp_write_cb_fn; + void *bp_write_cb_arg; + + /* Firmware Download */ + void *fw_payload; + unsigned int fw_size_remaining; + unsigned int fw_offset; + unsigned int fw_transfer_size; }; struct spdk_nvme_probe_ctx { @@ -1085,6 +1105,9 @@ int nvme_ctrlr_get_cap(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_cap_regist int nvme_ctrlr_get_vs(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_vs_register *vs); int nvme_ctrlr_get_cmbsz(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_cmbsz_register *cmbsz); int nvme_ctrlr_get_pmrcap(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_pmrcap_register *pmrcap); +int nvme_ctrlr_get_bpinfo(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_bpinfo_register *bpinfo); +int nvme_ctrlr_set_bprsel(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_bprsel_register *bprsel); +int nvme_ctrlr_set_bpmbl(struct spdk_nvme_ctrlr *ctrlr, uint64_t bpmbl_value); bool nvme_ctrlr_multi_iocs_enabled(struct spdk_nvme_ctrlr *ctrlr); void nvme_ctrlr_process_async_event(struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_cpl *cpl); diff --git a/lib/nvme/spdk_nvme.map b/lib/nvme/spdk_nvme.map index 9a6477ff09..deff6ebbcd 100644 --- a/lib/nvme/spdk_nvme.map +++ b/lib/nvme/spdk_nvme.map @@ -48,6 +48,7 @@ spdk_nvme_ctrlr_get_regs_vs; spdk_nvme_ctrlr_get_regs_cmbsz; spdk_nvme_ctrlr_get_regs_pmrcap; + spdk_nvme_ctrlr_get_regs_bpinfo; spdk_nvme_ctrlr_get_pmrsz; spdk_nvme_ctrlr_get_num_ns; spdk_nvme_ctrlr_get_pci_device; @@ -100,6 +101,9 @@ spdk_nvme_ctrlr_disable_pmr; spdk_nvme_ctrlr_map_pmr; spdk_nvme_ctrlr_unmap_pmr; + spdk_nvme_ctrlr_read_boot_partition_start; + spdk_nvme_ctrlr_read_boot_partition_poll; + spdk_nvme_ctrlr_write_boot_partition; spdk_nvme_ctrlr_get_transport_id; spdk_nvme_ctrlr_alloc_qid; spdk_nvme_ctrlr_free_qid;