summaryrefslogtreecommitdiffstats
path: root/nvme.c
diff options
context:
space:
mode:
Diffstat (limited to 'nvme.c')
-rw-r--r--nvme.c1118
1 files changed, 889 insertions, 229 deletions
diff --git a/nvme.c b/nvme.c
index e9aac8f..4a65c4b 100644
--- a/nvme.c
+++ b/nvme.c
@@ -52,9 +52,10 @@
#include <sys/random.h>
#endif
+#include <libnvme.h>
+
#include "common.h"
#include "nvme.h"
-#include "libnvme.h"
#include "nvme-print.h"
#include "plugin.h"
#include "util/base64.h"
@@ -62,6 +63,7 @@
#include "nvme-wrap.h"
#include "util/argconfig.h"
#include "util/suffix.h"
+#include "util/logging.h"
#include "fabrics.h"
#define CREATE_CMD
#include "nvme-builtin.h"
@@ -106,9 +108,64 @@ struct passthru_config {
bool latency;
};
+struct get_reg_config {
+ int offset;
+ bool human_readable;
+ bool cap;
+ bool vs;
+ bool intms;
+ bool intmc;
+ bool cc;
+ bool csts;
+ bool nssr;
+ bool aqa;
+ bool asq;
+ bool acq;
+ bool cmbloc;
+ bool cmbsz;
+ bool bpinfo;
+ bool bprsel;
+ bool bpmbl;
+ bool cmbmsc;
+ bool cmbsts;
+ bool cmbebs;
+ bool cmbswtp;
+ bool nssd;
+ bool crto;
+ bool pmrcap;
+ bool pmrctl;
+ bool pmrsts;
+ bool pmrebs;
+ bool pmrswtp;
+ bool pmrmscl;
+ bool pmrmscu;
+ bool fabrics;
+};
+
+struct set_reg_config {
+ int offset;
+ bool mmio32;
+ __u64 value;
+ __u32 intms;
+ __u32 intmc;
+ __u32 cc;
+ __u32 csts;
+ __u32 nssr;
+ __u32 aqa;
+ __u64 asq;
+ __u64 acq;
+ __u32 bprsel;
+ __u64 bpmbl;
+ __u64 cmbmsc;
+ __u32 nssd;
+ __u32 pmrctl;
+ __u32 pmrmscl;
+ __u32 pmrmscu;
+};
+
#define NVME_ARGS(n, ...) \
struct argconfig_commandline_options n[] = { \
- OPT_FLAG("verbose", 'v', NULL, verbose), \
+ OPT_INCR("verbose", 'v', &verbose_level, verbose), \
OPT_FMT("output-format", 'o', &output_format_val, output_format), \
##__VA_ARGS__, \
OPT_END() \
@@ -184,10 +241,27 @@ static const char *uuid_index_specify = "specify uuid index";
static const char *verbose = "Increase output verbosity";
static const char dash[51] = {[0 ... 49] = '=', '\0'};
static const char space[51] = {[0 ... 49] = ' ', '\0'};
+static const char *offset = "offset of the requested register";
+static const char *intms = "INTMS=0xc register offset";
+static const char *intmc = "INTMC=0x10 register offset";
+static const char *cc = "CC=0x14 register offset";
+static const char *csts = "CSTS=0x1c register offset";
+static const char *nssr = "NSSR=0x20 register offset";
+static const char *aqa = "AQA=0x24 register offset";
+static const char *asq = "ASQ=0x28 register offset";
+static const char *acq = "ACQ=0x30 register offset";
+static const char *bprsel = "BPRSEL=0x44 register offset";
+static const char *bpmbl = "BPMBL=0x48 register offset";
+static const char *cmbmsc = "CMBMSC=0x50 register offset";
+static const char *nssd = "NSSD=0x64 register offset";
+static const char *pmrctl = "PMRCTL=0xe04 register offset";
+static const char *pmrmscl = "PMRMSCL=0xe14 register offset";
+static const char *pmrmscu = "PMRMSCU=0xe18 register offset";
static char *output_format_val = "normal";
+int verbose_level;
-static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev);
+static void *mmap_registers(struct nvme_dev *dev, bool writable);
const char *nvme_strerror(int errnum)
{
@@ -196,36 +270,6 @@ const char *nvme_strerror(int errnum)
return strerror(errnum);
}
-int map_log_level(int verbose, bool quiet)
-{
- int log_level;
-
- /*
- * LOG_NOTICE is unused thus the user has to provide two 'v' for getting
- * any feedback at all. Thus skip this level
- */
- verbose++;
-
- switch (verbose) {
- case 0:
- log_level = LOG_WARNING;
- break;
- case 1:
- log_level = LOG_NOTICE;
- break;
- case 2:
- log_level = LOG_INFO;
- break;
- default:
- log_level = LOG_DEBUG;
- break;
- }
- if (quiet)
- log_level = LOG_ERR;
-
- return log_level;
-}
-
static ssize_t getrandom_bytes(void *buf, size_t buflen)
{
#if HAVE_SYS_RANDOM
@@ -281,7 +325,8 @@ static int open_dev_direct(struct nvme_dev **devp, char *devstr, int flags)
}
if (!is_chardev(dev) && !is_blkdev(dev)) {
nvme_show_error("%s is not a block or character device", devstr);
- err = -ENODEV;
+ errno = ENODEV;
+ err = -1;
goto err_close;
}
*devp = dev;
@@ -400,8 +445,8 @@ int parse_and_open(struct nvme_dev **dev, int argc, char **argv,
ret = get_dev(dev, argc, argv, O_RDONLY);
if (ret < 0)
argconfig_print_help(desc, opts);
- else if (argconfig_parse_seen(opts, "verbose"))
- nvme_cli_set_debug(*dev, true);
+ else
+ log_level = map_log_level(verbose_level, false);
return ret;
}
@@ -531,7 +576,7 @@ static int get_ana_log(int argc, char **argv, struct command *cmd,
"decoded format (default), json or binary.";
const char *groups = "Return ANA groups only.";
- _cleanup_nvme_dev_ struct nvme_dev *dev= NULL;
+ _cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
_cleanup_free_ struct nvme_id_ctrl *ctrl = NULL;
_cleanup_free_ void *ana_log = NULL;
size_t ana_log_len;
@@ -868,7 +913,9 @@ static int get_telemetry_log(int argc, char **argv, struct command *cmd,
while (data_remaining) {
data_written = write(output, data_ptr, data_remaining);
if (data_written < 0) {
- data_remaining = data_written;
+ err = -errno;
+ nvme_show_error("ERROR: %s: : write failed with error : %s",
+ __func__, strerror(errno));
break;
} else if (data_written <= data_remaining) {
data_remaining -= data_written;
@@ -877,6 +924,7 @@ static int get_telemetry_log(int argc, char **argv, struct command *cmd,
/* Unexpected overwrite */
fprintf(stderr, "Failure: Unexpected telemetry log overwrite - data_remaining = 0x%x, data_written = 0x%x\n",
data_remaining, data_written);
+ err = -1;
break;
}
}
@@ -1007,12 +1055,9 @@ static int get_effects_log(int argc, char **argv, struct command *cmd, struct pl
list_head_init(&log_pages);
if (cfg.csi < 0) {
- nvme_root_t r;
__u64 cap;
- r = nvme_scan(NULL);
- bar = mmap_registers(r, dev);
- nvme_free_tree(r);
+ bar = mmap_registers(dev, false);
if (bar) {
cap = mmio_read64(bar + NVME_REG_CAP);
@@ -1172,7 +1217,7 @@ static int get_fw_log(int argc, char **argv, struct command *cmd, struct plugin
const char *desc = "Retrieve the firmware log for the "
"specified device in either decoded format (default) or binary.";
- _cleanup_free_ struct nvme_firmware_slot *fw_log = NULL;;
+ _cleanup_free_ struct nvme_firmware_slot *fw_log = NULL;
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
enum nvme_print_flags flags;
int err;
@@ -3251,7 +3296,8 @@ static int list_subsys(int argc, char **argv, struct command *cmd,
if (argconfig_parse_seen(opts, "verbose"))
flags |= VERBOSE;
- r = nvme_create_root(stderr, map_log_level(!!(flags & VERBOSE), false));
+
+ r = nvme_create_root(stderr, log_level);
if (!r) {
if (devname)
nvme_show_error("Failed to scan nvme subsystem for %s", devname);
@@ -3274,7 +3320,8 @@ static int list_subsys(int argc, char **argv, struct command *cmd,
err = nvme_scan_topology(r, filter, (void *)devname);
if (err) {
- nvme_show_error("Failed to scan topology: %s", nvme_strerror(errno));
+ if (errno != ENOENT)
+ nvme_show_error("Failed to scan topology: %s", nvme_strerror(errno));
goto ret;
}
@@ -3308,14 +3355,15 @@ static int list(int argc, char **argv, struct command *cmd, struct plugin *plugi
if (argconfig_parse_seen(opts, "verbose"))
flags |= VERBOSE;
- r = nvme_create_root(stderr, map_log_level(!!(flags & VERBOSE), false));
+ r = nvme_create_root(stderr, log_level);
if (!r) {
nvme_show_error("Failed to create topology root: %s", nvme_strerror(errno));
return -errno;
}
err = nvme_scan_topology(r, NULL, NULL);
if (err < 0) {
- nvme_show_error("Failed to scan topology: %s", nvme_strerror(errno));
+ if (errno != ENOENT)
+ nvme_show_error("Failed to scan topology: %s", nvme_strerror(errno));
nvme_free_tree(r);
return err;
}
@@ -3584,7 +3632,7 @@ static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *p
const char *raw = "show descriptors in binary format";
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
- _cleanup_free_ void *nsdescs = NULL;;
+ _cleanup_free_ void *nsdescs = NULL;
enum nvme_print_flags flags;
int err;
@@ -3615,6 +3663,9 @@ static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *p
if (cfg.raw_binary)
flags = BINARY;
+ if (argconfig_parse_seen(opts, "verbose"))
+ flags |= VERBOSE;
+
if (!cfg.namespace_id) {
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
if (err < 0) {
@@ -4824,7 +4875,7 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin
int err;
struct stat sb;
void *fw_buf;
- struct nvme_id_ctrl ctrl;
+ struct nvme_id_ctrl ctrl = { 0 };
struct config {
char *fw;
@@ -4836,7 +4887,7 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin
struct config cfg = {
.fw = "",
- .xfer = 4096,
+ .xfer = 0,
.offset = 0,
.progress = false,
.ignore_ovr = false,
@@ -4885,6 +4936,10 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin
} else if (cfg.xfer % 4096)
cfg.xfer = 4096;
+ if (ctrl.fwug && ctrl.fwug != 0xff && fw_size % cfg.xfer)
+ nvme_show_error("WARNING: firmware file size %u not conform to FWUG alignment %lu",
+ fw_size, cfg.xfer);
+
fw_buf = nvme_alloc_huge(fw_size, &mh);
if (!fw_buf)
return -ENOMEM;
@@ -5119,7 +5174,7 @@ static int ns_rescan(int argc, char **argv, struct command *cmd, struct plugin *
err = nvme_ns_rescan(dev_fd(dev));
if (err < 0)
- nvme_show_error("Namespace Rescan");
+ nvme_show_error("Namespace Rescan: %s\n", nvme_strerror(errno));
return err;
}
@@ -5224,42 +5279,62 @@ static int sanitize_cmd(int argc, char **argv, struct command *cmd, struct plugi
return err;
}
-static int nvme_get_properties(int fd, void **pbar)
+static int nvme_get_single_property(int fd, struct get_reg_config *cfg, __u64 *value)
+{
+ int err;
+ struct nvme_get_property_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .offset = cfg->offset,
+ .value = value,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ };
+
+ err = nvme_get_property(&args);
+ if (!err)
+ return 0;
+
+ if (!cfg->fabrics &&
+ nvme_status_equals(err, NVME_STATUS_TYPE_NVME, NVME_SC_INVALID_FIELD)) {
+ *value = -1;
+ return 0;
+ }
+
+ if (cfg->fabrics && err > 0)
+ nvme_show_status(err);
+ else
+ nvme_show_error("get-property: %s", nvme_strerror(errno));
+
+ return err;
+}
+
+static int nvme_get_properties(int fd, void **pbar, struct get_reg_config *cfg)
{
- int offset, err, size = getpagesize();
+ int err, size = getpagesize();
+ bool is_64bit = false;
__u64 value;
- void *bar = malloc(size);
+ void *bar;
+ int offset;
+ bar = malloc(size);
if (!bar) {
nvme_show_error("malloc: %s", strerror(errno));
- return -1;
+ return -errno;
}
memset(bar, 0xff, size);
- for (offset = NVME_REG_CAP; offset <= NVME_REG_CMBSZ;) {
- struct nvme_get_property_args args = {
- .args_size = sizeof(args),
- .fd = fd,
- .offset = offset,
- .value = &value,
- .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
- };
-
- err = nvme_get_property(&args);
- if (nvme_status_equals(err, NVME_STATUS_TYPE_NVME, NVME_SC_INVALID_FIELD)) {
- err = 0;
- value = -1;
- } else if (err) {
- nvme_show_error("get-property: %s", nvme_strerror(errno));
+ for (offset = NVME_REG_CAP; offset <= NVME_REG_CMBSZ;
+ offset += is_64bit ? sizeof(uint64_t) : sizeof(uint32_t)) {
+ cfg->offset = offset;
+ err = nvme_get_single_property(fd, cfg, &value);
+ if (err)
break;
- }
- if (nvme_is_64bit_reg(offset)) {
- *(uint64_t *)(bar + offset) = value;
- offset += 8;
- } else {
- *(uint32_t *)(bar + offset) = value;
- offset += 4;
- }
+
+ is_64bit = nvme_is_64bit_reg(cfg->offset);
+ if (is_64bit)
+ *(uint64_t *)(bar + cfg->offset) = value;
+ else
+ *(uint32_t *)(bar + cfg->offset) = value;
}
if (err)
@@ -5270,42 +5345,28 @@ static int nvme_get_properties(int fd, void **pbar)
return err;
}
-static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev)
+static void *mmap_registers(struct nvme_dev *dev, bool writable)
{
- nvme_ctrl_t c = NULL;
- nvme_ns_t n = NULL;
-
char path[512];
void *membase;
int fd;
+ int prot = PROT_READ;
- c = nvme_scan_ctrl(r, dev->name);
- if (c) {
- snprintf(path, sizeof(path), "%s/device/resource0",
- nvme_ctrl_get_sysfs_dir(c));
- nvme_free_ctrl(c);
- } else {
- n = nvme_scan_namespace(dev->name);
- if (!n) {
- nvme_show_error("Unable to find %s", dev->name);
- return NULL;
- }
- snprintf(path, sizeof(path), "%s/device/device/resource0",
- nvme_ns_get_sysfs_dir(n));
- nvme_free_ns(n);
- }
+ if (writable)
+ prot |= PROT_WRITE;
- fd = open(path, O_RDONLY);
+ sprintf(path, "/sys/class/nvme/%s/device/resource0", dev->name);
+ fd = open(path, writable ? O_RDWR : O_RDONLY);
if (fd < 0) {
- if (map_log_level(0, false) >= LOG_DEBUG)
+ if (log_level >= LOG_DEBUG)
nvme_show_error("%s did not find a pci resource, open failed %s",
dev->name, strerror(errno));
return NULL;
}
- membase = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0);
+ membase = mmap(NULL, getpagesize(), prot, MAP_SHARED, fd, 0);
if (membase == MAP_FAILED) {
- if (map_log_level(0, false) >= LOG_DEBUG) {
+ if (log_level >= LOG_DEBUG) {
fprintf(stderr, "%s failed to map. ", dev->name);
fprintf(stderr, "Did your kernel enable CONFIG_IO_STRICT_DEVMEM?\n");
}
@@ -5326,15 +5387,10 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
enum nvme_print_flags flags;
bool fabrics = false;
- nvme_root_t r;
void *bar;
int err;
- struct config {
- bool human_readable;
- };
-
- struct config cfg = {
+ struct get_reg_config cfg = {
.human_readable = false,
};
@@ -5345,21 +5401,20 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu
if (err)
return err;
- r = nvme_scan(NULL);
err = validate_output_format(output_format_val, &flags);
if (err < 0) {
nvme_show_error("Invalid output format");
- goto free_tree;
+ return err;
}
if (cfg.human_readable)
flags |= VERBOSE;
- bar = mmap_registers(r, dev);
+ bar = mmap_registers(dev, false);
if (!bar) {
- err = nvme_get_properties(dev_fd(dev), &bar);
+ err = nvme_get_properties(dev_fd(dev), &bar, &cfg);
if (err)
- goto free_tree;
+ return err;
fabrics = true;
}
@@ -5368,8 +5423,575 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu
free(bar);
else
munmap(bar, getpagesize());
-free_tree:
- nvme_free_tree(r);
+
+ return 0;
+}
+
+int get_reg_size(int offset)
+{
+ return nvme_is_64bit_reg(offset) ? sizeof(uint64_t) : sizeof(uint32_t);
+}
+
+static bool is_reg_selected(struct get_reg_config *cfg, int offset)
+{
+ switch (offset) {
+ case NVME_REG_CAP:
+ return cfg->cap;
+ case NVME_REG_VS:
+ return cfg->vs;
+ case NVME_REG_INTMS:
+ return cfg->intms;
+ case NVME_REG_INTMC:
+ return cfg->intmc;
+ case NVME_REG_CC:
+ return cfg->cc;
+ case NVME_REG_CSTS:
+ return cfg->csts;
+ case NVME_REG_NSSR:
+ return cfg->nssr;
+ case NVME_REG_AQA:
+ return cfg->aqa;
+ case NVME_REG_ASQ:
+ return cfg->asq;
+ case NVME_REG_ACQ:
+ return cfg->acq;
+ case NVME_REG_CMBLOC:
+ return cfg->cmbloc;
+ case NVME_REG_CMBSZ:
+ return cfg->cmbsz;
+ case NVME_REG_BPINFO:
+ return cfg->bpinfo;
+ case NVME_REG_BPRSEL:
+ return cfg->bprsel;
+ case NVME_REG_BPMBL:
+ return cfg->bpmbl;
+ case NVME_REG_CMBMSC:
+ return cfg->cmbmsc;
+ case NVME_REG_CMBSTS:
+ return cfg->cmbsts;
+ case NVME_REG_CMBEBS:
+ return cfg->cmbebs;
+ case NVME_REG_CMBSWTP:
+ return cfg->cmbswtp;
+ case NVME_REG_NSSD:
+ return cfg->nssd;
+ case NVME_REG_CRTO:
+ return cfg->crto;
+ case NVME_REG_PMRCAP:
+ return cfg->pmrcap;
+ case NVME_REG_PMRCTL:
+ return cfg->pmrctl;
+ case NVME_REG_PMRSTS:
+ return cfg->pmrsts;
+ case NVME_REG_PMREBS:
+ return cfg->pmrebs;
+ case NVME_REG_PMRSWTP:
+ return cfg->pmrswtp;
+ case NVME_REG_PMRMSCL:
+ return cfg->pmrmscl;
+ case NVME_REG_PMRMSCU:
+ return cfg->pmrmscu;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static int get_register_properties(int fd, void **pbar, struct get_reg_config *cfg)
+{
+ int offset = NVME_REG_CRTO;
+ __u64 value;
+ int size;
+ int err;
+ void *bar;
+ struct nvme_get_property_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .value = &value,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ };
+
+ size = offset + get_reg_size(offset);
+ bar = malloc(size);
+ if (!bar) {
+ nvme_show_error("malloc: %s", strerror(errno));
+ return -1;
+ }
+
+ for (offset = NVME_REG_CAP; offset <= NVME_REG_CRTO; offset += get_reg_size(offset)) {
+ if ((cfg->offset != offset && !is_reg_selected(cfg, offset)) ||
+ !nvme_is_fabrics_reg(offset))
+ continue;
+
+ args.offset = offset;
+ err = nvme_get_property(&args);
+ if (nvme_status_equals(err, NVME_STATUS_TYPE_NVME, NVME_SC_INVALID_FIELD)) {
+ value = -1;
+ } else if (err) {
+ nvme_show_error("get-property: %s", nvme_strerror(errno));
+ free(bar);
+ return err;
+ }
+
+ if (nvme_is_64bit_reg(offset))
+ *(uint64_t *)(bar + offset) = value;
+ else
+ *(uint32_t *)(bar + offset) = value;
+ }
+
+ *pbar = bar;
+
+ return 0;
+}
+
+bool nvme_is_ctrl_reg(int offset)
+{
+ switch (offset) {
+ case NVME_REG_CAP:
+ case NVME_REG_VS:
+ case NVME_REG_INTMS:
+ case NVME_REG_INTMC:
+ case NVME_REG_CC:
+ case NVME_REG_CSTS:
+ case NVME_REG_NSSR:
+ case NVME_REG_AQA:
+ case NVME_REG_ASQ:
+ case NVME_REG_ACQ:
+ case NVME_REG_CMBLOC:
+ case NVME_REG_CMBSZ:
+ case NVME_REG_BPINFO:
+ case NVME_REG_BPRSEL:
+ case NVME_REG_BPMBL:
+ case NVME_REG_CMBMSC:
+ case NVME_REG_CMBSTS:
+ case NVME_REG_CMBEBS:
+ case NVME_REG_CMBSWTP:
+ case NVME_REG_NSSD:
+ case NVME_REG_CRTO:
+ case NVME_REG_PMRCAP:
+ case NVME_REG_PMRCTL:
+ case NVME_REG_PMRSTS:
+ case NVME_REG_PMREBS:
+ case NVME_REG_PMRSWTP:
+ case NVME_REG_PMRMSCL:
+ case NVME_REG_PMRMSCU:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool get_register_offset(void *bar, bool fabrics, struct get_reg_config *cfg,
+ enum nvme_print_flags flags)
+{
+ bool offset_matched = cfg->offset >= 0;
+ int offset;
+
+ if (offset_matched)
+ nvme_show_ctrl_register(bar, fabrics, cfg->offset, flags);
+
+ for (offset = NVME_REG_CAP; offset <= NVME_REG_PMRMSCU; offset += get_reg_size(offset)) {
+ if (!nvme_is_ctrl_reg(offset) || offset == cfg->offset || !is_reg_selected(cfg, offset))
+ continue;
+ nvme_show_ctrl_register(bar, fabrics, offset, flags);
+ if (!offset_matched)
+ offset_matched = true;
+ }
+
+ return offset_matched;
+}
+
+static int get_register(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Reads and shows the defined NVMe controller register.\n"
+ "Register offset must be one of:\n"
+ "CAP=0x0, VS=0x8, INTMS=0xc, INTMC=0x10, CC=0x14, CSTS=0x1c,\n"
+ "NSSR=0x20, AQA=0x24, ASQ=0x28, ACQ=0x30, CMBLOC=0x38,\n"
+ "CMBSZ=0x3c, BPINFO=0x40, BPRSEL=0x44, BPMBL=0x48, CMBMSC=0x50,\n"
+ "CMBSTS=0x58, CRTO=0x68, PMRCAP=0xe00, PMRCTL=0xe04,\n"
+ "PMRSTS=0xe08, PMREBS=0xe0c, PMRSWTP=0xe10, PMRMSCL=0xe14, PMRMSCU=0xe18";
+ const char *human_readable = "show register in readable format";
+ const char *cap = "CAP=0x0 register offset";
+ const char *vs = "VS=0x8 register offset";
+ const char *cmbloc = "CMBLOC=0x38 register offset";
+ const char *cmbsz = "CMBSZ=0x3c register offset";
+ const char *bpinfo = "BPINFO=0x40 register offset";
+ const char *cmbsts = "CMBSTS=0x58 register offset";
+ const char *cmbebs = "CMBEBS=0x5c register offset";
+ const char *cmbswtp = "CMBSWTP=0x60 register offset";
+ const char *crto = "CRTO=0x68 register offset";
+ const char *pmrcap = "PMRCAP=0xe00 register offset";
+ const char *pmrsts = "PMRSTS=0xe08 register offset";
+ const char *pmrebs = "PMREBS=0xe0c register offset";
+ const char *pmrswtp = "PMRSWTP=0xe10 register offset";
+ const char *pmrmscl = "PMRMSCL=0xe14 register offset";
+ const char *pmrmscu = "PMRMSCU=0xe18 register offset";
+
+ _cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
+ int err;
+ enum nvme_print_flags flags;
+ bool fabrics = false;
+
+ void *bar;
+
+ struct get_reg_config cfg = {
+ .offset = -1,
+ };
+
+ NVME_ARGS(opts,
+ OPT_UINT("offset", 'O', &cfg.offset, offset),
+ OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
+ OPT_FLAG("cap", 0, &cfg.cap, cap),
+ OPT_FLAG("vs", 0, &cfg.vs, vs),
+ OPT_FLAG("cmbloc", 0, &cfg.cmbloc, cmbloc),
+ OPT_FLAG("cmbsz", 0, &cfg.cmbsz, cmbsz),
+ OPT_FLAG("bpinfo", 0, &cfg.bpinfo, bpinfo),
+ OPT_FLAG("cmbsts", 0, &cfg.cmbsts, cmbsts),
+ OPT_FLAG("cmbebs", 0, &cfg.cmbebs, cmbebs),
+ OPT_FLAG("cmbswtp", 0, &cfg.cmbswtp, cmbswtp),
+ OPT_FLAG("crto", 0, &cfg.crto, crto),
+ OPT_FLAG("pmrcap", 0, &cfg.pmrcap, pmrcap),
+ OPT_FLAG("pmrsts", 0, &cfg.pmrsts, pmrsts),
+ OPT_FLAG("pmrebs", 0, &cfg.pmrebs, pmrebs),
+ OPT_FLAG("pmrswtp", 0, &cfg.pmrswtp, pmrswtp),
+ OPT_FLAG("intms", 0, &cfg.intms, intms),
+ OPT_FLAG("intmc", 0, &cfg.intmc, intmc),
+ OPT_FLAG("cc", 0, &cfg.cc, cc),
+ OPT_FLAG("csts", 0, &cfg.csts, csts),
+ OPT_FLAG("nssr", 0, &cfg.nssr, nssr),
+ OPT_FLAG("aqa", 0, &cfg.aqa, aqa),
+ OPT_FLAG("asq", 0, &cfg.asq, asq),
+ OPT_FLAG("acq", 0, &cfg.acq, acq),
+ OPT_FLAG("bprsel", 0, &cfg.bprsel, bprsel),
+ OPT_FLAG("bpmbl", 0, &cfg.bpmbl, bpmbl),
+ OPT_FLAG("cmbmsc", 0, &cfg.cmbmsc, cmbmsc),
+ OPT_FLAG("nssd", 0, &cfg.nssd, nssd),
+ OPT_FLAG("pmrctl", 0, &cfg.pmrctl, pmrctl),
+ OPT_FLAG("pmrmscl", 0, &cfg.pmrmscl, pmrmscl),
+ OPT_FLAG("pmrmscu", 0, &cfg.pmrmscu, pmrmscu));
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = validate_output_format(output_format_val, &flags);
+ if (err < 0) {
+ nvme_show_error("Invalid output format");
+ return err;
+ }
+
+ if (cfg.human_readable)
+ flags |= VERBOSE;
+
+ bar = mmap_registers(dev, false);
+ if (!bar) {
+ err = get_register_properties(dev_fd(dev), &bar, &cfg);
+ if (err)
+ return err;
+ fabrics = true;
+ }
+
+ nvme_show_init();
+
+ if (!get_register_offset(bar, fabrics, &cfg, flags)) {
+ nvme_show_error("offset required param");
+ err = -EINVAL;
+ }
+
+ nvme_show_finish();
+
+ if (fabrics)
+ free(bar);
+ else
+ munmap(bar, getpagesize());
+
+ return err;
+}
+
+static int nvme_set_single_property(int fd, int offset, uint64_t value)
+{
+ struct nvme_set_property_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .offset = offset,
+ .value = value,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ int err = nvme_set_property(&args);
+
+ if (err < 0)
+ nvme_show_error("set-property: %s", nvme_strerror(errno));
+ else if (!err)
+ printf("set-property: %#02x (%s), value: %#"PRIx64"\n", offset,
+ nvme_register_to_string(offset), value);
+ else if (err > 0)
+ nvme_show_status(err);
+
+ return err;
+}
+
+static int set_register_property(int fd, int offset, uint64_t value)
+{
+ if (!nvme_is_fabrics_reg(offset)) {
+ printf("register: %#04x (%s) not fabrics\n", offset,
+ nvme_register_to_string(offset));
+ return -EINVAL;
+ }
+
+ return nvme_set_single_property(fd, offset, value);
+}
+
+static int nvme_set_register(int fd, void *bar, int offset, uint64_t value, bool mmio32)
+{
+ if (!bar)
+ return set_register_property(fd, offset, value);
+
+ if (nvme_is_64bit_reg(offset))
+ mmio_write64(bar + offset, value, mmio32);
+ else
+ mmio_write32(bar + offset, value);
+
+ printf("set-register: %#02x (%s), value: %#"PRIx64"\n", offset,
+ nvme_register_to_string(offset), value);
+
+ return 0;
+}
+
+static inline int set_register_names_check(struct argconfig_commandline_options *opts, int offset)
+{
+ switch (offset) {
+ case NVME_REG_INTMS:
+ if (argconfig_parse_seen(opts, "intms"))
+ return -EINVAL;
+ break;
+ case NVME_REG_INTMC:
+ if (argconfig_parse_seen(opts, "intmc"))
+ return -EINVAL;
+ break;
+ case NVME_REG_CC:
+ if (argconfig_parse_seen(opts, "cc"))
+ return -EINVAL;
+ break;
+ case NVME_REG_CSTS:
+ if (argconfig_parse_seen(opts, "csts"))
+ return -EINVAL;
+ break;
+ case NVME_REG_NSSR:
+ if (argconfig_parse_seen(opts, "nssr"))
+ return -EINVAL;
+ break;
+ case NVME_REG_AQA:
+ if (argconfig_parse_seen(opts, "aqa"))
+ return -EINVAL;
+ break;
+ case NVME_REG_ASQ:
+ if (argconfig_parse_seen(opts, "asq"))
+ return -EINVAL;
+ break;
+ case NVME_REG_ACQ:
+ if (argconfig_parse_seen(opts, "acq"))
+ return -EINVAL;
+ break;
+ case NVME_REG_BPRSEL:
+ if (argconfig_parse_seen(opts, "bprsel"))
+ return -EINVAL;
+ break;
+ case NVME_REG_CMBMSC:
+ if (argconfig_parse_seen(opts, "cmbmsc"))
+ return -EINVAL;
+ break;
+ case NVME_REG_NSSD:
+ if (argconfig_parse_seen(opts, "nssd"))
+ return -EINVAL;
+ break;
+ case NVME_REG_PMRCTL:
+ if (argconfig_parse_seen(opts, "pmrctl"))
+ return -EINVAL;
+ break;
+ case NVME_REG_PMRMSCL:
+ if (argconfig_parse_seen(opts, "pmrmscl"))
+ return -EINVAL;
+ break;
+ case NVME_REG_PMRMSCU:
+ if (argconfig_parse_seen(opts, "pmrmscu"))
+ return -EINVAL;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int set_register_offset(int fd, void *bar, struct argconfig_commandline_options *opts,
+ struct set_reg_config *cfg)
+{
+ int err;
+
+ if (!argconfig_parse_seen(opts, "value")) {
+ nvme_show_error("value required param");
+ return -EINVAL;
+ }
+
+ err = set_register_names_check(opts, cfg->offset);
+ if (err) {
+ nvme_show_error("offset duplicated param");
+ return err;
+ }
+
+ err = nvme_set_register(fd, bar, cfg->offset, cfg->value, cfg->mmio32);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int set_register_names(int fd, void *bar, struct argconfig_commandline_options *opts,
+ struct set_reg_config *cfg)
+{
+ int err;
+
+ if (argconfig_parse_seen(opts, "intms")) {
+ err = nvme_set_register(fd, bar, NVME_REG_INTMS, cfg->intms, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "intmc")) {
+ err = nvme_set_register(fd, bar, NVME_REG_INTMC, cfg->intmc, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "cc")) {
+ err = nvme_set_register(fd, bar, NVME_REG_CC, cfg->cc, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "csts")) {
+ err = nvme_set_register(fd, bar, NVME_REG_CSTS, cfg->csts, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "nssr")) {
+ err = nvme_set_register(fd, bar, NVME_REG_NSSR, cfg->nssr, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "aqa")) {
+ err = nvme_set_register(fd, bar, NVME_REG_AQA, cfg->aqa, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "asq")) {
+ err = nvme_set_register(fd, bar, NVME_REG_ASQ, cfg->asq, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "acq")) {
+ err = nvme_set_register(fd, bar, NVME_REG_ACQ, cfg->acq, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "bprsel")) {
+ err = nvme_set_register(fd, bar, NVME_REG_BPRSEL, cfg->bprsel, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "cmbmsc")) {
+ err = nvme_set_register(fd, bar, NVME_REG_CMBMSC, cfg->cmbmsc, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "nssd")) {
+ err = nvme_set_register(fd, bar, NVME_REG_NSSD, cfg->nssd, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "pmrctl")) {
+ err = nvme_set_register(fd, bar, NVME_REG_PMRCTL, cfg->pmrctl, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "pmrmscl")) {
+ err = nvme_set_register(fd, bar, NVME_REG_PMRMSCL, cfg->pmrmscl, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ if (argconfig_parse_seen(opts, "pmrmscu")) {
+ err = nvme_set_register(fd, bar, NVME_REG_PMRMSCU, cfg->pmrmscu, cfg->mmio32);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int set_register(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Writes and shows the defined NVMe controller register";
+ const char *value = "the value of the register to be set";
+ const char *mmio32 = "Access 64-bit registers as 2 32-bit";
+
+ _cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
+ int err;
+
+ void *bar;
+
+ struct set_reg_config cfg = {
+ .offset = -1,
+ };
+
+ NVME_ARGS(opts,
+ OPT_UINT("offset", 'O', &cfg.offset, offset),
+ OPT_SUFFIX("value", 'V', &cfg.value, value),
+ OPT_FLAG("mmio32", 'm', &cfg.mmio32, mmio32),
+ OPT_UINT("intms", 0, &cfg.intms, intms),
+ OPT_UINT("intmc", 0, &cfg.intmc, intmc),
+ OPT_UINT("cc", 0, &cfg.cc, cc),
+ OPT_UINT("csts", 0, &cfg.csts, csts),
+ OPT_UINT("nssr", 0, &cfg.nssr, nssr),
+ OPT_UINT("aqa", 0, &cfg.aqa, aqa),
+ OPT_SUFFIX("asq", 0, &cfg.asq, asq),
+ OPT_SUFFIX("acq", 0, &cfg.acq, acq),
+ OPT_UINT("bprsel", 0, &cfg.bprsel, bprsel),
+ OPT_SUFFIX("bpmbl", 0, &cfg.bpmbl, bpmbl),
+ OPT_SUFFIX("cmbmsc", 0, &cfg.cmbmsc, cmbmsc),
+ OPT_UINT("nssd", 0, &cfg.nssd, nssd),
+ OPT_UINT("pmrctl", 0, &cfg.pmrctl, pmrctl),
+ OPT_UINT("pmrmscl", 0, &cfg.pmrmscl, pmrmscl),
+ OPT_UINT("pmrmscu", 0, &cfg.pmrmscu, pmrmscu));
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ bar = mmap_registers(dev, true);
+
+ if (argconfig_parse_seen(opts, "offset"))
+ err = set_register_offset(dev_fd(dev), bar, opts, &cfg);
+
+ if (!err)
+ err = set_register_names(dev_fd(dev), bar, opts, &cfg);
+
+ if (bar)
+ munmap(bar, getpagesize());
+
return err;
}
@@ -5377,7 +5999,7 @@ static int get_property(int argc, char **argv, struct command *cmd, struct plugi
{
const char *desc = "Reads and shows the defined NVMe controller property\n"
"for NVMe over Fabric. Property offset must be one of:\n"
- "CAP=0x0, VS=0x8, CC=0x14, CSTS=0x1c, NSSR=0x20";
+ "CAP=0x0, VS=0x8, CC=0x14, CSTS=0x1c, NSSR=0x20, NSSD=0x64, CRTO=0x68";
const char *offset = "offset of the requested property";
const char *human_readable = "show property in readable format";
@@ -5385,14 +6007,10 @@ static int get_property(int argc, char **argv, struct command *cmd, struct plugi
__u64 value;
int err;
- struct config {
- int offset;
- bool human_readable;
- };
-
- struct config cfg = {
+ struct get_reg_config cfg = {
.offset = -1,
.human_readable = false,
+ .fabrics = true,
};
NVME_ARGS(opts,
@@ -5408,20 +6026,9 @@ static int get_property(int argc, char **argv, struct command *cmd, struct plugi
return -EINVAL;
}
- struct nvme_get_property_args args = {
- .args_size = sizeof(args),
- .fd = dev_fd(dev),
- .offset = cfg.offset,
- .value = &value,
- .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
- };
- err = nvme_get_property(&args);
- if (err < 0)
- nvme_show_error("get-property: %s", nvme_strerror(errno));
- else if (!err)
+ err = nvme_get_single_property(dev_fd(dev), &cfg, &value);
+ if (!err)
nvme_show_single_property(cfg.offset, value, cfg.human_readable);
- else if (err > 0)
- nvme_show_status(err);
return err;
}
@@ -5436,12 +6043,7 @@ static int set_property(int argc, char **argv, struct command *cmd, struct plugi
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
int err;
- struct config {
- int offset;
- int value;
- };
-
- struct config cfg = {
+ struct set_reg_config cfg = {
.offset = -1,
.value = -1,
};
@@ -5463,24 +6065,7 @@ static int set_property(int argc, char **argv, struct command *cmd, struct plugi
return -EINVAL;
}
- struct nvme_set_property_args args = {
- .args_size = sizeof(args),
- .fd = dev_fd(dev),
- .offset = cfg.offset,
- .value = cfg.value,
- .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
- .result = NULL,
- };
- err = nvme_set_property(&args);
- if (err < 0)
- nvme_show_error("set-property: %s", nvme_strerror(errno));
- else if (!err)
- printf("set-property: %02x (%s), value: %#08x\n", cfg.offset,
- nvme_register_to_string(cfg.offset), cfg.value);
- else if (err > 0)
- nvme_show_status(err);
-
- return err;
+ return nvme_set_single_property(dev_fd(dev), cfg.offset, cfg.value);
}
static int format_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin)
@@ -7644,7 +8229,7 @@ static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *p
return err;
if (cfg.size) {
- sec_buf = nvme_alloc(sizeof(*sec_buf));
+ sec_buf = nvme_alloc(cfg.size);
if (!sec_buf)
return -ENOMEM;
}
@@ -8363,7 +8948,8 @@ static int gen_dhchap_key(int argc, char **argv, struct command *command, struct
"HMAC function to use for key transformation (0 = none, 1 = SHA-256, 2 = SHA-384, 3 = SHA-512).";
const char *nqn = "Host NQN to use for key transformation.";
- unsigned char *raw_secret;
+ _cleanup_free_ unsigned char *raw_secret = NULL;
+ _cleanup_free_ char *hnqn = NULL;
unsigned char key[68];
char encoded_key[128];
unsigned long crc = crc32(0L, NULL, 0);
@@ -8458,7 +9044,7 @@ static int gen_dhchap_key(int argc, char **argv, struct command *command, struct
}
if (!cfg.nqn) {
- cfg.nqn = nvmf_hostnqn_from_file();
+ cfg.nqn = hnqn = nvmf_hostnqn_from_file();
if (!cfg.nqn) {
nvme_show_error("Could not read host NQN");
return -ENOENT;
@@ -8581,12 +9167,12 @@ static int gen_tls_key(int argc, char **argv, struct command *command, struct pl
const char *subsysnqn = "Subsystem NQN for the retained key.";
const char *keyring = "Keyring for the retained key.";
const char *keytype = "Key type of the retained key.";
- const char *insert = "Insert only, do not print the retained key.";
+ const char *insert = "Insert retained key into the keyring.";
- unsigned char *raw_secret;
- char encoded_key[128];
+ _cleanup_free_ unsigned char *raw_secret = NULL;
+ _cleanup_free_ char *encoded_key = NULL;
+ _cleanup_free_ char *hnqn = NULL;
int key_len = 32;
- unsigned long crc = crc32(0L, NULL, 0);
int err;
long tls_key;
@@ -8640,7 +9226,7 @@ static int gen_tls_key(int argc, char **argv, struct command *command, struct pl
return -EINVAL;
}
if (!cfg.hostnqn) {
- cfg.hostnqn = nvmf_hostnqn_from_file();
+ cfg.hostnqn = hnqn = nvmf_hostnqn_from_file();
if (!cfg.hostnqn) {
nvme_show_error("Failed to read host NQN");
return -EINVAL;
@@ -8677,6 +9263,9 @@ static int gen_tls_key(int argc, char **argv, struct command *command, struct pl
}
}
+ encoded_key = nvme_export_tls_key(raw_secret, key_len);
+ printf("%s\n", encoded_key);
+
if (cfg.insert) {
tls_key = nvme_insert_tls_key_versioned(cfg.keyring,
cfg.keytype, cfg.hostnqn,
@@ -8688,18 +9277,7 @@ static int gen_tls_key(int argc, char **argv, struct command *command, struct pl
}
printf("Inserted TLS key %08x\n", (unsigned int)tls_key);
- return 0;
}
- crc = crc32(crc, raw_secret, key_len);
- raw_secret[key_len++] = crc & 0xff;
- raw_secret[key_len++] = (crc >> 8) & 0xff;
- raw_secret[key_len++] = (crc >> 16) & 0xff;
- raw_secret[key_len++] = (crc >> 24) & 0xff;
-
- memset(encoded_key, 0, sizeof(encoded_key));
- base64_encode(raw_secret, key_len, encoded_key);
-
- printf("NVMeTLSkey-1:%02x:%s:\n", cfg.hmac, encoded_key);
return 0;
}
@@ -8714,11 +9292,10 @@ static int check_tls_key(int argc, char **argv, struct command *command, struct
const char *keytype = "Key type of the retained key.";
const char *insert = "Insert retained key into the keyring.";
- unsigned char decoded_key[128];
- unsigned int decoded_len;
- u_int32_t crc = crc32(0L, NULL, 0);
- u_int32_t key_crc;
- int err = 0, hmac;
+ _cleanup_free_ unsigned char *decoded_key = NULL;
+ _cleanup_free_ char *hnqn = NULL;
+ int decoded_len, err = 0;
+ unsigned int hmac;
long tls_key;
struct config {
char *keyring;
@@ -8763,60 +9340,25 @@ static int check_tls_key(int argc, char **argv, struct command *command, struct
return -EINVAL;
}
- if (sscanf(cfg.keydata, "NVMeTLSkey-1:%02x:*s", &hmac) != 1) {
- nvme_show_error("Invalid key '%s'", cfg.keydata);
- return -EINVAL;
- }
- switch (hmac) {
- case 1:
- if (strlen(cfg.keydata) != 65) {
- nvme_show_error("Invalid key length %zu for SHA(256)", strlen(cfg.keydata));
- return -EINVAL;
- }
- break;
- case 2:
- if (strlen(cfg.keydata) != 89) {
- nvme_show_error("Invalid key length %zu for SHA(384)", strlen(cfg.keydata));
- return -EINVAL;
- }
- break;
- default:
- nvme_show_error("Invalid HMAC identifier %d", hmac);
- return -EINVAL;
+ decoded_key = nvme_import_tls_key(cfg.keydata, &decoded_len, &hmac);
+ if (!decoded_key) {
+ nvme_show_error("Key decoding failed, error %d\n", errno);
+ return -errno;
}
if (cfg.subsysnqn) {
- if (cfg.insert && !cfg.hostnqn) {
- cfg.hostnqn = nvmf_hostnqn_from_file();
+ if (!cfg.hostnqn) {
+ cfg.hostnqn = hnqn = nvmf_hostnqn_from_file();
if (!cfg.hostnqn) {
nvme_show_error("Failed to read host NQN");
return -EINVAL;
}
}
- } else if (cfg.insert || cfg.identity == 1) {
+ } else {
nvme_show_error("Need to specify a subsystem NQN");
return -EINVAL;
}
- err = base64_decode(cfg.keydata + 16, strlen(cfg.keydata) - 17, decoded_key);
- if (err < 0) {
- nvme_show_error("Base64 decoding failed (%s, error %d)", cfg.keydata + 16, err);
- return err;
- }
- decoded_len = err;
- decoded_len -= 4;
- if (decoded_len != 32 && decoded_len != 48) {
- nvme_show_error("Invalid key length %d", decoded_len);
- return -EINVAL;
- }
- crc = crc32(crc, decoded_key, decoded_len);
- key_crc = ((u_int32_t)decoded_key[decoded_len]) |
- ((u_int32_t)decoded_key[decoded_len + 1] << 8) |
- ((u_int32_t)decoded_key[decoded_len + 2] << 16) |
- ((u_int32_t)decoded_key[decoded_len + 3] << 24);
- if (key_crc != crc) {
- nvme_show_error("CRC mismatch (key %08x, crc %08x)", key_crc, crc);
- return -EINVAL;
- }
+
if (cfg.insert) {
tls_key = nvme_insert_tls_key_versioned(cfg.keyring,
cfg.keytype, cfg.hostnqn,
@@ -8828,7 +9370,7 @@ static int check_tls_key(int argc, char **argv, struct command *command, struct
}
printf("Inserted TLS key %08x\n", (unsigned int)tls_key);
} else {
- char *tls_id;
+ _cleanup_free_ char *tls_id = NULL;
tls_id = nvme_generate_tls_key_identity(cfg.hostnqn,
cfg.subsysnqn, cfg.identity,
@@ -8839,11 +9381,128 @@ static int check_tls_key(int argc, char **argv, struct command *command, struct
return -errno;
}
printf("%s\n", tls_id);
- free(tls_id);
}
return 0;
}
+static void __scan_tls_key(long keyring_id, long key_id,
+ char *desc, int desc_len, void *data)
+{
+ FILE *fd = data;
+ _cleanup_free_ const unsigned char *key_data = NULL;
+ _cleanup_free_ char *encoded_key = NULL;
+ int key_len;
+
+ key_data = nvme_read_key(keyring_id, key_id, &key_len);
+ if (!key_data)
+ return;
+ encoded_key = nvme_export_tls_key(key_data, key_len);
+ if (!encoded_key)
+ return;
+ fprintf(fd, "%s %s\n", desc, encoded_key);
+}
+
+static int tls_key(int argc, char **argv, struct command *command, struct plugin *plugin)
+{
+ const char *desc = "Manipulation of TLS keys.\n";
+ const char *keyring = "Keyring for the retained key.";
+ const char *keytype = "Key type of the retained key.";
+ const char *keyfile = "File for list of keys.";
+ const char *import = "Import all keys into the keyring.";
+ const char *export = "Export all keys from the keyring.";
+
+ FILE *fd;
+ int err = 0;
+
+ struct config {
+ char *keyring;
+ char *keytype;
+ char *keyfile;
+ bool import;
+ bool export;
+ };
+
+ struct config cfg = {
+ .keyring = ".nvme",
+ .keytype = "psk",
+ .keyfile = NULL,
+ .import = false,
+ .export = false,
+ };
+
+ NVME_ARGS(opts,
+ OPT_STR("keyring", 'k', &cfg.keyring, keyring),
+ OPT_STR("keytype", 't', &cfg.keytype, keytype),
+ OPT_STR("keyfile", 'f', &cfg.keyfile, keyfile),
+ OPT_FLAG("import", 'i', &cfg.import, import),
+ OPT_FLAG("export", 'e', &cfg.export, export));
+
+ err = argconfig_parse(argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (cfg.keyfile) {
+ fd = fopen(cfg.keyfile, "r");
+ if (!fd) {
+ nvme_show_error("Cannot open keyfile %s, error %d\n",
+ cfg.keyfile, errno);
+ return -errno;
+ }
+ } else
+ fd = stdin;
+
+ if (cfg.export && cfg.import) {
+ nvme_show_error("Cannot specify both --import and --export");
+ err = -EINVAL;
+ } else if (cfg.export) {
+ nvme_scan_tls_keys(cfg.keyring, __scan_tls_key, fd);
+ } else if (cfg.import) {
+ long keyring_id;
+ char tls_str[512];
+ char *tls_key;
+ unsigned char *psk;
+ unsigned int hmac;
+ int linenum = -1, key_len;
+
+ keyring_id = nvme_lookup_keyring(cfg.keyring);
+ if (!keyring_id) {
+ nvme_show_error("Invalid keyring '%s'", cfg.keyring);
+ err = -ENOKEY;
+ goto out;
+ }
+
+ while (fgets(tls_str, 512, fd)) {
+ linenum++;
+ tls_key = strrchr(tls_str, ' ');
+ if (!tls_key) {
+ nvme_show_error("Parse error in line %d",
+ linenum);
+ continue;
+ }
+ *tls_key = '\0';
+ tls_key++;
+ psk = nvme_import_tls_key(tls_key, &key_len, &hmac);
+ if (!psk) {
+ nvme_show_error("Failed to import key in line %d",
+ linenum);
+ continue;
+ }
+ nvme_update_key(keyring_id, "psk", tls_str,
+ psk, key_len);
+ free(psk);
+ }
+ } else {
+ nvme_show_error("Must specify either --import or --export");
+ err = -EINVAL;
+ }
+
+out:
+ if (cfg.keyfile)
+ fclose(fd);
+
+ return err;
+}
+
static int show_topology_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
{
const char *desc = "Show the topology\n";
@@ -8886,7 +9545,7 @@ static int show_topology_cmd(int argc, char **argv, struct command *command, str
return -EINVAL;
}
- r = nvme_create_root(stderr, map_log_level(!!(flags & VERBOSE), false));
+ r = nvme_create_root(stderr, log_level);
if (!r) {
nvme_show_error("Failed to create topology root: %s", nvme_strerror(errno));
return -errno;
@@ -8894,7 +9553,8 @@ static int show_topology_cmd(int argc, char **argv, struct command *command, str
err = nvme_scan_topology(r, NULL, NULL);
if (err < 0) {
- nvme_show_error("Failed to scan topology: %s", nvme_strerror(errno));
+ if (errno != ENOENT)
+ nvme_show_error("Failed to scan topology: %s", nvme_strerror(errno));
nvme_free_tree(r);
return err;
}