diff options
Diffstat (limited to 'src/nvme')
-rw-r--r-- | src/nvme/api-types.h | 2 | ||||
-rw-r--r-- | src/nvme/fabrics.c | 95 | ||||
-rw-r--r-- | src/nvme/fabrics.h | 13 | ||||
-rw-r--r-- | src/nvme/ioctl.c | 104 | ||||
-rw-r--r-- | src/nvme/ioctl.h | 36 | ||||
-rw-r--r-- | src/nvme/json.c | 155 | ||||
-rw-r--r-- | src/nvme/linux.c | 452 | ||||
-rw-r--r-- | src/nvme/linux.h | 37 | ||||
-rw-r--r-- | src/nvme/mi.c | 82 | ||||
-rw-r--r-- | src/nvme/mi.h | 94 | ||||
-rw-r--r-- | src/nvme/private.h | 6 | ||||
-rw-r--r-- | src/nvme/tree.c | 138 | ||||
-rw-r--r-- | src/nvme/tree.h | 45 | ||||
-rw-r--r-- | src/nvme/types.h | 444 | ||||
-rw-r--r-- | src/nvme/util.c | 6 | ||||
-rw-r--r-- | src/nvme/util.h | 2 |
16 files changed, 1386 insertions, 325 deletions
diff --git a/src/nvme/api-types.h b/src/nvme/api-types.h index 296a7b0..4cf8e8c 100644 --- a/src/nvme/api-types.h +++ b/src/nvme/api-types.h @@ -511,6 +511,7 @@ struct nvme_get_property_args { * @owpass: Overwrite pass count * @oipbp: Set to overwrite invert pattern between passes * @nodas: Set to not deallocate blocks after sanitizing + * @emvs: Set to enter media verification state */ struct nvme_sanitize_nvm_args { __u32 *result; @@ -523,6 +524,7 @@ struct nvme_sanitize_nvm_args { __u8 owpass; bool oipbp; bool nodas; + bool emvs; }; /** diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c index acf12bc..69acf04 100644 --- a/src/nvme/fabrics.c +++ b/src/nvme/fabrics.c @@ -309,6 +309,22 @@ static int __add_bool_argument(char **argstr, char *tok, bool arg) return 0; } +static int __add_hex_argument(char **argstr, char *tok, int arg, bool allow_zero) +{ + char *nstr; + + if (arg < 0 || (!arg && !allow_zero)) + return 0; + if (asprintf(&nstr, "%s,%s=0x%08x", *argstr, tok, arg) < 0) { + errno = ENOMEM; + return -1; + } + free(*argstr); + *argstr = nstr; + + return 0; +} + static int __add_int_argument(char **argstr, char *tok, int arg, bool allow_zero) { char *nstr; @@ -363,7 +379,7 @@ static int __nvmf_supported_options(nvme_root_t r); !__nvmf_supported_options(r) && (r)->options->tok; \ }) -#define add_bool_argument(o, argstr, tok, arg) \ +#define add_bool_argument(r, argstr, tok, arg) \ ({ \ int ret; \ if (nvmf_check_option(r, tok)) { \ @@ -379,7 +395,24 @@ static int __nvmf_supported_options(nvme_root_t r); ret; \ }) -#define add_int_argument(o, argstr, tok, arg, allow_zero) \ +#define add_hex_argument(r, argstr, tok, arg, allow_zero) \ +({ \ + int ret; \ + if (nvmf_check_option(r, tok)) { \ + ret = __add_hex_argument(argstr, \ + stringify(tok), \ + arg, \ + allow_zero); \ + } else { \ + nvme_msg(r, LOG_DEBUG, \ + "option \"%s\" ignored\n", \ + stringify(tok)); \ + ret = 0; \ + } \ + ret; \ +}) + +#define add_int_argument(r, argstr, tok, arg, allow_zero) \ ({ \ int ret; \ if (nvmf_check_option(r, tok)) { \ @@ -396,7 +429,7 @@ static int __nvmf_supported_options(nvme_root_t r); ret; \ }) -#define add_int_or_minus_one_argument(o, argstr, tok, arg) \ +#define add_int_or_minus_one_argument(r, argstr, tok, arg) \ ({ \ int ret; \ if (nvmf_check_option(r, tok)) { \ @@ -552,6 +585,9 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) const char *hostnqn, *hostid, *hostkey, *ctrlkey; bool discover = false, discovery_nqn = false; nvme_root_t r = h->r; + long keyring_id = 0; + long key_id = 0; + int ret; if (!transport) { nvme_msg(h->r, LOG_ERR, "need a transport (-t) argument\n"); @@ -573,19 +609,37 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) errno = ENOMEM; return -1; } + if (!strcmp(nvme_ctrl_get_subsysnqn(c), NVME_DISC_SUBSYS_NAME)) { nvme_ctrl_set_discovery_ctrl(c, true); nvme_ctrl_set_unique_discovery_ctrl(c, false); discovery_nqn = true; } + if (nvme_ctrl_is_discovery_ctrl(c)) discover = true; + hostnqn = nvme_host_get_hostnqn(h); hostid = nvme_host_get_hostid(h); hostkey = nvme_host_get_dhchap_key(h); if (!hostkey) hostkey = nvme_ctrl_get_dhchap_host_key(c); + ctrlkey = nvme_ctrl_get_dhchap_key(c); + + ret = __nvme_import_keys_from_config(h, c, &keyring_id, &key_id); + if (ret) { + errno = -ret; + return -1; + } + + if (key_id == 0) { + if (cfg->tls_configured_key) + key_id = cfg->tls_configured_key; + else + key_id = cfg->tls_key; + } + if (add_argument(r, argstr, transport, transport) || add_argument(r, argstr, traddr, nvme_ctrl_get_traddr(c)) || @@ -627,9 +681,9 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) cfg->fast_io_fail_tmo, false)) || (strcmp(transport, "loop") && add_int_argument(r, argstr, tos, cfg->tos, true)) || - add_int_argument(r, argstr, keyring, cfg->keyring, false) || + add_hex_argument(r, argstr, keyring, keyring_id, false) || (!strcmp(transport, "tcp") && - add_int_argument(r, argstr, tls_key, cfg->tls_key, false)) || + add_hex_argument(r, argstr, tls_key, key_id, false)) || add_bool_argument(r, argstr, duplicate_connect, cfg->duplicate_connect) || add_bool_argument(r, argstr, disable_sqflow, @@ -760,7 +814,7 @@ static int __nvmf_add_ctrl(nvme_root_t r, const char *argstr) (int)strcspn(argstr,"\n"), argstr); ret = write(fd, argstr, len); if (ret != len) { - nvme_msg(r, LOG_NOTICE, "Failed to write to %s: %s\n", + nvme_msg(r, LOG_INFO, "Failed to write to %s: %s\n", nvmf_dev, strerror(errno)); switch (errno) { case EALREADY: @@ -777,6 +831,8 @@ static int __nvmf_add_ctrl(nvme_root_t r, const char *argstr) return -ENVME_CONNECT_CONNREFUSED; case EADDRNOTAVAIL: return -ENVME_CONNECT_ADDRNOTAVAIL; + case ENOKEY: + return -ENVME_CONNECT_NOKEY; default: return -ENVME_CONNECT_WRITE; } @@ -864,6 +920,15 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, key = nvme_ctrl_get_dhchap_key(fc); if (key) nvme_ctrl_set_dhchap_key(c, key); + key = nvme_ctrl_get_keyring(fc); + if (key) + nvme_ctrl_set_keyring(c, key); + key = nvme_ctrl_get_tls_key_identity(fc); + if (key) + nvme_ctrl_set_tls_key_identity(c, key); + key = nvme_ctrl_get_tls_key(fc); + if (key) + nvme_ctrl_set_tls_key(c, key); } } @@ -916,6 +981,24 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, return nvme_init_ctrl(h, c, ret); } +int nvmf_connect_ctrl(nvme_ctrl_t c) +{ + _cleanup_free_ char *argstr = NULL; + int ret; + + ret = build_options(c->s->h, c, &argstr); + if (ret) + return ret; + + ret = __nvmf_add_ctrl(c->s->h->r, argstr); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; +} + nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, struct nvmf_disc_log_entry *e, const struct nvme_fabrics_config *cfg, diff --git a/src/nvme/fabrics.h b/src/nvme/fabrics.h index 8e26e9f..4da4388 100644 --- a/src/nvme/fabrics.h +++ b/src/nvme/fabrics.h @@ -37,6 +37,7 @@ * @tos: Type of service * @keyring: Keyring to store and lookup keys * @tls_key: TLS PSK for the connection + * @tls_configured_key: TLS PSK for connect command for the connection * @duplicate_connect: Allow multiple connections to the same target * @disable_sqflow: Disable controller sq flow control * @hdr_digest: Generate/verify header digest (TCP) @@ -58,6 +59,7 @@ struct nvme_fabrics_config { int tos; long keyring; long tls_key; + long tls_configured_key; bool duplicate_connect; bool disable_sqflow; @@ -222,6 +224,17 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, const struct nvme_fabrics_config *cfg); /** + * nvmf_connect_ctrl() - Connect a controller + * @c: Controller to be connected + * + * Issues a 'connect' command to the NVMe-oF controller. + * @c must be initialized and not connected to the topology. + * + * Return: 0 on success; on failure errno is set and -1 is returned. + */ +int nvmf_connect_ctrl(nvme_ctrl_t c); + +/** * nvmf_get_discovery_log() - Return the discovery log page * @c: Discovery controller to use * @logp: Pointer to the log page to be returned diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c index 9707829..ba9d5b1 100644 --- a/src/nvme/ioctl.c +++ b/src/nvme/ioctl.c @@ -625,45 +625,19 @@ int nvme_set_features_async_event(int fd, __u32 events, int nvme_set_features_auto_pst(int fd, bool apste, bool save, struct nvme_feat_auto_pst *apst, __u32 *result) { - struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = fd, - .fid = NVME_FEAT_FID_AUTO_PST, - .nsid = NVME_NSID_NONE, - .cdw11 = NVME_SET(!!apste, FEAT_APST_APSTE), - .save = save, - .uuidx = NVME_UUID_NONE, - .data = apst, - .data_len = sizeof(*apst), - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = result, - }; - - return nvme_set_features(&args); + return nvme_set_features_data(fd, NVME_FEAT_FID_AUTO_PST, + NVME_NSID_NONE, NVME_SET(!!apste, FEAT_APST_APSTE), save, + sizeof(*apst), apst, result); } int nvme_set_features_timestamp(int fd, bool save, __u64 timestamp) { __le64 t = cpu_to_le64(timestamp); struct nvme_timestamp ts = {}; - struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = fd, - .fid = NVME_FEAT_FID_TIMESTAMP, - .nsid = NVME_NSID_NONE, - .cdw11 = 0, - .cdw12 = 0, - .save = save, - .uuidx = NVME_UUID_NONE, - .cdw15 = 0, - .data_len = sizeof(ts), - .data = &ts, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - memcpy(ts.timestamp, &t, sizeof(ts.timestamp)); - return nvme_set_features(&args); + + return nvme_set_features_data(fd, NVME_FEAT_FID_TIMESTAMP, + NVME_NSID_NONE, 0, save, sizeof(ts), &ts, NULL); } int nvme_set_features_hctm(int fd, __u16 tmt2, __u16 tmt1, @@ -764,23 +738,8 @@ int nvme_set_features_lba_sts_interval(int fd, __u16 lsiri, __u16 lsipi, int nvme_set_features_host_behavior(int fd, bool save, struct nvme_feat_host_behavior *data) { - struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = fd, - .fid = NVME_FEAT_FID_HOST_BEHAVIOR, - .nsid = NVME_NSID_NONE, - .cdw11 = 0, - .cdw12 = 0, - .save = false, - .uuidx = NVME_UUID_NONE, - .cdw15 = 0, - .data_len = sizeof(*data), - .data = data, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - - return nvme_set_features(&args); + return nvme_set_features_data(fd, NVME_FEAT_FID_HOST_BEHAVIOR, + NVME_NSID_NONE, 0, false, sizeof(*data), data, NULL); } int nvme_set_features_sanitize(int fd, bool nodrm, bool save, __u32 *result) @@ -809,23 +768,9 @@ int nvme_set_features_host_id(int fd, bool exhid, bool save, __u8 *hostid) { __u32 len = exhid ? 16 : 8; __u32 value = !!exhid; - struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = fd, - .fid = NVME_FEAT_FID_HOST_ID, - .nsid = NVME_NSID_NONE, - .cdw11 = value, - .cdw12 = 0, - .save = save, - .uuidx = NVME_UUID_NONE, - .cdw15 = 0, - .data_len = len, - .data = hostid, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - return nvme_set_features(&args); + return nvme_set_features_data(fd, NVME_FEAT_FID_HOST_ID, + NVME_NSID_NONE, value, save, len, hostid, NULL); } int nvme_set_features_resv_mask(int fd, __u32 mask, bool save, __u32 *result) @@ -1696,12 +1641,25 @@ int nvme_get_property(struct nvme_get_property_args *args) int nvme_sanitize_nvm(struct nvme_sanitize_nvm_args *args) { - __u32 cdw10 = NVME_SET(args->sanact, SANITIZE_CDW10_SANACT) | - NVME_SET(!!args->ause, SANITIZE_CDW10_AUSE) | - NVME_SET(args->owpass, SANITIZE_CDW10_OWPASS) | - NVME_SET(!!args->oipbp, SANITIZE_CDW10_OIPBP) | - NVME_SET(!!args->nodas, SANITIZE_CDW10_NODAS); - __u32 cdw11 = args->ovrpat; + const size_t size_v1 = sizeof_args(struct nvme_sanitize_nvm_args, nodas, __u64); + const size_t size_v2 = sizeof_args(struct nvme_sanitize_nvm_args, emvs, __u64); + __u32 cdw10, cdw11; + + if (args->args_size < size_v1 || args->args_size > size_v2) { + errno = EINVAL; + return -1; + } + + cdw10 = NVME_SET(args->sanact, SANITIZE_CDW10_SANACT) | + NVME_SET(!!args->ause, SANITIZE_CDW10_AUSE) | + NVME_SET(args->owpass, SANITIZE_CDW10_OWPASS) | + NVME_SET(!!args->oipbp, SANITIZE_CDW10_OIPBP) | + NVME_SET(!!args->nodas, SANITIZE_CDW10_NODAS); + + if (args->args_size == size_v2) + cdw10 |= NVME_SET(!!args->emvs, SANITIZE_CDW10_EMVS); + + cdw11 = args->ovrpat; struct nvme_passthru_cmd cmd = { .opcode = nvme_admin_sanitize_nvm, @@ -1710,10 +1668,6 @@ int nvme_sanitize_nvm(struct nvme_sanitize_nvm_args *args) .timeout_ms = args->timeout, }; - if (args->args_size < sizeof(*args)) { - errno = EINVAL; - return -1; - } return nvme_submit_admin_passthru(args->fd, &cmd, args->result); } diff --git a/src/nvme/ioctl.h b/src/nvme/ioctl.h index 2ebd39c..7d407a0 100644 --- a/src/nvme/ioctl.h +++ b/src/nvme/ioctl.h @@ -285,11 +285,13 @@ enum nvme_cmd_dword_fields { NVME_SANITIZE_CDW10_OWPASS_SHIFT = 4, NVME_SANITIZE_CDW10_OIPBP_SHIFT = 8, NVME_SANITIZE_CDW10_NODAS_SHIFT = 9, + NVME_SANITIZE_CDW10_EMVS_SHIFT = 10, NVME_SANITIZE_CDW10_SANACT_MASK = 0x7, NVME_SANITIZE_CDW10_AUSE_MASK = 0x1, NVME_SANITIZE_CDW10_OWPASS_MASK = 0xf, NVME_SANITIZE_CDW10_OIPBP_MASK = 0x1, NVME_SANITIZE_CDW10_NODAS_MASK = 0x1, + NVME_SANITIZE_CDW10_EMVS_MASK = 0x1, NVME_SECURITY_NSSF_SHIFT = 0, NVME_SECURITY_SPSP0_SHIFT = 8, NVME_SECURITY_SPSP1_SHIFT = 16, @@ -2311,6 +2313,38 @@ static inline int nvme_get_log_persistent_event(int fd, } /** + * nvme_get_log_lockdown() - Retrieve lockdown Log + * @fd: File descriptor of nvme device + * @cnscp: Contents and Scope of Command and Feature Identifier Lists + * @lockdown_log: Buffer to store the lockdown log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_lockdown(int fd, + __u8 cnscp, struct nvme_lockdown_log *lockdown_log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = lockdown_log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN, + .len = sizeof(*lockdown_log), + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = cnscp, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** * nvme_set_features() - Set a feature attribute * @args: &struct nvme_set_features_args argument structure * @@ -2350,7 +2384,7 @@ static inline int nvme_set_features_data(int fd, __u8 fid, __u32 nsid, .cdw15 = 0, .data_len = data_len, .save = save, - .uuidx = 0, + .uuidx = NVME_UUID_NONE, .fid = fid, }; return nvme_set_features(&args); diff --git a/src/nvme/json.c b/src/nvme/json.c index 2c769f2..af28bd4 100644 --- a/src/nvme/json.c +++ b/src/nvme/json.c @@ -25,62 +25,10 @@ #define JSON_UPDATE_BOOL_OPTION(c, k, a, o) \ if (!strcmp(# a, k ) && !c->a) c->a = json_object_get_boolean(o); -static void json_import_nvme_tls_key(nvme_ctrl_t c, const char *keyring_str, - const char *encoded_key) -{ - struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c); - const char *hostnqn = nvme_host_get_hostnqn(c->s->h); - const char *subsysnqn = nvme_ctrl_get_subsysnqn(c); - int key_len; - unsigned int hmac; - long key_id; - _cleanup_free_ unsigned char *key_data = NULL; - - if (!hostnqn || !subsysnqn) { - nvme_msg(NULL, LOG_ERR, "Invalid NQNs (%s, %s)\n", - hostnqn, subsysnqn); - return; - } - key_data = nvme_import_tls_key(encoded_key, &key_len, &hmac); - if (!key_data) { - nvme_msg(NULL, LOG_ERR, "Failed to decode TLS Key '%s'\n", - encoded_key); - return; - } - key_id = nvme_insert_tls_key_versioned(keyring_str, "psk", - hostnqn, subsysnqn, - 0, hmac, key_data, key_len); - if (key_id <= 0) - nvme_msg(NULL, LOG_ERR, "Failed to insert TLS KEY, error %d\n", - errno); - else { - cfg->tls_key = key_id; - cfg->tls = true; - } -} - -static void json_export_nvme_tls_key(long keyring_id, long tls_key, - struct json_object *obj) -{ - int key_len; - _cleanup_free_ unsigned char *key_data = NULL; - - key_data = nvme_read_key(keyring_id, tls_key, &key_len); - if (key_data) { - _cleanup_free_ char *tls_str = NULL; - - tls_str = nvme_export_tls_key(key_data, key_len); - if (tls_str) - json_object_object_add(obj, "tls_key", - json_object_new_string(tls_str)); - } -} - static void json_update_attributes(nvme_ctrl_t c, struct json_object *ctrl_obj) { struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c); - const char *keyring_str = NULL, *encoded_key = NULL; json_object_object_foreach(ctrl_obj, key_str, val_obj) { JSON_UPDATE_INT_OPTION(cfg, key_str, @@ -120,31 +68,18 @@ static void json_update_attributes(nvme_ctrl_t c, if (!strcmp("discovery", key_str) && !nvme_ctrl_is_discovery_ctrl(c)) nvme_ctrl_set_discovery_ctrl(c, true); - /* - * The JSON configuration holds the keyring description - * which needs to be converted into the keyring serial number. - */ - if (!strcmp("keyring", key_str) && cfg->keyring == 0) { - long keyring; - - keyring_str = json_object_get_string(val_obj); - keyring = nvme_lookup_keyring(keyring_str); - if (keyring) { - cfg->keyring = keyring; - nvme_set_keyring(cfg->keyring); - } + if (!strcmp("keyring", key_str)) + nvme_ctrl_set_keyring(c, + json_object_get_string(val_obj)); + if (!strcmp("tls_key_identity", key_str)) { + nvme_ctrl_set_tls_key_identity(c, + json_object_get_string(val_obj)); + } + if (!strcmp("tls_key", key_str)) { + nvme_ctrl_set_tls_key(c, + json_object_get_string(val_obj)); } - if (!strcmp("tls_key", key_str) && cfg->tls_key == 0) - encoded_key = json_object_get_string(val_obj); } - - /* - * We might need the keyring information from the above loop, - * so we can only import the TLS key once all entries are - * processed. - */ - if (encoded_key) - json_import_nvme_tls_key(c, keyring_str, encoded_key); } static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj) @@ -181,6 +116,19 @@ static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj) attr_obj = json_object_object_get(port_obj, "dhchap_ctrl_key"); if (attr_obj) nvme_ctrl_set_dhchap_key(c, json_object_get_string(attr_obj)); + attr_obj = json_object_object_get(port_obj, "keyring"); + if (attr_obj) + nvme_ctrl_set_keyring(c, json_object_get_string(attr_obj)); + attr_obj = json_object_object_get(port_obj, "tls_key_identity"); + if (attr_obj) { + nvme_ctrl_set_tls_key_identity(c, + json_object_get_string(attr_obj)); + } + attr_obj = json_object_object_get(port_obj, "tls_key"); + if (attr_obj) { + nvme_ctrl_set_tls_key(c, + json_object_get_string(attr_obj)); + } } static void json_parse_subsys(nvme_host_t h, struct json_object *subsys_obj) @@ -368,6 +316,19 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c) if (value) json_object_object_add(port_obj, "dhchap_ctrl_key", json_object_new_string(value)); + JSON_BOOL_OPTION(cfg, port_obj, tls); + value = nvme_ctrl_get_keyring(c); + if (value) + json_object_object_add(port_obj, "keyring", + json_object_new_string(value)); + value = nvme_ctrl_get_tls_key_identity(c); + if (value) + json_object_object_add(port_obj, "tls_key_identity", + json_object_new_string(value)); + value = nvme_ctrl_get_tls_key(c); + if (value) + json_object_object_add(port_obj, "tls_key", + json_object_new_string(value)); JSON_INT_OPTION(cfg, port_obj, nr_io_queues, 0); JSON_INT_OPTION(cfg, port_obj, nr_write_queues, 0); JSON_INT_OPTION(cfg, port_obj, nr_poll_queues, 0); @@ -384,7 +345,6 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c) JSON_BOOL_OPTION(cfg, port_obj, disable_sqflow); JSON_BOOL_OPTION(cfg, port_obj, hdr_digest); JSON_BOOL_OPTION(cfg, port_obj, data_digest); - JSON_BOOL_OPTION(cfg, port_obj, tls); JSON_BOOL_OPTION(cfg, port_obj, concat); if (nvme_ctrl_is_persistent(c)) json_object_object_add(port_obj, "persistent", @@ -392,23 +352,6 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c) if (nvme_ctrl_is_discovery_ctrl(c)) json_object_object_add(port_obj, "discovery", json_object_new_boolean(true)); - /* - * Store the keyring description in the JSON config file. - */ - if (cfg->keyring) { - _cleanup_free_ char *desc = - nvme_describe_key_serial(cfg->keyring); - - if (desc) { - json_object_object_add(port_obj, "keyring", - json_object_new_string(desc)); - } - } - /* - * Store the TLS key in PSK interchange format - */ - if (cfg->tls_key) - json_export_nvme_tls_key(cfg->keyring, cfg->tls_key, port_obj); json_object_array_add(ctrl_array, port_obj); } @@ -491,11 +434,14 @@ int json_update_config(nvme_root_t r, const char *config_file) } } if (!config_file) { - ret = json_object_to_fd(1, json_root, JSON_C_TO_STRING_PRETTY); + ret = json_object_to_fd(1, json_root, + JSON_C_TO_STRING_PRETTY | + JSON_C_TO_STRING_NOSLASHESCAPE); printf("\n"); } else ret = json_object_to_file_ext(config_file, json_root, - JSON_C_TO_STRING_PRETTY); + JSON_C_TO_STRING_PRETTY | + JSON_C_TO_STRING_NOSLASHESCAPE); if (ret < 0) { nvme_msg(r, LOG_ERR, "Failed to write to %s, %s\n", config_file ? "stdout" : config_file, @@ -564,9 +510,18 @@ static void json_dump_ctrl(struct json_object *ctrl_array, nvme_ctrl_t c) if (!strcmp(transport, "tcp")) { JSON_BOOL_OPTION(cfg, ctrl_obj, tls); - if (cfg->tls_key) - json_export_nvme_tls_key(cfg->keyring, cfg->tls_key, - ctrl_obj); + value = nvme_ctrl_get_keyring(c); + if (value) + json_object_object_add(ctrl_obj, "keyring", + json_object_new_string(value)); + value = nvme_ctrl_get_tls_key_identity(c); + if (value) + json_object_object_add(ctrl_obj, "tls_key_identity", + json_object_new_string(value)); + value = nvme_ctrl_get_tls_key(c); + if (value) + json_object_object_add(ctrl_obj, "tls_key", + json_object_new_string(value)); } JSON_BOOL_OPTION(cfg, ctrl_obj, concat); if (nvme_ctrl_is_persistent(c)) @@ -640,7 +595,9 @@ int json_dump_tree(nvme_root_t r) } json_object_object_add(json_root, "hosts", host_array); - ret = json_object_to_fd(r->log.fd, json_root, JSON_C_TO_STRING_PRETTY); + ret = json_object_to_fd(r->log.fd, json_root, + JSON_C_TO_STRING_PRETTY | + JSON_C_TO_STRING_NOSLASHESCAPE); if (ret < 0) { nvme_msg(r, LOG_ERR, "Failed to write, %s\n", json_util_get_last_err()); diff --git a/src/nvme/linux.c b/src/nvme/linux.c index aff0544..e74fac2 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -525,6 +525,11 @@ char *nvme_get_path_attr(nvme_path_t p, const char *attr) } #ifndef CONFIG_OPENSSL +static unsigned char default_hmac(size_t key_len) +{ + return NVME_HMAC_ALG_NONE; +} + int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, unsigned int key_len, unsigned char *secret, unsigned char *key) @@ -552,7 +557,7 @@ static int derive_retained_key(int hmac, const char *hostnqn, } static int derive_psk_digest(const char *hostnqn, const char *subsysnqn, - int version, int hmac, + int version, int cipher, unsigned char *retained, size_t key_len, char *digest, size_t digest_len) { @@ -562,7 +567,7 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn, return -1; } -static int derive_tls_key(int version, int hmac, const char *context, +static int derive_tls_key(int version, int cipher, const char *context, unsigned char *retained, unsigned char *psk, size_t key_len) { @@ -572,20 +577,41 @@ static int derive_tls_key(int version, int hmac, const char *context, return -1; } #else /* CONFIG_OPENSSL */ -static const EVP_MD *select_hmac(int hmac, size_t *key_len) +static unsigned char default_hmac(size_t key_len) +{ + unsigned char hmac = NVME_HMAC_ALG_NONE; + + switch (key_len) { + case 32: + hmac = NVME_HMAC_ALG_SHA2_256; + break; + case 48: + hmac = NVME_HMAC_ALG_SHA2_384; + break; + case 64: + hmac = NVME_HMAC_ALG_SHA2_512; + break; + default: + break; + } + return hmac; +} + +static const EVP_MD *select_hmac(int hmac, size_t *hmac_len) { const EVP_MD *md = NULL; switch (hmac) { case NVME_HMAC_ALG_SHA2_256: md = EVP_sha256(); - *key_len = 32; + *hmac_len = 32; break; case NVME_HMAC_ALG_SHA2_384: md = EVP_sha384(); - *key_len = 48; + *hmac_len = 48; break; default: + *hmac_len = 0; break; } return md; @@ -595,18 +621,51 @@ static DEFINE_CLEANUP_FUNC( cleanup_evp_pkey_ctx, EVP_PKEY_CTX *, EVP_PKEY_CTX_free) #define _cleanup_evp_pkey_ctx_ __cleanup__(cleanup_evp_pkey_ctx) +/* + * derive_retained_key() + * + * Derive a retained key according to NVMe TCP Transport specification: + * + * The retained PSK is derived from the configured PSK. The configured PSK + * shall be destroyed as soon as the retained PSK is generated and stored. + * Each NVMe/TCP entity shall support: + * 1) transforming the configured PSK into a retained PSK before it is stored + * by the NVMe/TCP entity for repeated use with another NVMe/TCP entity; and + * 2) using the configured PSK as a retained PSK. + * + * The method to derive a retained PSK from a configured PSK shall be using + * the HKDF-Extract and HKDF-Expand-Label operations (refer to RFC 5869 and + * RFC 8446): + * 1. PRK = HKDF-Extract(0, Configured PSK); and + * 2. Retained PSK = HKDF-Expand-Label(PRK, “HostNQN”, NQNh, + * Length(Configured PSK)), + * where NQNh is the NQN of the host. + * + * 'hmac' indicates the hash function to be used to transform the configured + * PSK in a retained PSK, encoded as follows: + * + * - 0 indicates no transform (i.e., the configured PSK is used as a + * retained PSK) + * - 1 indicates SHA-256 + * - 2 indicates SHA-384 + */ static int derive_retained_key(int hmac, const char *hostnqn, - unsigned char *generated, + unsigned char *configured, unsigned char *retained, size_t key_len) { - const EVP_MD *md; _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL; uint16_t length = key_len & 0xFFFF; + const EVP_MD *md; size_t hmac_len; + if (hmac == NVME_HMAC_ALG_NONE) { + memcpy(retained, configured, key_len); + return key_len; + } + md = select_hmac(hmac, &hmac_len); - if (!md || hmac_len > key_len) { + if (!md || !hmac_len) { errno = EINVAL; return -1; } @@ -625,7 +684,7 @@ static int derive_retained_key(int hmac, const char *hostnqn, errno = ENOKEY; return -1; } - if (EVP_PKEY_CTX_set1_hkdf_key(ctx, generated, key_len) <= 0) { + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, configured, key_len) <= 0) { errno = ENOKEY; return -1; } @@ -658,17 +717,39 @@ static int derive_retained_key(int hmac, const char *hostnqn, return key_len; } -static int derive_tls_key(int version, int hmac, const char *context, - unsigned char *retained, +/* + * derive_tls_key() + * + * Derive a TLS PSK from a retained PSK. + * + * The TLS PSK shall be derived as follows from an input PSK (i.e., either + * a retained PSK or a generated PSK) and a PSK identity using the HKDF-Extract + * and HKDF-Expand-Label operations (refer to RFC 5869 and RFC 8446) where the + * hash function is the one specified by the hash specifier of the PSK identity: + * 1. PRK = HKDF-Extract(0, Input PSK); and + * 2. TLS PSK = HKDF-Expand-Label(PRK, “nvme-tls-psk”, PskIdentity, L), + * where PskIdentity is the PSK identity and L is the output size in bytes of + * the hash function (i.e., 32 for SHA-256 and 48 for SHA-384). + * + * Note that this is _not_ the hash value as specified by the configured key, + * but rather the hash function of the cipher suite associated with the + * PSK: + * - 1 indicates SHA-245 (for the TLS_AES_128_GCM_SHA256 cipher suite) + * - 2 indicates SHA-384 (for the TLS_AES_256_GCM_SHA384 cipher suite) + * + * and the value '0' is invalid here. + */ +static int derive_tls_key(int version, unsigned char cipher, + const char *context, unsigned char *retained, unsigned char *psk, size_t key_len) { - const EVP_MD *md; _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL; - size_t hmac_len; uint16_t length = key_len & 0xFFFF; + const EVP_MD *md; + size_t hmac_len; - md = select_hmac(hmac, &hmac_len); - if (!md || hmac_len > key_len) { + md = select_hmac(cipher, &hmac_len); + if (!md || !hmac_len) { errno = EINVAL; return -1; } @@ -707,9 +788,9 @@ static int derive_tls_key(int version, int hmac, const char *context, return -1; } if (version == 1) { - char hash_str[4]; + char hash_str[5]; - sprintf(hash_str, "%02d ", hmac); + sprintf(hash_str, "%02d ", cipher); if (EVP_PKEY_CTX_add1_hkdf_info(ctx, (const unsigned char *)hash_str, strlen(hash_str)) <= 0) { @@ -805,10 +886,10 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn, char *digest, size_t digest_len) { static const char hmac_seed[] = "NVMe-over-Fabrics"; - size_t hmac_len; const EVP_MD *md = select_hmac(hmac, &hmac_len); _cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx = NULL; _cleanup_free_ unsigned char *psk_ctx = NULL; + size_t hmac_len; size_t len; hmac_ctx = HMAC_CTX_new(); @@ -816,7 +897,8 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn, errno = ENOMEM; return -1; } - if (!md) { + md = select_hmac(cipher, &hmac_len); + if (!md || !hmac_len) { errno = EINVAL; return -1; } @@ -886,10 +968,10 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, unsigned char *key) { const char hmac_seed[] = "NVMe-over-Fabrics"; - OSSL_PARAM params[2], *p = params; _cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx = NULL; _cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL; _cleanup_evp_mac_ EVP_MAC *mac = NULL; + OSSL_PARAM params[2], *p = params; char *progq = NULL; char *digest; size_t len; @@ -965,19 +1047,19 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, } static int derive_psk_digest(const char *hostnqn, const char *subsysnqn, - int version, int hmac, + int version, int cipher, unsigned char *retained, size_t key_len, char *digest, size_t digest_len) { static const char hmac_seed[] = "NVMe-over-Fabrics"; - size_t hmac_len; - OSSL_PARAM params[2], *p = params; _cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx = NULL; _cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL; + _cleanup_free_ unsigned char *psk_ctx = NULL; _cleanup_evp_mac_ EVP_MAC *mac = NULL; + OSSL_PARAM params[2], *p = params; + size_t hmac_len; char *progq = NULL; char *dig = NULL; - _cleanup_free_ unsigned char *psk_ctx = NULL; size_t len; lib_ctx = OSSL_LIB_CTX_new(); @@ -996,7 +1078,7 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn, errno = ENOMEM; return -1; } - switch (hmac) { + switch (cipher) { case NVME_HMAC_ALG_SHA2_256: dig = OSSL_DIGEST_NAME_SHA2_256; break; @@ -1069,12 +1151,12 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn, #endif /* !CONFIG_OPENSSL_3 */ static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, - int version, int hmac, char *digest, + int version, int cipher, char *digest, char *identity) { if (version == 0) { sprintf(identity, "NVMe%01dR%02d %s %s", - version, hmac, hostnqn, subsysnqn); + version, cipher, hostnqn, subsysnqn); return strlen(identity); } if (version > 1) { @@ -1083,7 +1165,7 @@ static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, } sprintf(identity, "NVMe%01dR%02d %s %s %s", - version, hmac, hostnqn, subsysnqn, digest); + version, cipher, hostnqn, subsysnqn, digest); return strlen(identity); } @@ -1095,6 +1177,7 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, _cleanup_free_ unsigned char *retained = NULL; _cleanup_free_ char *digest = NULL; char *context = identity; + unsigned char cipher; int ret = -1; if (!hostnqn || !subsysnqn || !identity || !psk) { @@ -1111,6 +1194,11 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, if (ret < 0) return ret; + if (hmac == NVME_HMAC_ALG_NONE) + cipher = default_hmac(key_len); + else + cipher = hmac; + if (version == 1) { size_t digest_len = 2 * key_len; @@ -1119,25 +1207,25 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, errno = ENOMEM; return -1; } - ret = derive_psk_digest(hostnqn, subsysnqn, version, hmac, + ret = derive_psk_digest(hostnqn, subsysnqn, version, cipher, retained, key_len, digest, digest_len); - if (ret) + if (ret < 0) return ret; context = digest; } - ret = gen_tls_identity(hostnqn, subsysnqn, version, hmac, + ret = gen_tls_identity(hostnqn, subsysnqn, version, cipher, digest, identity); if (ret < 0) return ret; - return derive_tls_key(version, hmac, context, retained, + return derive_tls_key(version, cipher, context, retained, psk, key_len); } -static size_t nvme_identity_len(int hmac, int version, const char *hostnqn, - const char *subsysnqn) +static ssize_t nvme_identity_len(int hmac, int version, const char *hostnqn, + const char *subsysnqn) { - size_t len; + ssize_t len; if (!hostnqn || !subsysnqn) { errno = EINVAL; @@ -1160,9 +1248,9 @@ char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn, int version, int hmac, unsigned char *configured_key, int key_len) { - char *identity; - size_t identity_len; _cleanup_free_ unsigned char *psk = NULL; + char *identity; + ssize_t identity_len; int ret = -1; identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); @@ -1203,11 +1291,21 @@ long nvme_lookup_keyring(const char *keyring) char *nvme_describe_key_serial(long key_id) { - char *desc; + _cleanup_free_ char *str = NULL; + char *last; + + if (keyctl_describe_alloc(key_id, &str) < 0) + return NULL; + + last = strrchr(str, ';'); + if (!last) + return NULL; - if (keyctl_describe_alloc(key_id, &desc) < 0) - desc = NULL; - return desc; + last++; + if (strlen(last) == 0) + return NULL; + + return strdup(last); } long nvme_lookup_key(const char *type, const char *identity) @@ -1337,27 +1435,17 @@ int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb, return ret; } -long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, - const char *hostnqn, const char *subsysnqn, - int version, int hmac, - unsigned char *configured_key, int key_len) +static long __nvme_insert_tls_key_versioned(key_serial_t keyring_id, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) { - key_serial_t keyring_id, key; - _cleanup_free_ char *identity = NULL; - size_t identity_len; _cleanup_free_ unsigned char *psk = NULL; + _cleanup_free_ char *identity = NULL; + ssize_t identity_len; + key_serial_t key; int ret; - keyring_id = nvme_lookup_keyring(keyring); - if (keyring_id == 0) { - errno = ENOKEY; - return 0; - } - - ret = nvme_set_keyring(keyring_id); - if (ret < 0) - return 0; - identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); if (identity_len < 0) return 0; @@ -1387,6 +1475,29 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, return key; } +long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) +{ + key_serial_t keyring_id; + int ret; + + keyring_id = nvme_lookup_keyring(keyring); + if (keyring_id == 0) { + errno = ENOKEY; + return 0; + } + + ret = nvme_set_keyring(keyring_id); + if (ret < 0) + return 0; + return __nvme_insert_tls_key_versioned(keyring_id, key_type, + hostnqn, subsysnqn, + version, hmac, + configured_key, key_len); +} + long nvme_revoke_tls_key(const char *keyring, const char *key_type, const char *identity) { @@ -1405,6 +1516,94 @@ long nvme_revoke_tls_key(const char *keyring, const char *key_type, return keyctl_revoke(key); } + +static int __nvme_insert_tls_key(long keyring_id, + const char *hostnqn, const char *subsysnqn, + const char *identity, const char *key) +{ + _cleanup_free_ unsigned char *key_data = NULL; + unsigned char version; + unsigned char hmac; + size_t key_len; + + key_data = nvme_import_tls_key_versioned(key, &version, + &hmac, &key_len); + if (!key_data) + return -EINVAL; + + if (hmac == NVME_HMAC_ALG_NONE || !identity) { + /* + * This is a configured key (hmac 0) or we don't know the + * identity and so the assumtion is it is also a + * configured key. Derive a new key and load the newly + * created key into the keystore. + */ + return __nvme_insert_tls_key_versioned(keyring_id, "psk", + hostnqn, subsysnqn, + version, hmac, + key_data, key_len); + } + + return nvme_update_key(keyring_id, "psk", identity, + key_data, key_len); +} + +int __nvme_import_keys_from_config(nvme_host_t h, nvme_ctrl_t c, + long *keyring_id, long *key_id) +{ + const char *hostnqn = nvme_host_get_hostnqn(h); + const char *subsysnqn = nvme_ctrl_get_subsysnqn(c); + const char *keyring, *key, *identity; + long kr_id, id = 0; + + if (!hostnqn || !subsysnqn) { + nvme_msg(h->r, LOG_ERR, "Invalid NQNs (%s, %s)\n", + hostnqn, subsysnqn); + return -EINVAL; + } + + keyring = nvme_ctrl_get_keyring(c); + if (keyring) + kr_id = nvme_lookup_keyring(keyring); + else + kr_id = c->cfg.keyring; + + /* + * Fallback to the default keyring. Note this will also add the + * keyring to connect command line and to the JSON config output. + * That means we are explicitly selecting the keyring. + */ + if (!kr_id) + kr_id = nvme_lookup_keyring(".nvme"); + + if (nvme_set_keyring(kr_id) < 0) { + nvme_msg(h->r, LOG_ERR, "Failed to set keyring\n"); + return -errno; + } + + key = nvme_ctrl_get_tls_key(c); + if (!key) + return 0; + + identity = nvme_ctrl_get_tls_key_identity(c); + if (identity) + id = nvme_lookup_key("psk", identity); + + if (!id) + id = __nvme_insert_tls_key(kr_id, hostnqn, + subsysnqn, identity, key); + + if (id <= 0) { + nvme_msg(h->r, LOG_ERR, "Failed to insert TLS KEY, error %d\n", + errno); + return -errno; + } + + *keyring_id = kr_id; + *key_id = id; + + return 0; +} #else long nvme_lookup_keyring(const char *keyring) { @@ -1478,6 +1677,12 @@ long nvme_revoke_tls_key(const char *keyring, const char *key_type, errno = ENOTSUP; return -1; } + +int __nvme_import_keys_from_config(nvme_host_t h, nvme_ctrl_t c, + long *keyring_id, long *key_id) +{ + return -ENOTSUP; +} #endif long nvme_insert_tls_key(const char *keyring, const char *key_type, @@ -1489,21 +1694,44 @@ long nvme_insert_tls_key(const char *keyring, const char *key_type, configured_key, key_len); } -char *nvme_export_tls_key(const unsigned char *key_data, int key_len) +/* + * PSK Interchange Format + * NVMeTLSkey-<v>:<xx>:<s>: + * + * x: version as one ASCII char + * yy: hmac encoded as two ASCII chars + * 00: no transform ('configured PSK') + * 01: SHA-256 + * 02: SHA-384 + * s: 32 or 48 bytes binary followed by a CRC-32 of the configured PSK + * (4 bytes) encoded as base64 + */ +char *nvme_export_tls_key_versioned(unsigned char version, unsigned char hmac, + const unsigned char *key_data, + size_t key_len) { - unsigned char raw_secret[52]; - char *encoded_key; unsigned int raw_len, encoded_len, len; unsigned long crc = crc32(0L, NULL, 0); + unsigned char raw_secret[52]; + char *encoded_key; - if (key_len == 32) { - raw_len = 32; - } else if (key_len == 48) { - raw_len = 48; - } else { - errno = EINVAL; - return NULL; + switch (hmac) { + case NVME_HMAC_ALG_NONE: + if (key_len != 32 && key_len != 48) + goto err_inval; + break; + case NVME_HMAC_ALG_SHA2_256: + if (key_len != 32) + goto err_inval; + break; + case NVME_HMAC_ALG_SHA2_384: + if (key_len != 48) + goto err_inval; + break; + default: + goto err_inval; } + raw_len = key_len; memcpy(raw_secret, key_data, raw_len); crc = crc32(crc, raw_secret, raw_len); @@ -1519,48 +1747,75 @@ char *nvme_export_tls_key(const unsigned char *key_data, int key_len) return NULL; } memset(encoded_key, 0, encoded_len); - len = sprintf(encoded_key, "NVMeTLSkey-1:%02x:", - key_len == 32 ? 1 : 2); + len = sprintf(encoded_key, "NVMeTLSkey-%x:%02x:", version, hmac); len += base64_encode(raw_secret, raw_len, encoded_key + len); encoded_key[len++] = ':'; encoded_key[len++] = '\0'; return encoded_key; + +err_inval: + errno = EINVAL; + return NULL; + } -unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len, - unsigned int *hmac) +char *nvme_export_tls_key(const unsigned char *key_data, int key_len) +{ + unsigned char hmac; + + if (key_len == 32) + hmac = NVME_HMAC_ALG_SHA2_256; + else + hmac = NVME_HMAC_ALG_SHA2_384; + + return nvme_export_tls_key_versioned(1, hmac, key_data, key_len); +} + +unsigned char *nvme_import_tls_key_versioned(const char *encoded_key, + unsigned char *version, + unsigned char *hmac, + size_t *key_len) { unsigned char decoded_key[128], *key_data; unsigned int crc = crc32(0L, NULL, 0); unsigned int key_crc; - int err, decoded_len; + int err, _version, _hmac, decoded_len; + size_t len; - if (sscanf(encoded_key, "NVMeTLSkey-1:%02x:*s", &err) != 1) { + if (sscanf(encoded_key, "NVMeTLSkey-%d:%02x:*s", + &_version, &_hmac) != 2) { errno = EINVAL; return NULL; } - switch (err) { - case 1: - if (strlen(encoded_key) != 65) { - errno = EINVAL; - return NULL; - } + + if (_version != 1) { + errno = EINVAL; + return NULL; + } + *version = _version; + + len = strlen(encoded_key); + switch (_hmac) { + case NVME_HMAC_ALG_NONE: + if (len != 65 && len != 89) + goto err_inval; break; - case 2: - if (strlen(encoded_key) != 89) { - errno = EINVAL; - return NULL; - } + case NVME_HMAC_ALG_SHA2_256: + if (len != 65) + goto err_inval; + break; + case NVME_HMAC_ALG_SHA2_384: + if (len != 89) + goto err_inval; break; default: errno = EINVAL; return NULL; } + *hmac = _hmac; - *hmac = err; - err = base64_decode(encoded_key + 16, strlen(encoded_key) - 17, - decoded_key); + err = base64_decode(encoded_key + 16, len - 17, decoded_key); if (err < 0) { errno = ENOKEY; return NULL; @@ -1592,4 +1847,25 @@ unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len, *key_len = decoded_len; return key_data; + +err_inval: + errno = EINVAL; + return NULL; +} + +unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len, + unsigned int *hmac) +{ + unsigned char version, _hmac; + unsigned char *psk; + size_t len; + + psk = nvme_import_tls_key_versioned(encoded_key, &version, + &_hmac, &len); + if (!psk) + return NULL; + + *hmac = _hmac; + *key_len = len; + return psk; } diff --git a/src/nvme/linux.h b/src/nvme/linux.h index 8e5e8ad..5dbc092 100644 --- a/src/nvme/linux.h +++ b/src/nvme/linux.h @@ -437,6 +437,25 @@ long nvme_revoke_tls_key(const char *keyring, const char *key_type, char *nvme_export_tls_key(const unsigned char *key_data, int key_len); /** + * nvme_export_tls_key_versioned() - Export a TLS pre-shared key + * @version: Indicated the representation of the TLS PSK + * @hmac: HMAC algorithm used to transfor the configured PSK + * in a retained PSK + * @key_data: Raw data of the key + * @key_len: Length of @key_data + * + * Returns @key_data in the PSK Interchange format as defined in section + * 3.6.1.5 of the NVMe TCP Transport specification. + * + * Return: The string containing the TLS identity or NULL with errno set + * on error. It is the responsibility of the caller to free the returned + * string. + */ +char *nvme_export_tls_key_versioned(unsigned char version, unsigned char hmac, + const unsigned char *key_data, + size_t key_len); + +/** * nvme_import_tls_key() - Import a TLS key * @encoded_key: TLS key in PSK interchange format * @key_len: Length of the resulting key data @@ -452,6 +471,24 @@ unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len, unsigned int *hmac); /** + * nvme_import_tls_key_versioned() - Import a TLS key + * @encoded_key: TLS key in PSK interchange format + * @version: Indicated the representation of the TLS PSK + * @hmac: HMAC algorithm used to transfor the configured + * PSK in a retained PSK + * @key_len: Length of the resulting key data + * + * Imports @key_data in the PSK Interchange format as defined in section + * 3.6.1.5 of the NVMe TCP Transport specification. + * + * Return: The raw data of the PSK or NULL with errno set on error. It is + * the responsibility of the caller to free the returned string. + */ +unsigned char *nvme_import_tls_key_versioned(const char *encoded_key, + unsigned char *version, + unsigned char *hmac, + size_t *key_len); +/** * nvme_submit_passthru - Low level ioctl wrapper for passthru commands * @fd: File descriptor of the nvme device * @ioctl_cmd: IOCTL command id diff --git a/src/nvme/mi.c b/src/nvme/mi.c index d98c74a..e9f39a8 100644 --- a/src/nvme/mi.c +++ b/src/nvme/mi.c @@ -529,6 +529,31 @@ static void nvme_mi_admin_init_resp(struct nvme_mi_resp *resp, resp->hdr_len = sizeof(*hdr); } +static void nvme_mi_control_init_req(struct nvme_mi_req *req, + struct nvme_mi_control_req *control_req, + __u8 opcode, __u16 cpsp) +{ + memset(req, 0, sizeof(*req)); + memset(control_req, 0, sizeof(*control_req)); + + control_req->hdr.type = NVME_MI_MSGTYPE_NVME; + control_req->hdr.nmp = (NVME_MI_ROR_REQ << 7) | + (NVME_MI_MT_CONTROL << 3); /* we always use command slot 0 */ + control_req->opcode = opcode; + control_req->cpsp = cpu_to_le16(cpsp); + + req->hdr = &control_req->hdr; + req->hdr_len = sizeof(*control_req); +} + +static void nvme_mi_control_init_resp(struct nvme_mi_resp *resp, + struct nvme_mi_control_resp *control_resp) +{ + memset(resp, 0, sizeof(*resp)); + resp->hdr = &control_resp->hdr; + resp->hdr_len = sizeof(*control_resp); +} + static int nvme_mi_admin_parse_status(struct nvme_mi_resp *resp, __u32 *result) { struct nvme_mi_admin_resp_hdr *admin_hdr; @@ -580,6 +605,26 @@ static int nvme_mi_admin_parse_status(struct nvme_mi_resp *resp, __u32 *result) return nvme_status; } +static int nvme_mi_control_parse_status(struct nvme_mi_resp *resp, __u16 *cpsr) +{ + struct nvme_mi_control_resp *control_resp; + + if (resp->hdr_len < sizeof(*control_resp)) { + errno = -EPROTO; + return -1; + } + control_resp = (struct nvme_mi_control_resp *)resp->hdr; + + if (control_resp->status) + return control_resp->status | + (NVME_STATUS_TYPE_MI << NVME_STATUS_TYPE_SHIFT); + + if (cpsr) + *cpsr = le16_to_cpu(control_resp->cpsr); + + return control_resp->status; +} + int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, struct nvme_mi_admin_req_hdr *admin_req, size_t req_data_size, @@ -589,6 +634,7 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, { struct nvme_mi_resp resp; struct nvme_mi_req req; + __u32 dlen, doff; int rc; /* length/offset checks. The common _submit() API will do further @@ -647,8 +693,17 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, /* limit the response size, specify offset */ admin_req->flags = 0x3; - admin_req->dlen = cpu_to_le32(resp.data_len & 0xffffffff); - admin_req->doff = cpu_to_le32(resp_data_offset & 0xffffffff); + + /* dlen and doff have different interpretations depending on the data direction */ + if (req_data_size) { + dlen = req_data_size & 0xffffffff; + doff = 0; + } else { + dlen = *resp_data_size & 0xffffffff; + doff = resp_data_offset & 0xffffffff; + } + admin_req->dlen = cpu_to_le32(dlen); + admin_req->doff = cpu_to_le32(doff); rc = nvme_mi_submit(ctrl->ep, &req, &resp); if (rc) @@ -812,6 +867,29 @@ int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, return 0; } +int nvme_mi_control(nvme_mi_ep_t ep, __u8 opcode, + __u16 cpsp, __u16 *result_cpsr) +{ + struct nvme_mi_control_resp control_resp; + struct nvme_mi_control_req control_req; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc = 0; + + nvme_mi_control_init_req(&req, &control_req, opcode, cpsp); + nvme_mi_control_init_resp(&resp, &control_resp); + + rc = nvme_mi_submit(ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_control_parse_status(&resp, result_cpsr); + if (rc) + return rc; + + return 0; +} + /* retrieves a MCTP-messsage-sized chunk of log page data. offset and len are * specified within the args->data area. The `offset` parameter is a relative * offset to the args->lpo ! diff --git a/src/nvme/mi.h b/src/nvme/mi.h index ae32a90..3ea2b22 100644 --- a/src/nvme/mi.h +++ b/src/nvme/mi.h @@ -368,6 +368,52 @@ struct nvme_mi_admin_resp_hdr { } __attribute__((packed)); /** + * enum nvme_mi_control_opcode - Operation code for Control Primitives. + * @nvme_mi_control_opcode_pause: Suspend response transmission/timeout + * @nvme_mi_control_opcode_resume: Resume from a paused condition + * @nvme_mi_control_opcode_abort: Re-initialize a Command Slot to the Idle state + * @nvme_mi_control_opcode_get_state: Get the state of a Command Slot + * @nvme_mi_control_opcode_replay: Retransmit the Response Message + */ +enum nvme_mi_control_opcode { + nvme_mi_control_opcode_pause = 0x00, + nvme_mi_control_opcode_resume = 0x01, + nvme_mi_control_opcode_abort = 0x02, + nvme_mi_control_opcode_get_state = 0x03, + nvme_mi_control_opcode_replay = 0x04, +}; + +/** + * struct nvme_mi_control_req - The Control Primitive request. + * @hdr: Generic MI message header + * @opcode: Control Primitive Opcodes (using &enum nvme_mi_control_opcode) + * @tag: flag - Opaque value passed from request to response + * @cpsp: Control Primitive Specific Parameter + * + */ +struct nvme_mi_control_req { + struct nvme_mi_msg_hdr hdr; + __u8 opcode; + __u8 tag; + __le16 cpsp; +} __attribute((packed)); + +/** struct nvme_mi_control_resp - The Control Primitive response. + * @hdr: Generic MI message header + * @status: Generic response code, non-zero on failure + * @tag: flag - Opaque value passed from request to response + * @cpsr: Control Primitive Specific Response + * + */ + +struct nvme_mi_control_resp { + struct nvme_mi_msg_hdr hdr; + __u8 status; + __u8 tag; + __le16 cpsr; +} __attribute((packed)); + +/** * nvme_mi_status_to_string() - return a string representation of the MI * status. * @status: MI response status @@ -1076,6 +1122,24 @@ static inline int nvme_mi_admin_identify(nvme_mi_ctrl_t ctrl, } /** + * nvme_mi_control() - Perform a Control Primitive command + * @ep: endpoint for MI communication + * @opcode: Control Primitive opcode (using &enum nvme_mi_control_opcode) + * @cpsp: Control Primitive Specific Parameter + * @result_cpsr: Optional field to return the result from the CPSR field + * + * Perform a Control Primitive command, using the opcode specified in @opcode + * Stores the result from the CPSR field in @result_cpsr if set. + * + * Return: 0 on success, non-zero on failure + * + * See: &enum nvme_mi_control_opcode + * + */ +int nvme_mi_control(nvme_mi_ep_t ep, __u8 opcode, + __u16 cpsp, __u16 *result_cpsr); + +/** * nvme_mi_admin_identify_cns_nsid() - Perform an Admin identify command using * specific CNS/NSID parameters. * @ctrl: Controller to process identify command @@ -2369,6 +2433,36 @@ static inline int nvme_mi_admin_get_log_persistent_event(nvme_mi_ctrl_t ctrl, } /** + * nvme_mi_admin_get_log_lockdown() - Retrieve lockdown Log + * @ctrl: Controller to query + * @cnscp: Contents and Scope of Command and Feature Identifier Lists + * @lockdown_log: Buffer to store the lockdown log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_lockdown(nvme_mi_ctrl_t ctrl, + __u8 cnscp, struct nvme_lockdown_log *lockdown_log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = lockdown_log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN, + .len = sizeof(*lockdown_log), + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = cnscp, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** * nvme_mi_admin_security_send() - Perform a Security Send command on a * controller. * @ctrl: Controller to send command to diff --git a/src/nvme/private.h b/src/nvme/private.h index 3fa5aca..48ddedc 100644 --- a/src/nvme/private.h +++ b/src/nvme/private.h @@ -85,6 +85,9 @@ struct nvme_ctrl { char *trsvcid; char *dhchap_key; char *dhchap_ctrl_key; + char *keyring; + char *tls_key_identity; + char *tls_key; char *cntrltype; char *cntlid; char *dctype; @@ -297,4 +300,7 @@ void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops); #define SECTOR_SIZE 512 #define SECTOR_SHIFT 9 +int __nvme_import_keys_from_config(nvme_host_t h, nvme_ctrl_t c, + long *keyring_id, long *key_id); + #endif /* _LIBNVME_PRIVATE_H */ diff --git a/src/nvme/tree.c b/src/nvme/tree.c index 3722461..7fc2013 100644 --- a/src/nvme/tree.c +++ b/src/nvme/tree.c @@ -1151,6 +1151,51 @@ void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key) c->dhchap_ctrl_key = strdup(key); } +const char *nvme_ctrl_get_keyring(nvme_ctrl_t c) +{ + return c->keyring; +} + +void nvme_ctrl_set_keyring(nvme_ctrl_t c, const char *keyring) +{ + if (c->keyring) { + free(c->keyring); + c->keyring = NULL; + } + if (keyring) + c->keyring = strdup(keyring); +} + +const char *nvme_ctrl_get_tls_key_identity(nvme_ctrl_t c) +{ + return c->tls_key_identity; +} + +void nvme_ctrl_set_tls_key_identity(nvme_ctrl_t c, const char *identity) +{ + if (c->tls_key_identity) { + free(c->tls_key_identity); + c->tls_key_identity = NULL; + } + if (identity) + c->tls_key_identity = strdup(identity); +} + +const char *nvme_ctrl_get_tls_key(nvme_ctrl_t c) +{ + return c->tls_key; +} + +void nvme_ctrl_set_tls_key(nvme_ctrl_t c, const char *key) +{ + if (c->tls_key) { + free(c->tls_key); + c->tls_key = NULL; + } + if (key) + c->tls_key = strdup(key); +} + void nvme_ctrl_set_discovered(nvme_ctrl_t c, bool discovered) { c->discovered = discovered; @@ -1232,6 +1277,9 @@ void nvme_deconfigure_ctrl(nvme_ctrl_t c) FREE_CTRL_ATTR(c->sqsize); FREE_CTRL_ATTR(c->dhchap_key); FREE_CTRL_ATTR(c->dhchap_ctrl_key); + FREE_CTRL_ATTR(c->keyring); + FREE_CTRL_ATTR(c->tls_key_identity); + FREE_CTRL_ATTR(c->tls_key); FREE_CTRL_ATTR(c->address); FREE_CTRL_ATTR(c->dctype); FREE_CTRL_ATTR(c->cntrltype); @@ -1883,11 +1931,73 @@ static char *nvme_ctrl_lookup_phy_slot(nvme_root_t r, const char *address) return NULL; } +static void nvme_read_sysfs_dhchap(nvme_root_t r, nvme_ctrl_t c) +{ + char *host_key, *ctrl_key; + + host_key = nvme_get_ctrl_attr(c, "dhchap_secret"); + if (host_key && c->s && c->s->h && c->s->h->dhchap_key && + (!strcmp(c->s->h->dhchap_key, host_key) || + !strcmp("none", host_key))) { + free(host_key); + host_key = NULL; + } + if (host_key) { + nvme_ctrl_set_dhchap_host_key(c, NULL); + c->dhchap_key = host_key; + } + + ctrl_key = nvme_get_ctrl_attr(c, "dhchap_ctrl_secret"); + if (ctrl_key && !strcmp(ctrl_key, "none")) { + free(ctrl_key); + ctrl_key = NULL; + } + if (ctrl_key) { + nvme_ctrl_set_dhchap_key(c, NULL); + c->dhchap_ctrl_key = ctrl_key; + } +} + +static void nvme_read_sysfs_tls(nvme_root_t r, nvme_ctrl_t c) +{ + char *endptr; + long key_id; + char *key, *keyring; + + key = nvme_get_ctrl_attr(c, "tls_key"); + if (!key) { + /* tls_key is only present if --tls has been used. */ + return; + } + c->cfg.tls = true; + + keyring = nvme_get_ctrl_attr(c, "tls_keyring"); + nvme_ctrl_set_keyring(c, keyring); + free(keyring); + + /* the sysfs entry is not prefixing the id but it's in hex */ + key_id = strtol(key, &endptr, 16); + if (endptr != key) + c->cfg.tls_key = key_id; + + free(key); + + key = nvme_get_ctrl_attr(c, "tls_configured_key"); + if (!key) + return; + + /* the sysfs entry is not prefixing the id but it's in hex */ + key_id = strtol(key, &endptr, 16); + if (endptr != key) + c->cfg.tls_configured_key = key_id; + + free(key); +} + static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, const char *name) { DIR *d; - char *host_key, *tls_psk; d = opendir(path); if (!d) { @@ -1908,34 +2018,12 @@ static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, c->queue_count = nvme_get_ctrl_attr(c, "queue_count"); c->serial = nvme_get_ctrl_attr(c, "serial"); c->sqsize = nvme_get_ctrl_attr(c, "sqsize"); - host_key = nvme_get_ctrl_attr(c, "dhchap_secret"); - if (host_key && c->s && c->s->h && c->s->h->dhchap_key && - (!strcmp(c->s->h->dhchap_key, host_key) || - !strcmp("none", host_key))) { - free(host_key); - host_key = NULL; - } - if (host_key) - c->dhchap_key = host_key; - c->dhchap_ctrl_key = nvme_get_ctrl_attr(c, "dhchap_ctrl_secret"); - if (c->dhchap_ctrl_key && !strcmp(c->dhchap_ctrl_key, "none")) { - free(c->dhchap_ctrl_key); - c->dhchap_ctrl_key = NULL; - } - tls_psk = nvme_get_ctrl_attr(c, "tls_key"); - if (tls_psk) { - char *endptr; - long key_id = strtol(tls_psk, &endptr, 16); - - if (endptr != tls_psk) { - c->cfg.tls_key = key_id; - c->cfg.tls = true; - } - } c->cntrltype = nvme_get_ctrl_attr(c, "cntrltype"); c->cntlid = nvme_get_ctrl_attr(c, "cntlid"); c->dctype = nvme_get_ctrl_attr(c, "dctype"); c->phy_slot = nvme_ctrl_lookup_phy_slot(r, c->address); + nvme_read_sysfs_dhchap(r, c); + nvme_read_sysfs_tls(r, c); errno = 0; /* cleanup after nvme_get_ctrl_attr() */ return 0; diff --git a/src/nvme/tree.h b/src/nvme/tree.h index 1b583cd..818e17b 100644 --- a/src/nvme/tree.h +++ b/src/nvme/tree.h @@ -1099,6 +1099,51 @@ const char *nvme_ctrl_get_dhchap_key(nvme_ctrl_t c); void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key); /** + * nvme_ctrl_get_keyring() - Return keyring + * @c: Controller to be used for the lookup + * + * Return: Keyring or NULL if not set + */ +const char *nvme_ctrl_get_keyring(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_keyring() - Set keyring + * @c: Controller for which the keyring should be set + * @keyring: Keyring name + */ +void nvme_ctrl_set_keyring(nvme_ctrl_t c, const char *keyring); + +/** + * nvme_ctrl_get_tls_key_identity() - Return Derive TLS Identity + * @c: Controller to be used for the lookup + * + * Return: Derive TLS Identity or NULL if not set + */ +const char *nvme_ctrl_get_tls_key_identity(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_tls_key_identity() - Set Derive TLS Identity + * @c: Controller for which the key should be set + * @identity: Derive TLS identity or NULL to clear existing key + */ +void nvme_ctrl_set_tls_key_identity(nvme_ctrl_t c, const char *identity); + +/** + * nvme_ctrl_get_tls_key() - Return Derive TLS PSK + * @c: Controller to be used for the lookup + * + * Return: Key in PSK interchange format or NULL if not set + */ +const char *nvme_ctrl_get_tls_key(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_tls_key() - Set Derive TLS PSK + * @c: Controller for which the key should be set + * @key: Key in interchange format or NULL to clear existing key + */ +void nvme_ctrl_set_tls_key(nvme_ctrl_t c, const char *key); + +/** * nvme_ctrl_get_config() - Fabrics configuration of a controller * @c: Controller instance * diff --git a/src/nvme/types.h b/src/nvme/types.h index 5fa969d..7d143d3 100644 --- a/src/nvme/types.h +++ b/src/nvme/types.h @@ -46,6 +46,24 @@ (((__u32)(value) & NVME_##name##_MASK) << NVME_##name##_SHIFT) /** + * NVME_CHECK() - check value to compare field value + * @value: The value to be checked + * @name: The name of the sub-field within an nvme value + * @check: The sub-field value to check + * + * Returns: The result of compare the value and the sub-field value + */ +#define NVME_CHECK(value, name, check) ((value) == NVME_##name##_##check) + +/** + * NVME_VAL() - get mask value shifted + * @name: The name of the sub-field within an nvme value + * + * Returns: The mask value shifted + */ +#define NVME_VAL(name) (NVME_##name##_MASK << NVME_##name##_SHIFT) + +/** * enum nvme_constants - A place to stash various constant nvme values * @NVME_NSID_ALL: A broadcast value that is used to specify all * namespaces @@ -1293,7 +1311,8 @@ struct nvme_id_psd { * @rsvd358: Reserved * @megcap: Max Endurance Group Capacity indicates the maximum capacity * of a single Endurance Group. - * @rsvd384: Reserved + * @tmpthha: Temperature Threshold Hysteresis Attributes + * @rsvd385: Reserved * @sqes: Submission Queue Entry Size, see &enum nvme_id_ctrl_sqes. * @cqes: Completion Queue Entry Size, see &enum nvme_id_ctrl_cqes. * @maxcmd: Maximum Outstanding Commands indicates the maximum number of @@ -1341,6 +1360,39 @@ struct nvme_id_psd { * total number of outstanding I/O commands across all I/O queues * on the controller for optimal operation. * @rsvd568: Reserved + * @cmmrtd: Controller Maximum Memory Range Tracking Descriptors indicates + * the maximum number of Memory Range Tracking Descriptors the + * controller supports. + * @nmmrtd: NVM Subsystem Maximum Memory Range Tracking Descriptors + * indicates the maximum number of Memory Range Tracking Descriptors + * the NVM subsystem supports. + * @minmrtg: Minimum Memory Range Tracking Granularity indicates the minimum + * value supported in the Requested Memory Range Tracking + * Granularity (RMRTG) field of the Track Memory Ranges data + * structure. + * @maxmrtg: Maximum Memory Range Tracking Granularity indicates the maximum + * value supported in the Requested Memory Range Tracking + * Granularity (RMRTG) field of the Track Memory Ranges data + * structure. + * @trattr: Tracking Attributes indicates supported attributes for the + * Track Send command and Track Receive command. + * @rsvd577: Reserved + * @mcudmq: Maximum Controller User Data Migration Queues indicates the + * maximum number of User Data Migration Queues supported by the + * controller. + * @mnsudmq: Maximum NVM Subsystem User Data Migration Queues indicates the + * maximum number of User Data Migration Queues supported by the NVM + * subsystem. + * @mcmr: Maximum CDQ Memory Ranges indicates the maximum number of + * memory ranges allowed to be specified by the PRP1 field of a + * Controller Data Queue command. + * @nmcmr: NVM Subsystem Maximum CDQ Memory Ranges indicates the maximum + * number of memory ranges for all Controller Data Queues in the + * NVM subsystem. + * @mcdqpc: Maximum Controller Data Queue PRP Count indicates the maximum + * number of PRPs allowed to be specified in the PRP list in the + * Controller Data Queue command. + * @rsvd588: Reserved * @subnqn: NVM Subsystem NVMe Qualified Name, UTF-8 null terminated string * @rsvd1024: Reserved * @ioccsz: I/O Queue Command Capsule Supported Size, defines the maximum @@ -1426,7 +1478,8 @@ struct nvme_id_ctrl { __le16 domainid; __u8 rsvd358[10]; __u8 megcap[16]; - __u8 rsvd384[128]; + __u8 tmpthha; + __u8 rsvd385[127]; __u8 sqes; __u8 cqes; __le16 maxcmd; @@ -1446,7 +1499,19 @@ struct nvme_id_ctrl { __u8 maxdna[16]; __le32 maxcna; __le32 oaqd; - __u8 rsvd568[200]; + __u8 rsvd568[2]; + __u16 cmmrtd; + __u16 nmmrtd; + __u8 minmrtg; + __u8 maxmrtg; + __u8 trattr; + __u8 rsvd577; + __u16 mcudmq; + __u16 mnsudmq; + __u16 mcmr; + __u16 nmcmr; + __u16 mcdqpc; + __u8 rsvd588[180]; char subnqn[NVME_NQN_LENGTH]; __u8 rsvd1024[768]; @@ -1526,29 +1591,103 @@ enum nvme_id_ctrl_cmic { /** * enum nvme_id_ctrl_oaes - Optional Asynchronous Events Supported + * @NVME_CTRL_OAES_NA_SHIFT: Shift amount to get the Namespace Attribute Notices event supported + * @NVME_CTRL_OAES_FA_SHIFT: Shift amount to get the Firmware Activation Notices event supported + * @NVME_CTRL_OAES_ANA_SHIFT: Shift amount to get the ANA Change Notices supported + * @NVME_CTRL_OAES_PLEA_SHIFT: Shift amount to get the Predictable Latency Event Aggregate Log + * Change Notices event supported + * @NVME_CTRL_OAES_LBAS_SHIFT: Shift amount to get the LBA Status Information Notices event + * supported + * @NVME_CTRL_OAES_EGE_SHIFT: Shift amount to get the Endurance Group Events Aggregate Log Change + * Notices event supported + * @NVME_CTRL_OAES_NS_SHIFT: Shift amount to get the Normal NVM Subsystem Shutdown event supported + * @NVME_CTRL_OAES_TTH_SHIFT: Shift amount to get the Temperature Threshold Hysteresis Recovery + * event supported + * @NVME_CTRL_OAES_RGCNS_SHIFT: Shift amount to get the Reachability Groups Change Notices supported + * @NVME_CTRL_OAES_ANSAN_SHIFT: Shift amount to get the Allocated Namespace Attribute Notices + * supported + * @NVME_CTRL_OAES_ZD_SHIFT: Shift amount to get the Zone Descriptor Change Notifications supported + * @NVME_CTRL_OAES_DL_SHIFT: Shift amount to get the Discover Log Page Change Notifications + * supported + * @NVME_CTRL_OAES_NA_MASK: Mask to get the Namespace Attribute Notices event supported + * @NVME_CTRL_OAES_FA_MASK: Mask to get the Firmware Activation Notices event supported + * @NVME_CTRL_OAES_ANA_MASK: Mask to get the ANA Change Notices supported + * @NVME_CTRL_OAES_PLEA_MASK: Mask to get the Predictable Latency Event Aggregate Log Change Notices + * event supported + * @NVME_CTRL_OAES_LBAS_MASK: Mask to get the LBA Status Information Notices event supported + * @NVME_CTRL_OAES_EGE_MASK: Mask to get the Endurance Group Events Aggregate Log Change Notices + * event supported + * @NVME_CTRL_OAES_NS_MASK: Mask to get the Normal NVM Subsystem Shutdown event supported + * @NVME_CTRL_OAES_TTH_MASK: Mask to get the Temperature Threshold Hysteresis Recovery event + * supported + * @NVME_CTRL_OAES_RGCNS_MASK: Mask to get the Reachability Groups Change Notices supported + * @NVME_CTRL_OAES_ANSAN_MASK: Mask to get the Allocated Namespace Attribute Notices supported + * @NVME_CTRL_OAES_ZD_MASK: Mask to get the Zone Descriptor Change Notifications supported + * @NVME_CTRL_OAES_DL_MASK: Mask to get the Discover Log Page Change Notifications supported * @NVME_CTRL_OAES_NA: Namespace Attribute Notices event supported * @NVME_CTRL_OAES_FA: Firmware Activation Notices event supported * @NVME_CTRL_OAES_ANA: ANA Change Notices supported - * @NVME_CTRL_OAES_PLEA: Predictable Latency Event Aggregate Log - * Change Notices event supported + * @NVME_CTRL_OAES_PLEA: Predictable Latency Event Aggregate Log Change Notices event supported * @NVME_CTRL_OAES_LBAS: LBA Status Information Notices event supported - * @NVME_CTRL_OAES_EGE: Endurance Group Events Aggregate Log Change - * Notices event supported + * @NVME_CTRL_OAES_EGE: Endurance Group Events Aggregate Log Change Notices event supported * @NVME_CTRL_OAES_NS: Normal NVM Subsystem Shutdown event supported + * @NVME_CTRL_OAES_TTH: Temperature Threshold Hysteresis Recovery event supported + * @NVME_CTRL_OAES_RGCNS: Reachability Groups Change Notices supported + * @NVME_CTRL_OAES_ANSAN: Allocated Namespace Attribute Notices supported * @NVME_CTRL_OAES_ZD: Zone Descriptor Change Notifications supported * @NVME_CTRL_OAES_DL: Discover Log Page Change Notifications supported */ enum nvme_id_ctrl_oaes { - NVME_CTRL_OAES_NA = 1 << 8, - NVME_CTRL_OAES_FA = 1 << 9, - NVME_CTRL_OAES_ANA = 1 << 11, - NVME_CTRL_OAES_PLEA = 1 << 12, - NVME_CTRL_OAES_LBAS = 1 << 13, - NVME_CTRL_OAES_EGE = 1 << 14, - NVME_CTRL_OAES_NS = 1 << 15, - NVME_CTRL_OAES_ZD = 1 << 27, - NVME_CTRL_OAES_DL = 1 << 31, -}; + NVME_CTRL_OAES_NA_SHIFT = 8, + NVME_CTRL_OAES_FA_SHIFT = 9, + NVME_CTRL_OAES_ANA_SHIFT = 11, + NVME_CTRL_OAES_PLEA_SHIFT = 12, + NVME_CTRL_OAES_LBAS_SHIFT = 13, + NVME_CTRL_OAES_EGE_SHIFT = 14, + NVME_CTRL_OAES_NS_SHIFT = 15, + NVME_CTRL_OAES_TTH_SHIFT = 16, + NVME_CTRL_OAES_RGCNS_SHIFT = 17, + NVME_CTRL_OAES_ANSAN_SHIFT = 19, + NVME_CTRL_OAES_ZD_SHIFT = 27, + NVME_CTRL_OAES_DL_SHIFT = 31, + NVME_CTRL_OAES_NA_MASK = 0x1, + NVME_CTRL_OAES_FA_MASK = 0x1, + NVME_CTRL_OAES_ANA_MASK = 0x1, + NVME_CTRL_OAES_PLEA_MASK = 0x1, + NVME_CTRL_OAES_LBAS_MASK = 0x1, + NVME_CTRL_OAES_EGE_MASK = 0x1, + NVME_CTRL_OAES_NS_MASK = 0x1, + NVME_CTRL_OAES_TTH_MASK = 0x1, + NVME_CTRL_OAES_RGCNS_MASK = 0x1, + NVME_CTRL_OAES_ANSAN_MASK = 0x1, + NVME_CTRL_OAES_ZD_MASK = 0x1, + NVME_CTRL_OAES_DL_MASK = 0x1, + NVME_CTRL_OAES_NA = NVME_VAL(CTRL_OAES_NA), + NVME_CTRL_OAES_FA = NVME_VAL(CTRL_OAES_FA), + NVME_CTRL_OAES_ANA = NVME_VAL(CTRL_OAES_ANA), + NVME_CTRL_OAES_PLEA = NVME_VAL(CTRL_OAES_PLEA), + NVME_CTRL_OAES_LBAS = NVME_VAL(CTRL_OAES_LBAS), + NVME_CTRL_OAES_EGE = NVME_VAL(CTRL_OAES_EGE), + NVME_CTRL_OAES_NS = NVME_VAL(CTRL_OAES_NS), + NVME_CTRL_OAES_TTH = NVME_VAL(CTRL_OAES_TTH), + NVME_CTRL_OAES_RGCNS = NVME_VAL(CTRL_OAES_RGCNS), + NVME_CTRL_OAES_ANSAN = NVME_VAL(CTRL_OAES_ANSAN), + NVME_CTRL_OAES_ZD = NVME_VAL(CTRL_OAES_ZD), + NVME_CTRL_OAES_DL = NVME_VAL(CTRL_OAES_DL), +}; + +#define NVME_CTRL_OAES_NAN(oaes) NVME_GET(oaes, CTRL_OAES_NA) +#define NVME_CTRL_OAES_FAN(oaes) NVME_GET(oaes, CTRL_OAES_FA) +#define NVME_CTRL_OAES_ANACN(oaes) NVME_GET(oaes, CTRL_OAES_ANA) +#define NVME_CTRL_OAES_PLEALCN(oaes) NVME_GET(oaes, CTRL_OAES_PLEA) +#define NVME_CTRL_OAES_LBASIAN(oaes) NVME_GET(oaes, CTRL_OAES_LBAS) +#define NVME_CTRL_OAES_EGEALPCN(oaes) NVME_GET(oaes, CTRL_OAES_EGE) +#define NVME_CTRL_OAES_NNVMSS(oaes) NVME_GET(oaes, CTRL_OAES_NS) +#define NVME_CTRL_OAES_TTHR(oaes) NVME_GET(oaes, CTRL_OAES_TTH) +#define NVME_CTRL_OAES_RGCNS(oaes) NVME_GET(oaes, CTRL_OAES_RGCNS) +#define NVME_CTRL_OAES_ANSAN(oaes) NVME_GET(oaes, CTRL_OAES_ANSAN) +#define NVME_CTRL_OAES_ZDCN(oaes) NVME_GET(oaes, CTRL_OAES_ZD) +#define NVME_CTRL_OAES_DLPCN(oaes) NVME_GET(oaes, CTRL_OAES_DL) /** * enum nvme_id_ctrl_ctratt - Controller attributes @@ -1972,6 +2111,14 @@ enum nvme_id_ctrl_fuses { /** * enum nvme_id_ctrl_fna - This field indicates attributes for the Format NVM * command. + * @NVME_CTRL_FNA_FMT_ALL_NS_SHIFT: Shift amount to get the format applied to all namespaces + * @NVME_CTRL_FNA_SEC_ALL_NS_SHIFT: Shift amount to get the secure erase applied to all namespaces + * @NVME_CTRL_FNA_CES_SHIFT: Shift amount to get the cryptographic erase supported + * @NVME_CTRL_FNA_NSID_ALL_F_SHIFT: Shift amount to get the format supported an NSID FFFFFFFFh + * @NVME_CTRL_FNA_FMT_ALL_NS_MASK: Mask to get the format applied to all namespaces + * @NVME_CTRL_FNA_SEC_ALL_NS_MASK: Mask to get the secure erase applied to all namespaces + * @NVME_CTRL_FNA_CES_MASK: Mask to get the cryptographic erase supported + * @NVME_CTRL_FNA_NSID_ALL_F_MASK: Mask to get the format supported an NSID FFFFFFFFh * @NVME_CTRL_FNA_FMT_ALL_NAMESPACES: If set, then all namespaces in an NVM * subsystem shall be configured with the * same attributes and a format (excluding @@ -1996,11 +2143,24 @@ enum nvme_id_ctrl_fuses { * FFFFFFFFh. */ enum nvme_id_ctrl_fna { - NVME_CTRL_FNA_FMT_ALL_NAMESPACES = 1 << 0, - NVME_CTRL_FNA_SEC_ALL_NAMESPACES = 1 << 1, - NVME_CTRL_FNA_CRYPTO_ERASE = 1 << 2, - NVME_CTRL_FNA_NSID_FFFFFFFF = 1 << 3, -}; + NVME_CTRL_FNA_FMT_ALL_NS_SHIFT = 0, + NVME_CTRL_FNA_SEC_ALL_NS_SHIFT = 1, + NVME_CTRL_FNA_CES_SHIFT = 2, + NVME_CTRL_FNA_NSID_ALL_F_SHIFT = 3, + NVME_CTRL_FNA_FMT_ALL_NS_MASK = 0x1, + NVME_CTRL_FNA_SEC_ALL_NS_MASK = 0x1, + NVME_CTRL_FNA_CES_MASK = 0x1, + NVME_CTRL_FNA_NSID_ALL_F_MASK = 0x1, + NVME_CTRL_FNA_FMT_ALL_NAMESPACES = NVME_VAL(CTRL_FNA_FMT_ALL_NS), + NVME_CTRL_FNA_SEC_ALL_NAMESPACES = NVME_VAL(CTRL_FNA_SEC_ALL_NS), + NVME_CTRL_FNA_CRYPTO_ERASE = NVME_VAL(CTRL_FNA_CES), + NVME_CTRL_FNA_NSID_FFFFFFFF = NVME_VAL(CTRL_FNA_NSID_ALL_F), +}; + +#define NVME_CTRL_FNA_FMT_ALL_NS(fna) NVME_GET(fna, CTRL_FNA_FMT_ALL_NS) +#define NVME_CTRL_FNA_SEC_ALL_NS(fna) NVME_GET(fna, CTRL_FNA_SEC_ALL_NS) +#define NVME_CTRL_FNA_CES(fna) NVME_GET(fna, CTRL_FNA_CES) +#define NVME_CTRL_FNA_NSID_ALL_F(fna) NVME_GET(fna, CTRL_FNA_NSID_ALL_F) /** * enum nvme_id_ctrl_vwc - Volatile write cache @@ -2710,7 +2870,9 @@ struct nvme_ns_list { * @dmsl: Dataset Management Size Limit * @rsvd16: Reserved * @aocs: Admin Optional Command Support - * @rsvd20: Reserved + * @ver: Version + * @lbamqf: LBA Migration Queue Format + * @rsvd25: Reserved */ struct nvme_id_ctrl_nvm { __u8 vsl; @@ -2721,7 +2883,9 @@ struct nvme_id_ctrl_nvm { __le64 dmsl; __u8 rsvd16[2]; __le16 aocs; - __u8 rsvd20[4076]; + __le32 ver; + __u8 lbamqf; + __u8 rsvd25[4071]; }; /** @@ -2732,7 +2896,11 @@ struct nvme_id_ctrl_nvm { * @rsvd10: Reserved * @elbaf: List of Extended LBA Format Support * @npdgl: Namespace Preferred Deallocate Granularity Large - * @rsvd272: Reserved + * @nprg: Namespace Preferred Read Granularity + * @npra: Namespace Preferred Read Alignment + * @nors: Namespace Optimal Read Size + * @npdal: Namespace Preferred Deallocate Alignment Large + * @lbapss: LBA Format Placement Shard Size * @tlbaag: Tracked LBA Allocation Granularity * @rsvd296: Reserved */ @@ -2743,7 +2911,11 @@ struct nvme_nvm_id_ns { __u8 rsvd10[2]; __le32 elbaf[64]; __le32 npdgl; - __u8 rsvd272[20]; + __le32 nprg; + __le32 npra; + __le32 nors; + __le32 npdal; + __le32 lbapss; __le32 tlbaag; __u8 rsvd296[3800]; }; @@ -3301,6 +3473,7 @@ struct nvme_cmd_effects_log { * @NVME_CMD_EFFECTS_NCC: Namespace Capability Change * @NVME_CMD_EFFECTS_NIC: Namespace Inventory Change * @NVME_CMD_EFFECTS_CCC: Controller Capability Change + * @NVME_CMD_EFFECTS_CSER_MASK: Command Submission and Execution Relaxations * @NVME_CMD_EFFECTS_CSE_MASK: Command Submission and Execution * @NVME_CMD_EFFECTS_UUID_SEL: UUID Selection Supported */ @@ -3310,7 +3483,8 @@ enum nvme_cmd_effects { NVME_CMD_EFFECTS_NCC = 1 << 2, NVME_CMD_EFFECTS_NIC = 1 << 3, NVME_CMD_EFFECTS_CCC = 1 << 4, - NVME_CMD_EFFECTS_CSE_MASK = 3 << 16, + NVME_CMD_EFFECTS_CSER_MASK = 3 << 14, + NVME_CMD_EFFECTS_CSE_MASK = 7 << 16, NVME_CMD_EFFECTS_UUID_SEL = 1 << 19, }; @@ -3884,6 +4058,7 @@ struct nvme_persistent_event_entry { * @NVME_PEL_SET_FEATURE_EVENT: Set Feature Event * @NVME_PEL_TELEMETRY_CRT: Telemetry Log Create Event * @NVME_PEL_THERMAL_EXCURSION_EVENT: Thermal Excursion Event + * @NVME_PEL_SANITIZE_MEDIA_VERIF_EVENT:Sanitize Media Verification Event * @NVME_PEL_VENDOR_SPECIFIC_EVENT: Vendor Specific Event * @NVME_PEL_TCG_DEFINED_EVENT: TCG Defined Event */ @@ -3901,6 +4076,7 @@ enum nvme_persistent_event_types { NVME_PEL_SET_FEATURE_EVENT = 0x0b, NVME_PEL_TELEMETRY_CRT = 0x0c, NVME_PEL_THERMAL_EXCURSION_EVENT = 0x0d, + NVME_PEL_SANITIZE_MEDIA_VERIF_EVENT = 0x0e, NVME_PEL_VENDOR_SPECIFIC_EVENT = 0xde, NVME_PEL_TCG_DEFINED_EVENT = 0xdf, }; @@ -4608,6 +4784,20 @@ struct nvme_supported_cap_config_list_log { }; /** + * struct nvme_lockdown_log - Command and Feature Lockdown Log + * @cfila: Contents of the Command and Feature Identifier List field in the log page. + * @rsvd1: Reserved + * @lngth: Length of Command and Feature Identifier List field + * @cfil: Command and Feature Identifier List + */ +struct nvme_lockdown_log { + __u8 cfila; + __u8 rsvd1[2]; + __u8 lngth; + __u8 cfil[508]; +}; + +/** * struct nvme_resv_notification_log - Reservation Notification Log * @lpc: Log Page Count * @rnlpt: See &enum nvme_resv_notify_rnlpt. @@ -4705,7 +4895,15 @@ enum nvme_resv_notify_rnlpt { * to be completed in the background when the Sanitize command that * started that operation is completed. A value of FFFFFFFFh indicates * that no time period is reported. - * @rsvd32: Reserved + * @etpvds: Estimated Time For Post-Verification Deallocation State: indicates the + * number of seconds required to deallocate all media allocated for user data + * after exiting the Media Verification state (i.e., the time difference between + * entering and exiting the Post-Verification Deallocation state), if that state + * is entered as part of the sanitize operation. A value of FFFFFFFFh indicates + * that no time period is reported. + * @ssi: Sanitize State Information: indicate the state of the Sanitize Operation + * State Machine. + * @rsvd37: Reserved */ struct nvme_sanitize_log_page { __le16 sprog; @@ -4717,7 +4915,9 @@ struct nvme_sanitize_log_page { __le32 etond; __le32 etbend; __le32 etcend; - __u8 rsvd32[480]; + __le32 etpvds; + __u8 ssi; + __u8 rsvd37[475]; }; /** @@ -4761,6 +4961,10 @@ struct nvme_sanitize_log_page { * the NVM subsystem has never been sanitized; * or since the most recent successful sanitize * operation. + * @NVME_SANITIZE_SSTAT_MVCNCLD_SHIFT: Shift amount to get the value of Media Verification + * Canceled bit of Sanitize status field. + * @NVME_SANITIZE_SSTAT_MVCNCLD_MASK: Mask to get the value of Media Verification Canceled + * bit of Sanitize status field. */ enum nvme_sanitize_sstat { NVME_SANITIZE_SSTAT_STATUS_SHIFT = 0, @@ -4775,6 +4979,59 @@ enum nvme_sanitize_sstat { NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT = 8, NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_MASK = 0x1, NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED = 1 << NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT, + NVME_SANITIZE_SSTAT_MVCNCLD_SHIFT = 9, + NVME_SANITIZE_SSTAT_MVCNCLD_MASK = 0x1, +}; + +/** + * enum nvme_sanitize_ssi - Sanitize State Information (SSI) + * @NVME_SANITIZE_SSI_SANS_SHIFT: Shift amount to get the value of Sanitize State + * from Sanitize State Information (SSI) field. + * @NVME_SANITIZE_SSI_SANS_MASK: Mask to get the value of Sanitize State from + * Sanitize State Information (SSI) field. + * @NVME_SANITIZE_SSI_FAILS_SHIFT: Shift amount to get the value of Failure State + * from Sanitize State Information (SSI) field. + * @NVME_SANITIZE_SSI_FAILS_MASK: Mask to get the value of Failure State from + * Sanitize State Information (SSI) field. + * @NVME_SANITIZE_SSI_IDLE: No sanitize operation is in process. + * @NVME_SANITIZE_SSI_RESTRICT_PROCESSING: The Sanitize operation is in Restricted Processing + * State. + * @NVME_SANITIZE_SSI_RESTRICT_FAILURE: The Sanitize operation is in Restricted Failure + * State. This state is entered if sanitize processing + * was performed in the Restricted Processing state and + * sanitize processing failed or a failure occurred + * during deallocation of media allocated for user data + * in the Post-Verification Deallocation state. + * @NVME_SANITIZE_SSI_UNRESTRICT_PROCESSING: The Sanitize operation is in Unrestricted Processing + * State. + * @NVME_SANITIZE_SSI_UNRESTRICT_FAILURE: The Sanitize operation is in Unrestricted Failure + * State. This state is entered if sanitize processing + * was performed in the Unrestricted Processing state + * and sanitize processing failed or a failure occurred + * during deallocation of media allocated for user data + * in the Post-Verification. + * @NVME_SANITIZE_SSI_MEDIA_VERIFICATION: The Sanitize operation is in Media Verification + * State. In this state, the sanitize processing + * completed successfully, and all media allocated for + * user data in the sanitization target is readable by + * the host for purposes of verifying sanitization. + * @NVME_SANITIZE_SSI_POST_VERIF_DEALLOC: The Sanitize operation is in Post-Verification + * Deallocation State. In this state, the controller + * shall deallocate all media allocated for user data + * in the sanitization target. + */ +enum nvme_sanitize_ssi { + NVME_SANITIZE_SSI_SANS_SHIFT = 0, + NVME_SANITIZE_SSI_SANS_MASK = 0xf, + NVME_SANITIZE_SSI_FAILS_SHIFT = 4, + NVME_SANITIZE_SSI_FAILS_MASK = 0xf, + NVME_SANITIZE_SSI_IDLE = 0, + NVME_SANITIZE_SSI_RESTRICT_PROCESSING = 1, + NVME_SANITIZE_SSI_RESTRICT_FAILURE = 2, + NVME_SANITIZE_SSI_UNRESTRICT_PROCESSING = 3, + NVME_SANITIZE_SSI_UNRESTRICT_FAILURE = 4, + NVME_SANITIZE_SSI_MEDIA_VERIFICATION = 5, + NVME_SANITIZE_SSI_POST_VERIF_DEALLOC = 6, }; /** @@ -4908,6 +5165,46 @@ enum nvme_fdp_config_fdpa { }; /** + * enum nvme_lockdown_log_scope - lockdown log page scope attributes + * @NVME_LOCKDOWN_ADMIN_CMD: Scope value for Admin commandS + * @NVME_LOCKDOWN_FEATURE_ID: Scope value for Feature ID + * @NVME_LOCKDOWN_MI_CMD_SET: Scope value for Management Interface commands + * @NVME_LOCKDOWN_PCI_CMD_SET: Scope value for PCI commands + */ +enum nvme_lockdown_log_scope { + NVME_LOCKDOWN_ADMIN_CMD = 0x0, + NVME_LOCKDOWN_FEATURE_ID = 0x2, + NVME_LOCKDOWN_MI_CMD_SET = 0x3, + NVME_LOCKDOWN_PCI_CMD_SET = 0x4, +}; + +/** + * enum nvme_lockdown_log_contents - lockdown log page content attributes + * @NVME_LOCKDOWN_SUPPORTED_CMD: Content value for Supported commands + * @NVME_LOCKDOWN_PROHIBITED_CMD: Content value for prohibited commands + * @NVME_LOCKDOWN_PROHIBITED_OUTOFBAND_CMD: Content value for prohibited side band commands + */ +enum nvme_lockdown_log_contents { + NVME_LOCKDOWN_SUPPORTED_CMD = 0x0, + NVME_LOCKDOWN_PROHIBITED_CMD = 0x1, + NVME_LOCKDOWN_PROHIBITED_OUTOFBAND_CMD = 0x2, +}; + +/** + * enum nvme_lockdown_scope_contents - Lockdown Log shift and mask + * @NVME_LOCKDOWN_SS_SHIFT: Lockdown log scope select Shift + * @NVME_LOCKDOWN_SS_MASK: Lockdown log scope select Mask + * @NVME_LOCKDOWN_CS_SHIFT: Lockdown log contents Shift + * @NVME_LOCKDOWN_CS_MASK: Lockdown log contents Mask + */ +enum nvme_lockdown_scope_contents { + NVME_LOCKDOWN_SS_SHIFT = 0, + NVME_LOCKDOWN_SS_MASK = 0xf, + NVME_LOCKDOWN_CS_SHIFT = 4, + NVME_LOCKDOWN_CS_MASK = 0x3, +}; + +/** * struct nvme_fdp_config_desc - FDP Configuration Descriptor * @size: Descriptor size * @fdpa: FDP Attributes (&enum nvme_fdp_config_fdpa) @@ -5189,6 +5486,22 @@ struct nvme_lba_status { }; /** + * enum nvme_lba_status_cmpc - Get LBA Status Command Completion Condition + * @NVME_LBA_STATUS_CMPC_NO_CMPC: No indication of the completion condition + * @NVME_LBA_STATUS_CMPC_INCOMPLETE: Command completed, but additional LBA Status + * Descriptor Entries are available to transfer + * or scan did not complete (if ATYPE = 10h) + * @NVME_LBA_STATUS_CMPC_COMPLETE: Completed the specified action over the number + * of LBAs specified in the Range Length field and + * transferred all available LBA Status Descriptors + */ +enum nvme_lba_status_cmpc { + NVME_LBA_STATUS_CMPC_NO_CMPC = 0x0, + NVME_LBA_STATUS_CMPC_INCOMPLETE = 0x1, + NVME_LBA_STATUS_CMPC_COMPLETE = 0x2, +}; + +/** * struct nvme_feat_auto_pst - Autonomous Power State Transition * @apst_entry: See &enum nvme_apst_entry */ @@ -6956,6 +7269,20 @@ struct nvme_mi_vpd_hdr { * Originator field does not match the * Host NQN used by the DDC to connect * to the CDC. + * @NVME_SC_INVALID_CONTROLER_DATA_QUEUE: This error indicates that the + * specified Controller Data Queue + * Identifier is invalid for the controller + * processing the command. + * @NVME_SC_NOT_ENOUGH_RESOURCES: This error indicates that there is not + * enough resources in the controller to + * process the command. + * @NVME_SC_CONTROLLER_SUSPENDED: The operation requested is not allowed if + * the specified controller is suspended. + * @NVME_SC_CONTROLLER_NOT_SUSPENDED: The operation requested is not allowed if + * the specified controller is not + * suspended. + * @NVME_SC_CONTROLLER_DATA_QUEUE_FULL: The controller detected that a + * Controller Data Queue became full. * @NVME_SC_BAD_ATTRIBUTES: Conflicting Dataset Management Attributes * @NVME_SC_INVALID_PI: Invalid Protection Information * @NVME_SC_READ_ONLY: Attempted Write to Read Only Range @@ -7214,6 +7541,15 @@ enum nvme_status_field { NVME_SC_ZONEGRP_ORIGINATOR_INVLD = 0x34, /* + * Command Set Specific - Live Migration + */ + NVME_SC_INVALID_CONTROLER_DATA_QUEUE = 0x37, + NVME_SC_NOT_ENOUGH_RESOURCES = 0x38, + NVME_SC_CONTROLLER_SUSPENDED = 0x39, + NVME_SC_CONTROLLER_NOT_SUSPENDED = 0x3A, + NVME_SC_CONTROLLER_DATA_QUEUE_FULL = 0x3B, + + /* * I/O Command Set Specific - NVM commands: */ NVME_SC_BAD_ATTRIBUTES = 0x80, @@ -7491,6 +7827,9 @@ enum nvme_admin_opcode { * @NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE: I/O Command Set specific ID Namespace * Data Structure for Allocated Namespace ID * @NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE: Base Specification 2.0a section 5.17.2.21 + * @NVME_IDENTIFY_CNS_SUPPORTED_CTRL_STATE_FORMATS: Supported Controller State Formats + * identifying the supported NVMe Controller + * State data structures */ enum nvme_identify_cns { NVME_IDENTIFY_CNS_NS = 0x00, @@ -7517,6 +7856,7 @@ enum nvme_identify_cns { NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST = 0x1A, NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE = 0x1B, NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE = 0x1C, + NVME_IDENTIFY_CNS_SUPPORTED_CTRL_STATE_FORMATS = 0x20, }; /** @@ -7543,12 +7883,21 @@ enum nvme_identify_cns { * @NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS: NVMe-MI Commands Supported and Effects * @NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN: Command and Feature Lockdown * @NVME_LOG_LID_BOOT_PARTITION: Boot Partition + * @NVME_LOG_LID_ROTATIONAL_MEDIA_INFO: Rotational Media Information + * @NVME_LOG_LID_DISPERSED_NS_PARTICIPATING_NSS:Dispersed Namespace Participating NVM Subsystems + * @NVME_LOG_LID_MGMT_ADDR_LIST: Management Address List * @NVME_LOG_LID_PHY_RX_EOM: Physical Interface Receiver Eye Opening Measurement + * @NVME_LOG_LID_REACHABILITY_GROUPS: Reachability Groups + * @NVME_LOG_LID_REACHABILITY_ASSOCIATIONS: Reachability Associations + * @NVME_LOG_LID_CHANGED_ALLOC_NS_LIST: Changed Allocated Namespace List * @NVME_LOG_LID_FDP_CONFIGS: FDP Configurations * @NVME_LOG_LID_FDP_RUH_USAGE: Reclaim Unit Handle Usage * @NVME_LOG_LID_FDP_STATS: FDP Statistics * @NVME_LOG_LID_FDP_EVENTS: FDP Events * @NVME_LOG_LID_DISCOVER: Discovery + * @NVME_LOG_LID_HOST_DISCOVER: Host Discovery + * @NVME_LOG_LID_AVE_DISCOVER: AVE Discovery + * @NVME_LOG_LID_PULL_MODEL_DDC_REQ: Pull Model DDC Request * @NVME_LOG_LID_RESERVATION: Reservation Notification * @NVME_LOG_LID_SANITIZE: Sanitize Status * @NVME_LOG_LID_ZNS_CHANGED_ZONES: Changed Zone List @@ -7576,12 +7925,21 @@ enum nvme_cmd_get_log_lid { NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS = 0x13, NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN = 0x14, NVME_LOG_LID_BOOT_PARTITION = 0x15, + NVME_LOG_LID_ROTATIONAL_MEDIA_INFO = 0x16, + NVME_LOG_LID_DISPERSED_NS_PARTICIPATING_NSS = 0x17, + NVME_LOG_LID_MGMT_ADDR_LIST = 0x18, NVME_LOG_LID_PHY_RX_EOM = 0x19, + NVME_LOG_LID_REACHABILITY_GROUPS = 0x1a, + NVME_LOG_LID_REACHABILITY_ASSOCIATIONS = 0x1b, + NVME_LOG_LID_CHANGED_ALLOC_NS_LIST = 0x1c, NVME_LOG_LID_FDP_CONFIGS = 0x20, NVME_LOG_LID_FDP_RUH_USAGE = 0x21, NVME_LOG_LID_FDP_STATS = 0x22, NVME_LOG_LID_FDP_EVENTS = 0x23, NVME_LOG_LID_DISCOVER = 0x70, + NVME_LOG_LID_HOST_DISCOVER = 0x71, + NVME_LOG_LID_AVE_DISCOVER = 0x72, + NVME_LOG_LID_PULL_MODEL_DDC_REQ = 0x73, NVME_LOG_LID_RESERVATION = 0x80, NVME_LOG_LID_SANITIZE = 0x81, NVME_LOG_LID_ZNS_CHANGED_ZONES = 0xbf, @@ -8059,12 +8417,14 @@ enum nvme_directive_send_identify_endir { * @NVME_SANITIZE_SANACT_START_BLOCK_ERASE: Start a Block Erase sanitize operation. * @NVME_SANITIZE_SANACT_START_OVERWRITE: Start an Overwrite sanitize operation. * @NVME_SANITIZE_SANACT_START_CRYPTO_ERASE: Start a Crypto Erase sanitize operation. + * @NVME_SANITIZE_SANACT_EXIT_MEDIA_VERIF: Exit Media Verification State */ enum nvme_sanitize_sanact { NVME_SANITIZE_SANACT_EXIT_FAILURE = 1, NVME_SANITIZE_SANACT_START_BLOCK_ERASE = 2, NVME_SANITIZE_SANACT_START_OVERWRITE = 3, NVME_SANITIZE_SANACT_START_CRYPTO_ERASE = 4, + NVME_SANITIZE_SANACT_EXIT_MEDIA_VERIF = 5, }; /** @@ -8323,6 +8683,32 @@ enum nvme_io_opcode { }; /** + * enum nvme_kv_opcode - Opcodes for KV Commands + * @nvme_kv_cmd_flush: Flush + * @nvme_kv_cmd_store: Store + * @nvme_kv_cmd_retrieve: Retrieve + * @nvme_kv_cmd_list: List + * @nvme_kv_cmd_resv_register: Reservation Register + * @nvme_kv_cmd_resv_report: Reservation Report + * @nvme_kv_cmd_delete: Delete + * @nvme_kv_cmd_resv_acquire: Reservation Acquire + * @nvme_kv_cmd_exist: Exist + * @nvme_kv_cmd_resv_release: Reservation Release + */ +enum nvme_kv_opcode { + nvme_kv_cmd_flush = 0x00, + nvme_kv_cmd_store = 0x01, + nvme_kv_cmd_retrieve = 0x02, + nvme_kv_cmd_list = 0x06, + nvme_kv_cmd_resv_register = 0x0d, + nvme_kv_cmd_resv_report = 0x0e, + nvme_kv_cmd_delete = 0x10, + nvme_kv_cmd_resv_acquire = 0x11, + nvme_kv_cmd_exist = 0x14, + nvme_kv_cmd_resv_release = 0x15, +}; + +/** * enum nvme_io_control_flags - I/O control flags * @NVME_IO_DTYPE_STREAMS: Directive Type Streams * @NVME_IO_STC: Storage Tag Check diff --git a/src/nvme/util.c b/src/nvme/util.c index ce0ce76..1af358a 100644 --- a/src/nvme/util.c +++ b/src/nvme/util.c @@ -286,6 +286,11 @@ static const char * const cmd_spec_status[] = { [NVME_SC_INSUFFICIENT_DISC_RES] = "Discovery Info entries exceed Discovery Controller's capacity", [NVME_SC_REQSTD_FUNCTION_DISABLED] = "Fabric Zoning is not enabled on the CDC", [NVME_SC_ZONEGRP_ORIGINATOR_INVLD] = "The NQN contained in the ZoneGroup Originator field does not match the Host NQN used by the DDC to connect to the CDC", + [NVME_SC_INVALID_CONTROLER_DATA_QUEUE] = "Invalid Controller Data Queue", + [NVME_SC_NOT_ENOUGH_RESOURCES] = "Not Enough Resources", + [NVME_SC_CONTROLLER_SUSPENDED] = "Controller Suspended: Operation failed because the controller is currently in a suspended state", + [NVME_SC_CONTROLLER_NOT_SUSPENDED] = "Controller Not Suspended: Operation failed because the controller is not in a suspended state", + [NVME_SC_CONTROLLER_DATA_QUEUE_FULL] = "Controller Data Queue Full", }; static const char * const nvm_status[] = { @@ -621,6 +626,7 @@ static const char * const libnvme_status[] = { [ENVME_CONNECT_CONNREFUSED] = "connection refused", [ENVME_CONNECT_ADDRNOTAVAIL] = "cannot assign requested address", [ENVME_CONNECT_IGNORED] = "connection ignored", + [ENVME_CONNECT_NOKEY] = "pre-shared TLS key is missing" }; const char *nvme_errno_to_string(int status) diff --git a/src/nvme/util.h b/src/nvme/util.h index 71fea9f..364ca0f 100644 --- a/src/nvme/util.h +++ b/src/nvme/util.h @@ -41,6 +41,7 @@ * @ENVME_CONNECT_CONNREFUSED: connection refused * @ENVME_CONNECT_ADDRNOTAVAIL: cannot assign requested address * @ENVME_CONNECT_IGNORED: connect attempt is ignored due to configuration + * @ENVME_CONNECT_NOKEY: the TLS key is missing */ enum nvme_connect_err { ENVME_CONNECT_RESOLVE = 1000, @@ -63,6 +64,7 @@ enum nvme_connect_err { ENVME_CONNECT_CONNREFUSED, ENVME_CONNECT_ADDRNOTAVAIL, ENVME_CONNECT_IGNORED, + ENVME_CONNECT_NOKEY, }; /** |