Spdk/examples/nvme/nvme_manage/nvme_manage.c
Chunyang Hui 6b48e743a3 Opal: Add revert tper cmd option
Reset the device to its factory defaults.

Change-Id: I43f7dc8fb7bd5226283a4762beac0e2cf016f698
Signed-off-by: Chunyang Hui <chunyang.hui@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/445253
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
2019-04-25 18:20:42 +00:00

1210 lines
30 KiB
C

/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "spdk/stdinc.h"
#include "spdk/nvme.h"
#include "spdk/env.h"
#include "spdk/string.h"
#include "spdk/util.h"
#include "spdk/opal.h"
#define MAX_DEVS 64
struct dev {
struct spdk_pci_addr pci_addr;
struct spdk_nvme_ctrlr *ctrlr;
const struct spdk_nvme_ctrlr_data *cdata;
struct spdk_nvme_ns_data *common_ns_data;
int outstanding_admin_cmds;
struct spdk_opal_dev *opal_dev;
};
static struct dev devs[MAX_DEVS];
static int num_devs = 0;
static int g_shm_id = -1;
#define foreach_dev(iter) \
for (iter = devs; iter - devs < num_devs; iter++)
enum controller_display_model {
CONTROLLER_DISPLAY_ALL = 0x0,
CONTROLLER_DISPLAY_SIMPLISTIC = 0x1,
};
static int
cmp_devs(const void *ap, const void *bp)
{
const struct dev *a = ap, *b = bp;
return spdk_pci_addr_compare(&a->pci_addr, &b->pci_addr);
}
static bool
probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
struct spdk_nvme_ctrlr_opts *opts)
{
return true;
}
static void
identify_common_ns_cb(void *cb_arg, const struct spdk_nvme_cpl *cpl)
{
struct dev *dev = cb_arg;
if (cpl->status.sc != SPDK_NVME_SC_SUCCESS) {
/* Identify Namespace for NSID = FFFFFFFFh is optional, so failure is not fatal. */
spdk_dma_free(dev->common_ns_data);
dev->common_ns_data = NULL;
}
dev->outstanding_admin_cmds--;
}
static void
attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
{
struct dev *dev;
struct spdk_nvme_cmd cmd;
/* add to dev list */
dev = &devs[num_devs++];
spdk_pci_addr_parse(&dev->pci_addr, trid->traddr);
dev->ctrlr = ctrlr;
/* Retrieve controller data */
dev->cdata = spdk_nvme_ctrlr_get_data(dev->ctrlr);
dev->common_ns_data = spdk_dma_zmalloc(sizeof(struct spdk_nvme_ns_data), 4096, NULL);
if (dev->common_ns_data == NULL) {
fprintf(stderr, "common_ns_data allocation failure\n");
return;
}
/* Identify Namespace with NSID set to FFFFFFFFh to get common namespace capabilities. */
memset(&cmd, 0, sizeof(cmd));
cmd.opc = SPDK_NVME_OPC_IDENTIFY;
cmd.cdw10 = 0; /* CNS = 0 (Identify Namespace) */
cmd.nsid = SPDK_NVME_GLOBAL_NS_TAG;
dev->outstanding_admin_cmds++;
if (spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, dev->common_ns_data,
sizeof(struct spdk_nvme_ns_data), identify_common_ns_cb, dev) != 0) {
dev->outstanding_admin_cmds--;
spdk_dma_free(dev->common_ns_data);
dev->common_ns_data = NULL;
}
while (dev->outstanding_admin_cmds) {
spdk_nvme_ctrlr_process_admin_completions(ctrlr);
}
}
static void usage(void)
{
printf("NVMe Management Options");
printf("\n");
printf("\t[1: list controllers]\n");
printf("\t[2: create namespace]\n");
printf("\t[3: delete namespace]\n");
printf("\t[4: attach namespace to controller]\n");
printf("\t[5: detach namespace from controller]\n");
printf("\t[6: format namespace or controller]\n");
printf("\t[7: firmware update]\n");
printf("\t[8: opal]\n");
printf("\t[9: quit]\n");
}
static void
display_namespace_dpc(const struct spdk_nvme_ns_data *nsdata)
{
if (nsdata->dpc.pit1 || nsdata->dpc.pit2 || nsdata->dpc.pit3) {
if (nsdata->dpc.pit1) {
printf("PIT1 ");
}
if (nsdata->dpc.pit2) {
printf("PIT2 ");
}
if (nsdata->dpc.pit3) {
printf("PIT3 ");
}
} else {
printf("Not Supported\n");
return;
}
if (nsdata->dpc.md_start && nsdata->dpc.md_end) {
printf("Location: Head or Tail\n");
} else if (nsdata->dpc.md_start) {
printf("Location: Head\n");
} else if (nsdata->dpc.md_end) {
printf("Location: Tail\n");
} else {
printf("Not Supported\n");
}
}
static void
display_namespace(struct spdk_nvme_ns *ns)
{
const struct spdk_nvme_ns_data *nsdata;
uint32_t i;
nsdata = spdk_nvme_ns_get_data(ns);
printf("Namespace ID:%d\n", spdk_nvme_ns_get_id(ns));
printf("Size (in LBAs): %lld (%lldM)\n",
(long long)nsdata->nsze,
(long long)nsdata->nsze / 1024 / 1024);
printf("Capacity (in LBAs): %lld (%lldM)\n",
(long long)nsdata->ncap,
(long long)nsdata->ncap / 1024 / 1024);
printf("Utilization (in LBAs): %lld (%lldM)\n",
(long long)nsdata->nuse,
(long long)nsdata->nuse / 1024 / 1024);
printf("Format Progress Indicator: %s\n",
nsdata->fpi.fpi_supported ? "Supported" : "Not Supported");
if (nsdata->fpi.fpi_supported && nsdata->fpi.percentage_remaining) {
printf("Formatted Percentage: %d%%\n", 100 - nsdata->fpi.percentage_remaining);
}
printf("Number of LBA Formats: %d\n", nsdata->nlbaf + 1);
printf("Current LBA Format: LBA Format #%02d\n",
nsdata->flbas.format);
for (i = 0; i <= nsdata->nlbaf; i++)
printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d\n",
i, 1 << nsdata->lbaf[i].lbads, nsdata->lbaf[i].ms);
printf("Data Protection Capabilities:");
display_namespace_dpc(nsdata);
if (SPDK_NVME_FMT_NVM_PROTECTION_DISABLE == nsdata->dps.pit) {
printf("Data Protection Setting: N/A\n");
} else {
printf("Data Protection Setting: PIT%d Location: %s\n",
nsdata->dps.pit, nsdata->dps.md_start ? "Head" : "Tail");
}
printf("Multipath IO and Sharing: %s\n",
nsdata->nmic.can_share ? "Supported" : "Not Supported");
printf("\n");
}
static void
display_controller(struct dev *dev, int model)
{
struct spdk_nvme_ns *ns;
const struct spdk_nvme_ctrlr_data *cdata;
uint8_t str[128];
uint32_t nsid;
cdata = spdk_nvme_ctrlr_get_data(dev->ctrlr);
if (model == CONTROLLER_DISPLAY_SIMPLISTIC) {
printf("%04x:%02x:%02x.%02x ",
dev->pci_addr.domain, dev->pci_addr.bus, dev->pci_addr.dev, dev->pci_addr.func);
printf("%-40.40s %-20.20s ",
cdata->mn, cdata->sn);
printf("%5d ", cdata->cntlid);
printf("\n");
return;
}
printf("=====================================================\n");
printf("NVMe Controller: %04x:%02x:%02x.%02x\n",
dev->pci_addr.domain, dev->pci_addr.bus, dev->pci_addr.dev, dev->pci_addr.func);
printf("============================\n");
printf("Controller Capabilities/Features\n");
printf("Controller ID: %d\n", cdata->cntlid);
snprintf(str, sizeof(cdata->sn) + 1, "%s", cdata->sn);
printf("Serial Number: %s\n", str);
printf("\n");
printf("Admin Command Set Attributes\n");
printf("============================\n");
printf("Namespace Manage And Attach: %s\n",
cdata->oacs.ns_manage ? "Supported" : "Not Supported");
printf("Namespace Format: %s\n",
cdata->oacs.format ? "Supported" : "Not Supported");
printf("\n");
printf("NVM Command Set Attributes\n");
printf("============================\n");
if (cdata->fna.format_all_ns) {
printf("Namespace format operation applies to all namespaces\n");
} else {
printf("Namespace format operation applies to per namespace\n");
}
printf("\n");
printf("Namespace Attributes\n");
printf("============================\n");
for (nsid = spdk_nvme_ctrlr_get_first_active_ns(dev->ctrlr);
nsid != 0; nsid = spdk_nvme_ctrlr_get_next_active_ns(dev->ctrlr, nsid)) {
ns = spdk_nvme_ctrlr_get_ns(dev->ctrlr, nsid);
assert(ns != NULL);
display_namespace(ns);
}
}
static void
display_controller_list(void)
{
struct dev *iter;
foreach_dev(iter) {
display_controller(iter, CONTROLLER_DISPLAY_ALL);
}
}
static char *
get_line(char *buf, int buf_size, FILE *f)
{
char *ret;
size_t len;
ret = fgets(buf, buf_size, f);
if (ret == NULL) {
return NULL;
}
len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n') {
buf[len - 1] = '\0';
}
return buf;
}
static struct dev *
get_controller(void)
{
struct spdk_pci_addr pci_addr;
char address[64];
char *p;
int ch;
struct dev *iter;
memset(address, 0, sizeof(address));
foreach_dev(iter) {
display_controller(iter, CONTROLLER_DISPLAY_SIMPLISTIC);
}
printf("Please Input PCI Address(domain:bus:dev.func):\n");
while ((ch = getchar()) != '\n' && ch != EOF);
p = get_line(address, 64, stdin);
if (p == NULL) {
return NULL;
}
while (isspace(*p)) {
p++;
}
if (spdk_pci_addr_parse(&pci_addr, p) < 0) {
return NULL;
}
foreach_dev(iter) {
if (spdk_pci_addr_compare(&pci_addr, &iter->pci_addr) == 0) {
return iter;
}
}
return NULL;
}
static int
get_lba_format(const struct spdk_nvme_ns_data *ns_data)
{
int lbaf, i;
printf("\nSupported LBA formats:\n");
for (i = 0; i <= ns_data->nlbaf; i++) {
printf("%2d: %d data bytes", i, 1 << ns_data->lbaf[i].lbads);
if (ns_data->lbaf[i].ms) {
printf(" + %d metadata bytes", ns_data->lbaf[i].ms);
}
printf("\n");
}
printf("Please input LBA format index (0 - %d):\n", ns_data->nlbaf);
if (scanf("%d", &lbaf) != 1 || lbaf > ns_data->nlbaf) {
return -1;
}
return lbaf;
}
static void
identify_allocated_ns_cb(void *cb_arg, const struct spdk_nvme_cpl *cpl)
{
struct dev *dev = cb_arg;
dev->outstanding_admin_cmds--;
}
static uint32_t
get_allocated_nsid(struct dev *dev)
{
uint32_t nsid;
size_t i;
struct spdk_nvme_ns_list *ns_list;
struct spdk_nvme_cmd cmd = {0};
ns_list = spdk_dma_zmalloc(sizeof(*ns_list), 4096, NULL);
if (ns_list == NULL) {
printf("Allocation error\n");
return 0;
}
cmd.opc = SPDK_NVME_OPC_IDENTIFY;
cmd.cdw10 = SPDK_NVME_IDENTIFY_ALLOCATED_NS_LIST;
cmd.nsid = 0;
dev->outstanding_admin_cmds++;
if (spdk_nvme_ctrlr_cmd_admin_raw(dev->ctrlr, &cmd, ns_list, sizeof(*ns_list),
identify_allocated_ns_cb, dev)) {
printf("Identify command failed\n");
spdk_dma_free(ns_list);
return 0;
}
while (dev->outstanding_admin_cmds) {
spdk_nvme_ctrlr_process_admin_completions(dev->ctrlr);
}
printf("Allocated Namespace IDs:\n");
for (i = 0; i < SPDK_COUNTOF(ns_list->ns_list); i++) {
if (ns_list->ns_list[i] == 0) {
break;
}
printf("%u\n", ns_list->ns_list[i]);
}
spdk_dma_free(ns_list);
printf("Please Input Namespace ID:\n");
if (!scanf("%u", &nsid)) {
printf("Invalid Namespace ID\n");
nsid = 0;
}
return nsid;
}
static void
ns_attach(struct dev *device, int attachment_op, int ctrlr_id, int ns_id)
{
int ret = 0;
struct spdk_nvme_ctrlr_list *ctrlr_list;
ctrlr_list = spdk_dma_zmalloc(sizeof(struct spdk_nvme_ctrlr_list),
4096, NULL);
if (ctrlr_list == NULL) {
printf("Allocation error (controller list)\n");
exit(1);
}
ctrlr_list->ctrlr_count = 1;
ctrlr_list->ctrlr_list[0] = ctrlr_id;
if (attachment_op == SPDK_NVME_NS_CTRLR_ATTACH) {
ret = spdk_nvme_ctrlr_attach_ns(device->ctrlr, ns_id, ctrlr_list);
} else if (attachment_op == SPDK_NVME_NS_CTRLR_DETACH) {
ret = spdk_nvme_ctrlr_detach_ns(device->ctrlr, ns_id, ctrlr_list);
}
if (ret) {
fprintf(stdout, "ns attach: Failed\n");
}
spdk_dma_free(ctrlr_list);
}
static void
ns_manage_add(struct dev *device, uint64_t ns_size, uint64_t ns_capacity, int ns_lbasize,
uint8_t ns_dps_type, uint8_t ns_dps_location, uint8_t ns_nmic)
{
uint32_t nsid;
struct spdk_nvme_ns_data *ndata;
ndata = spdk_dma_zmalloc(sizeof(struct spdk_nvme_ns_data), 4096, NULL);
if (ndata == NULL) {
printf("Allocation error (namespace data)\n");
exit(1);
}
ndata->nsze = ns_size;
ndata->ncap = ns_capacity;
ndata->flbas.format = ns_lbasize;
if (SPDK_NVME_FMT_NVM_PROTECTION_DISABLE != ns_dps_type) {
ndata->dps.pit = ns_dps_type;
ndata->dps.md_start = ns_dps_location;
}
ndata->nmic.can_share = ns_nmic;
nsid = spdk_nvme_ctrlr_create_ns(device->ctrlr, ndata);
if (nsid == 0) {
fprintf(stdout, "ns manage: Failed\n");
} else {
printf("Created namespace ID %u\n", nsid);
}
spdk_dma_free(ndata);
}
static void
ns_manage_delete(struct dev *device, int ns_id)
{
int ret = 0;
ret = spdk_nvme_ctrlr_delete_ns(device->ctrlr, ns_id);
if (ret) {
fprintf(stdout, "ns manage: Failed\n");
return;
}
}
static void
nvme_manage_format(struct dev *device, int ns_id, int ses, int pi, int pil, int ms, int lbaf)
{
int ret = 0;
struct spdk_nvme_format format = {};
format.lbaf = lbaf;
format.ms = ms;
format.pi = pi;
format.pil = pil;
format.ses = ses;
ret = spdk_nvme_ctrlr_format(device->ctrlr, ns_id, &format);
if (ret) {
fprintf(stdout, "nvme format: Failed\n");
return;
}
}
static void
attach_and_detach_ns(int attachment_op)
{
uint32_t nsid;
struct dev *ctrlr;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI Address.\n");
return;
}
if (!ctrlr->cdata->oacs.ns_manage) {
printf("Controller does not support ns management\n");
return;
}
nsid = get_allocated_nsid(ctrlr);
if (nsid == 0) {
printf("Invalid Namespace ID\n");
return;
}
ns_attach(ctrlr, attachment_op, ctrlr->cdata->cntlid, nsid);
}
static void
add_ns(void)
{
uint64_t ns_size = 0;
uint64_t ns_capacity = 0;
int ns_lbasize;
int ns_dps_type = 0;
int ns_dps_location = 0;
int ns_nmic = 0;
struct dev *ctrlr = NULL;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI Address.\n");
return;
}
if (!ctrlr->cdata->oacs.ns_manage) {
printf("Controller does not support ns management\n");
return;
}
if (!ctrlr->common_ns_data) {
printf("Controller did not return common namespace capabilities\n");
return;
}
ns_lbasize = get_lba_format(ctrlr->common_ns_data);
if (ns_lbasize < 0) {
printf("Invalid LBA format number\n");
return;
}
printf("Please Input Namespace Size (in LBAs):\n");
if (!scanf("%" SCNu64, &ns_size)) {
printf("Invalid Namespace Size\n");
while (getchar() != '\n');
return;
}
printf("Please Input Namespace Capacity (in LBAs):\n");
if (!scanf("%" SCNu64, &ns_capacity)) {
printf("Invalid Namespace Capacity\n");
while (getchar() != '\n');
return;
}
printf("Please Input Data Protection Type (0 - 3):\n");
if (!scanf("%d", &ns_dps_type)) {
printf("Invalid Data Protection Type\n");
while (getchar() != '\n');
return;
}
if (SPDK_NVME_FMT_NVM_PROTECTION_DISABLE != ns_dps_type) {
printf("Please Input Data Protection Location (1: Head; 0: Tail):\n");
if (!scanf("%d", &ns_dps_location)) {
printf("Invalid Data Protection Location\n");
while (getchar() != '\n');
return;
}
}
printf("Please Input Multi-path IO and Sharing Capabilities (1: Share; 0: Private):\n");
if (!scanf("%d", &ns_nmic)) {
printf("Invalid Multi-path IO and Sharing Capabilities\n");
while (getchar() != '\n');
return;
}
ns_manage_add(ctrlr, ns_size, ns_capacity, ns_lbasize,
ns_dps_type, ns_dps_location, ns_nmic);
}
static void
delete_ns(void)
{
int ns_id;
struct dev *ctrlr;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI Address.\n");
return;
}
if (!ctrlr->cdata->oacs.ns_manage) {
printf("Controller does not support ns management\n");
return;
}
printf("Please Input Namespace ID:\n");
if (!scanf("%d", &ns_id)) {
printf("Invalid Namespace ID\n");
while (getchar() != '\n');
return;
}
ns_manage_delete(ctrlr, ns_id);
}
static void
format_nvm(void)
{
int ns_id;
int ses;
int pil;
int pi;
int ms;
int lbaf;
char option;
struct dev *ctrlr;
const struct spdk_nvme_ctrlr_data *cdata;
struct spdk_nvme_ns *ns;
const struct spdk_nvme_ns_data *nsdata;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI BDF.\n");
return;
}
cdata = ctrlr->cdata;
if (!cdata->oacs.format) {
printf("Controller does not support Format NVM command\n");
return;
}
if (cdata->fna.format_all_ns) {
ns_id = SPDK_NVME_GLOBAL_NS_TAG;
ns = spdk_nvme_ctrlr_get_ns(ctrlr->ctrlr, 1);
} else {
printf("Please Input Namespace ID (1 - %d):\n", cdata->nn);
if (!scanf("%d", &ns_id)) {
printf("Invalid Namespace ID\n");
while (getchar() != '\n');
return;
}
ns = spdk_nvme_ctrlr_get_ns(ctrlr->ctrlr, ns_id);
}
if (ns == NULL) {
printf("Namespace ID %d not found\n", ns_id);
while (getchar() != '\n');
return;
}
nsdata = spdk_nvme_ns_get_data(ns);
printf("Please Input Secure Erase Setting:\n");
printf(" 0: No secure erase operation requested\n");
printf(" 1: User data erase\n");
if (cdata->fna.crypto_erase_supported) {
printf(" 2: Cryptographic erase\n");
}
if (!scanf("%d", &ses)) {
printf("Invalid Secure Erase Setting\n");
while (getchar() != '\n');
return;
}
lbaf = get_lba_format(nsdata);
if (lbaf < 0) {
printf("Invalid LBA format number\n");
return;
}
if (nsdata->lbaf[lbaf].ms) {
printf("Please Input Protection Information:\n");
printf(" 0: Protection information is not enabled\n");
printf(" 1: Protection information is enabled, Type 1\n");
printf(" 2: Protection information is enabled, Type 2\n");
printf(" 3: Protection information is enabled, Type 3\n");
if (!scanf("%d", &pi)) {
printf("Invalid protection information\n");
while (getchar() != '\n');
return;
}
if (pi) {
printf("Please Input Protection Information Location:\n");
printf(" 0: Protection information transferred as the last eight bytes of metadata\n");
printf(" 1: Protection information transferred as the first eight bytes of metadata\n");
if (!scanf("%d", &pil)) {
printf("Invalid protection information location\n");
while (getchar() != '\n');
return;
}
} else {
pil = 0;
}
printf("Please Input Metadata Setting:\n");
printf(" 0: Metadata is transferred as part of a separate buffer\n");
printf(" 1: Metadata is transferred as part of an extended data LBA\n");
if (!scanf("%d", &ms)) {
printf("Invalid metadata setting\n");
while (getchar() != '\n');
return;
}
} else {
ms = 0;
pi = 0;
pil = 0;
}
printf("Warning: use this utility at your own risk.\n"
"This command will format your namespace and all data will be lost.\n"
"This command may take several minutes to complete,\n"
"so do not interrupt the utility until it completes.\n"
"Press 'Y' to continue with the format operation.\n");
while (getchar() != '\n');
if (!scanf("%c", &option)) {
printf("Invalid option\n");
while (getchar() != '\n');
return;
}
if (option == 'y' || option == 'Y') {
nvme_manage_format(ctrlr, ns_id, ses, pi, pil, ms, lbaf);
} else {
printf("NVMe format abort\n");
}
}
static void
update_firmware_image(void)
{
int rc;
int fd = -1;
int slot;
unsigned int size;
struct stat fw_stat;
char path[256];
void *fw_image;
struct dev *ctrlr;
const struct spdk_nvme_ctrlr_data *cdata;
enum spdk_nvme_fw_commit_action commit_action;
struct spdk_nvme_status status;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI BDF.\n");
return;
}
cdata = ctrlr->cdata;
if (!cdata->oacs.firmware) {
printf("Controller does not support firmware download and commit command\n");
return;
}
printf("Please Input The Path Of Firmware Image\n");
if (get_line(path, sizeof(path), stdin) == NULL) {
printf("Invalid path setting\n");
while (getchar() != '\n');
return;
}
fd = open(path, O_RDONLY);
if (fd < 0) {
perror("Open file failed");
return;
}
rc = fstat(fd, &fw_stat);
if (rc < 0) {
printf("Fstat failed\n");
close(fd);
return;
}
if (fw_stat.st_size % 4) {
printf("Firmware image size is not multiple of 4\n");
close(fd);
return;
}
size = fw_stat.st_size;
fw_image = spdk_dma_zmalloc(size, 4096, NULL);
if (fw_image == NULL) {
printf("Allocation error\n");
close(fd);
return;
}
if (read(fd, fw_image, size) != ((ssize_t)(size))) {
printf("Read firmware image failed\n");
close(fd);
spdk_dma_free(fw_image);
return;
}
close(fd);
printf("Please Input Slot(0 - 7):\n");
if (!scanf("%d", &slot)) {
printf("Invalid Slot\n");
spdk_dma_free(fw_image);
while (getchar() != '\n');
return;
}
commit_action = SPDK_NVME_FW_COMMIT_REPLACE_AND_ENABLE_IMG;
rc = spdk_nvme_ctrlr_update_firmware(ctrlr->ctrlr, fw_image, size, slot, commit_action, &status);
if (rc == -ENXIO && status.sct == SPDK_NVME_SCT_COMMAND_SPECIFIC &&
status.sc == SPDK_NVME_SC_FIRMWARE_REQ_CONVENTIONAL_RESET) {
printf("conventional reset is needed to enable firmware !\n");
} else if (rc) {
printf("spdk_nvme_ctrlr_update_firmware failed\n");
} else {
printf("spdk_nvme_ctrlr_update_firmware success\n");
}
spdk_dma_free(fw_image);
}
static void
opal_dump_info(struct spdk_opal_info *opal)
{
if (!opal->opal_ssc_dev) {
SPDK_ERRLOG("This device is not Opal enabled. Not Supported!\n");
return;
}
if (opal->tper) {
printf("\nOpal TPer feature:\n");
printf("ACKNACK = %s", (opal->tper_acknack ? "Y, " : "N, "));
printf("ASYNC = %s", (opal->tper_async ? "Y, " : "N, "));
printf("BufferManagement = %s\n", (opal->tper_buffer_mgt ? "Y, " : "N, "));
printf("ComIDManagement = %s", (opal->tper_comid_mgt ? "Y, " : "N, "));
printf("Streaming = %s", (opal->tper_streaming ? "Y, " : "N, "));
printf("Sync = %s\n", (opal->tper_sync ? "Y" : "N"));
printf("\n");
}
if (opal->locking) {
printf("Opal Locking feature:\n");
printf("Locked = %s", (opal->locking_locked ? "Y, " : "N, "));
printf("Locking Enabled = %s", (opal->locking_locking_enabled ? "Y, " : "N, "));
printf("Locking supported = %s\n", (opal->locking_locking_supported ? "Y" : "N"));
printf("MBR done = %s", (opal->locking_mbr_done ? "Y, " : "N, "));
printf("MBR enabled = %s", (opal->locking_mbr_enabled ? "Y, " : "N, "));
printf("Media encrypt = %s\n", (opal->locking_media_encrypt ? "Y" : "N"));
printf("\n");
}
if (opal->geometry) {
printf("Opal Geometry feature:\n");
printf("Align = %s", (opal->geometry_align ? "Y, " : "N, "));
printf("Logical block size = %d, ", opal->geometry_logical_block_size);
printf("Lowest aligned LBA = %ld\n", opal->geometry_lowest_aligned_lba);
printf("\n");
}
if (opal->single_user_mode) {
printf("Opal Single User Mode feature:\n");
printf("Any in SUM = %s", (opal->single_user_any ? "Y, " : "N, "));
printf("All in SUM = %s", (opal->single_user_all ? "Y, " : "N, "));
printf("Policy: %s Authority,\n", (opal->single_user_policy ? "Admin" : "Users"));
printf("Number of locking objects = %d\n ", opal->single_user_locking_objects);
printf("\n");
}
if (opal->datastore) {
printf("Opal DataStore feature:\n");
printf("Table alignment = %d, ", opal->datastore_alignment);
printf("Max number of tables = %d, ", opal->datastore_max_tables);
printf("Max size of tables = %d\n", opal->datastore_max_table_size);
printf("\n");
}
if (opal->opal_v100) {
printf("Opal V100 feature:\n");
printf("Base comID = %d, ", opal->opal_v100_base_comid);
printf("Number of comIDs = %d, ", opal->opal_v100_num_comid);
printf("Range crossing = %s\n", (opal->opal_v100_range_crossing ? "N" : "Y"));
printf("\n");
}
if (opal->opal_v200) {
printf("Opal V200 feature:\n");
printf("Base comID = %d, ", opal->opal_v200_base_comid);
printf("Number of comIDs = %d, ", opal->opal_v200_num_comid);
printf("Initial PIN = %d,\n", opal->opal_v200_initial_pin);
printf("Reverted PIN = %d, ", opal->opal_v200_reverted_pin);
printf("Number of admins = %d, ", opal->opal_v200_num_admin);
printf("Number of users = %d\n", opal->opal_v200_num_user);
printf("\n\n");
}
}
static void
opal_usage(void)
{
printf("Opal General Usage:\n");
printf("\n");
printf("\t[1: scan device]\n");
printf("\t[2: take ownership]\n");
printf("\t[3: revert tper]\n");
printf("\t[0: quit]\n");
}
static void
opal_scan(struct dev *iter)
{
if (spdk_nvme_ctrlr_get_flags(iter->ctrlr) & SPDK_NVME_CTRLR_SECURITY_SEND_RECV_SUPPORTED) {
iter->opal_dev = spdk_opal_init_dev(iter->ctrlr);
if (iter->opal_dev == NULL) {
return;
}
if (spdk_opal_supported(iter->opal_dev)) {
printf("\n\nOpal Supported:\n");
display_controller(iter, CONTROLLER_DISPLAY_SIMPLISTIC);
spdk_opal_scan(iter->opal_dev);
opal_dump_info(spdk_opal_get_info(iter->opal_dev));
}
spdk_opal_close(iter->opal_dev);
} else {
printf("%04x:%02x:%02x.%02x: NVMe Security Support/Receive Not supported.\n",
iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func);
printf("%04x:%02x:%02x.%02x: Opal Not Supported\n\n\n",
iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func);
}
}
static void
opal_take_ownership(struct dev *iter)
{
char new_passwd[MAX_PASSWORD_SIZE] = {0};
char *passwd_p;
int ret;
int ch;
if (spdk_nvme_ctrlr_get_flags(iter->ctrlr) & SPDK_NVME_CTRLR_SECURITY_SEND_RECV_SUPPORTED) {
iter->opal_dev = spdk_opal_init_dev(iter->ctrlr);
if (iter->opal_dev == NULL) {
return;
}
if (spdk_opal_supported(iter->opal_dev)) {
printf("Please input the new password for ownership:\n");
while ((ch = getchar()) != '\n' && ch != EOF);
passwd_p = get_line(new_passwd, MAX_PASSWORD_SIZE, stdin);
if (passwd_p) {
ret = spdk_opal_cmd(iter->opal_dev, OPAL_CMD_TAKE_OWNERSHIP, passwd_p);
if (ret) {
printf("Take ownership failure: %d\n", ret);
return;
}
printf("...\n...\nTake Ownership Success\n");
} else {
printf("Input password invalid. Take ownership failure\n");
}
}
spdk_opal_close(iter->opal_dev);
} else {
printf("%04x:%02x:%02x.%02x: NVMe Security Support/Receive Not supported.\nOpal Not Supported\n\n\n",
iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func);
}
}
static void
opal_revert_tper(struct dev *iter)
{
char passwd[MAX_PASSWORD_SIZE] = {0};
char *passwd_p;
int ret;
int ch;
if (spdk_nvme_ctrlr_get_flags(iter->ctrlr) & SPDK_NVME_CTRLR_SECURITY_SEND_RECV_SUPPORTED) {
iter->opal_dev = spdk_opal_init_dev(iter->ctrlr);
if (iter->opal_dev == NULL) {
return;
}
if (spdk_opal_supported(iter->opal_dev)) {
printf("Please be noted this operation will erase ALL DATA on this drive\n");
printf("Please input password for revert TPer:\n");
while ((ch = getchar()) != '\n' && ch != EOF);
passwd_p = get_line(passwd, MAX_PASSWORD_SIZE, stdin);
if (passwd_p) {
ret = spdk_opal_cmd(iter->opal_dev, OPAL_CMD_REVERT_TPER, passwd_p);
if (ret) {
printf("Revert TPer failure: %d\n", ret);
return;
}
printf("...\n...\nRevert TPer Success\n");
} else {
printf("Input password invalid. Revert TPer failure\n");
}
}
spdk_opal_close(iter->opal_dev);
} else {
printf("%04x:%02x:%02x.%02x: NVMe Security Support/Receive Not supported.\nOpal Not Supported\n\n\n",
iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func);
}
}
static void
test_opal(void)
{
int exit_flag = false;
struct dev *ctrlr;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI Address.\n");
return;
}
opal_usage();
while (!exit_flag) {
int cmd;
if (!scanf("%d", &cmd)) {
printf("Invalid Command: command must be number 0-2\n");
while (getchar() != '\n');
opal_usage();
continue;
}
switch (cmd) {
case 0:
exit_flag = true;
continue;
case 1:
opal_scan(ctrlr);
break;
case 2:
opal_take_ownership(ctrlr);
break;
case 3:
opal_revert_tper(ctrlr);
break;
default:
printf("Invalid option\n");
}
while (getchar() != '\n');
printf("press Enter to display Opal cmd menu ...\n");
while (getchar() != '\n');
opal_usage();
}
}
static void
args_usage(const char *program_name)
{
printf("%s [options]", program_name);
printf("\n");
printf("options:\n");
printf(" -i shared memory group ID\n");
}
static int
parse_args(int argc, char **argv)
{
int op;
while ((op = getopt(argc, argv, "i:")) != -1) {
switch (op) {
case 'i':
g_shm_id = spdk_strtol(optarg, 10);
if (g_shm_id < 0) {
fprintf(stderr, "Invalid shared memory ID\n");
return g_shm_id;
}
break;
default:
args_usage(argv[0]);
return 1;
}
}
return 0;
}
int main(int argc, char **argv)
{
int i, rc;
struct spdk_env_opts opts;
rc = parse_args(argc, argv);
if (rc != 0) {
return rc;
}
spdk_env_opts_init(&opts);
opts.name = "nvme_manage";
opts.core_mask = "0x1";
opts.shm_id = g_shm_id;
if (spdk_env_init(&opts) < 0) {
fprintf(stderr, "Unable to initialize SPDK env\n");
return 1;
}
if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL) != 0) {
fprintf(stderr, "spdk_nvme_probe() failed\n");
return 1;
}
qsort(devs, num_devs, sizeof(devs[0]), cmp_devs);
usage();
while (1) {
int cmd;
bool exit_flag = false;
if (!scanf("%d", &cmd)) {
printf("Invalid Command: command must be number 1-8\n");
while (getchar() != '\n');
usage();
continue;
}
switch (cmd) {
case 1:
display_controller_list();
break;
case 2:
add_ns();
break;
case 3:
delete_ns();
break;
case 4:
attach_and_detach_ns(SPDK_NVME_NS_CTRLR_ATTACH);
break;
case 5:
attach_and_detach_ns(SPDK_NVME_NS_CTRLR_DETACH);
break;
case 6:
format_nvm();
break;
case 7:
update_firmware_image();
break;
case 8:
test_opal();
break;
case 9:
exit_flag = true;
break;
default:
printf("Invalid Command\n");
break;
}
if (exit_flag) {
break;
}
while (getchar() != '\n');
printf("press Enter to display cmd menu ...\n");
while (getchar() != '\n');
usage();
}
printf("Cleaning up...\n");
for (i = 0; i < num_devs; i++) {
struct dev *dev = &devs[i];
spdk_nvme_detach(dev->ctrlr);
}
return 0;
}