diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-06-30 22:36:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-06-30 22:36:10 +0000 |
commit | 61d0a8bdffbbb7229776d2f4f2e79ed22d21551f (patch) | |
tree | 2e249969fedce45eb37ae6314ad167595900fe38 /src | |
parent | Releasing debian version 1.4-4. (diff) | |
download | libnvme-61d0a8bdffbbb7229776d2f4f2e79ed22d21551f.tar.xz libnvme-61d0a8bdffbbb7229776d2f4f2e79ed22d21551f.zip |
Merging upstream version 1.5.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/libnvme-mi.map | 5 | ||||
-rw-r--r-- | src/libnvme.h | 1 | ||||
-rw-r--r-- | src/libnvme.map | 12 | ||||
-rw-r--r-- | src/meson.build | 2 | ||||
-rw-r--r-- | src/nvme/api-types.h | 6 | ||||
-rw-r--r-- | src/nvme/fabrics.c | 333 | ||||
-rw-r--r-- | src/nvme/ioctl.c | 35 | ||||
-rw-r--r-- | src/nvme/ioctl.h | 10 | ||||
-rw-r--r-- | src/nvme/json.c | 40 | ||||
-rw-r--r-- | src/nvme/mi.c | 35 | ||||
-rw-r--r-- | src/nvme/mi.h | 20 | ||||
-rw-r--r-- | src/nvme/nbft.c | 726 | ||||
-rw-r--r-- | src/nvme/nbft.h | 1238 | ||||
-rw-r--r-- | src/nvme/private.h | 36 | ||||
-rw-r--r-- | src/nvme/tree.c | 141 | ||||
-rw-r--r-- | src/nvme/tree.h | 43 | ||||
-rw-r--r-- | src/nvme/types.h | 95 | ||||
-rw-r--r-- | src/nvme/util.c | 80 | ||||
-rw-r--r-- | src/nvme/util.h | 15 |
19 files changed, 2788 insertions, 85 deletions
diff --git a/src/libnvme-mi.map b/src/libnvme-mi.map index 0b8b5b7..f1ce712 100644 --- a/src/libnvme-mi.map +++ b/src/libnvme-mi.map @@ -1,3 +1,8 @@ +LIBNVME_MI_1_5 { + global: + nvme_mi_ctrl_id; +}; + LIBNVME_MI_1_4 { global: nvme_mi_admin_get_log_page; diff --git a/src/libnvme.h b/src/libnvme.h index 6be9058..2c7fe3a 100644 --- a/src/libnvme.h +++ b/src/libnvme.h @@ -17,6 +17,7 @@ extern "C" { #include "nvme/types.h" #include "nvme/linux.h" #include "nvme/ioctl.h" +#include "nvme/nbft.h" #include "nvme/fabrics.h" #include "nvme/filters.h" #include "nvme/tree.h" diff --git a/src/libnvme.map b/src/libnvme.map index a1294f4..82387d4 100644 --- a/src/libnvme.map +++ b/src/libnvme.map @@ -1,5 +1,17 @@ # SPDX-License-Identifier: LGPL-2.1-or-later +LIBNVME_1_5 { + global: + nvme_ctrl_get_phy_slot; + nvme_ipaddrs_eq; + nvme_nbft_free; + nvme_nbft_read; + nvme_root_get_application; + nvme_root_set_application; + nvme_subsystem_get_application; + nvme_subsystem_set_application; +}; + LIBNVME_1_4 { global: nvme_lookup_keyring; diff --git a/src/meson.build b/src/meson.build index 3732f8c..e8b667c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,6 +7,7 @@ # sources = [ 'nvme/cleanup.c', + 'nvme/nbft.c', 'nvme/fabrics.c', 'nvme/filters.c', 'nvme/ioctl.c', @@ -125,6 +126,7 @@ install_headers([ 'nvme/ioctl.h', 'nvme/linux.h', 'nvme/log.h', + 'nvme/nbft.h', 'nvme/tree.h', 'nvme/types.h', 'nvme/util.h', diff --git a/src/nvme/api-types.h b/src/nvme/api-types.h index 9f3604e..296a7b0 100644 --- a/src/nvme/api-types.h +++ b/src/nvme/api-types.h @@ -196,6 +196,9 @@ struct nvme_format_nvm_args { * @nsid: Namespace identifier * @sel: Type of management operation to perform * @csi: Command Set Identifier + * @rsvd1: Reserved + * @rsvd2: Reserved + * @data: Host Software Specified Fields */ struct nvme_ns_mgmt_args { __u32 *result; @@ -206,6 +209,9 @@ struct nvme_ns_mgmt_args { __u32 nsid; enum nvme_ns_mgmt_sel sel; __u8 csi; + __u8 rsvd1[3]; + void *rsvd2; + struct nvme_ns_mgmt_host_sw_specified *data; }; /** diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c index 3c32e27..f0a06e8 100644 --- a/src/nvme/fabrics.c +++ b/src/nvme/fabrics.c @@ -30,6 +30,7 @@ #include <ccan/endian/endian.h> #include <ccan/list/list.h> #include <ccan/array_size/array_size.h> +#include <ccan/str/str.h> #include "fabrics.h" #include "linux.h" @@ -195,6 +196,37 @@ const char *nvmf_cms_str(__u8 cm) return arg_str(cms, ARRAY_SIZE(cms), cm); } +/* + * Derived from Linux's supported options (the opt_tokens table) + * when the mechanism to report supported options was added (f18ee3d988157). + * Not all of these options may actually be supported, + * but we retain the old behavior of passing all that might be. + */ +static const struct nvme_fabric_options default_supported_options = { + .ctrl_loss_tmo = true, + .data_digest = true, + .disable_sqflow = true, + .discovery = true, + .duplicate_connect = true, + .fast_io_fail_tmo = true, + .hdr_digest = true, + .host_iface = true, + .host_traddr = true, + .hostid = true, + .hostnqn = true, + .keep_alive_tmo = true, + .nqn = true, + .nr_io_queues = true, + .nr_poll_queues = true, + .nr_write_queues = true, + .queue_size = true, + .reconnect_delay = true, + .tos = true, + .traddr = true, + .transport = true, + .trsvcid = true, +}; + void nvmf_default_config(struct nvme_fabrics_config *cfg) { memset(cfg, 0, sizeof(*cfg)); @@ -261,7 +293,7 @@ void nvmf_update_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg) UPDATE_CFG_OPTION(ctrl_cfg, cfg, tls, false); } -static int add_bool_argument(char **argstr, char *tok, bool arg) +static int __add_bool_argument(char **argstr, char *tok, bool arg) { char *nstr; @@ -277,7 +309,7 @@ static int add_bool_argument(char **argstr, char *tok, bool arg) return 0; } -static int add_int_argument(char **argstr, char *tok, int arg, bool allow_zero) +static int __add_int_argument(char **argstr, char *tok, int arg, bool allow_zero) { char *nstr; @@ -293,7 +325,7 @@ static int add_int_argument(char **argstr, char *tok, int arg, bool allow_zero) return 0; } -static int add_int_or_minus_one_argument(char **argstr, char *tok, int arg) +static int __add_int_or_minus_one_argument(char **argstr, char *tok, int arg) { char *nstr; @@ -309,7 +341,7 @@ static int add_int_or_minus_one_argument(char **argstr, char *tok, int arg) return 0; } -static int add_argument(char **argstr, const char *tok, const char *arg) +static int __add_argument(char **argstr, const char *tok, const char *arg) { char *nstr; @@ -325,6 +357,71 @@ static int add_argument(char **argstr, const char *tok, const char *arg) return 0; } +#define add_bool_argument(o, argstr, tok, arg) \ +({ \ + int ret; \ + if (r->options->tok) { \ + ret = __add_bool_argument(argstr, \ + stringify(tok), \ + arg); \ + } else { \ + nvme_msg(r, LOG_DEBUG, \ + "option \"%s\" ignored\n", \ + stringify(tok)); \ + ret = 0; \ + } \ + ret; \ +}) + +#define add_int_argument(o, argstr, tok, arg, allow_zero) \ +({ \ + int ret; \ + if (r->options->tok) { \ + ret = __add_int_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_or_minus_one_argument(o, argstr, tok, arg) \ +({ \ + int ret; \ + if (r->options->tok) { \ + ret = __add_int_or_minus_one_argument(argstr, \ + stringify(tok), \ + arg); \ + } else { \ + nvme_msg(r, LOG_DEBUG, \ + "option \"%s\" ignored\n", \ + stringify(tok)); \ + ret = 0; \ + } \ + ret; \ +}) + +#define add_argument(r, argstr, tok, arg) \ +({ \ + int ret; \ + if (r->options->tok) { \ + ret = __add_argument(argstr, \ + stringify(tok), \ + arg); \ + } else { \ + nvme_msg(r, LOG_NOTICE, \ + "option \"%s\" ignored\n", \ + stringify(tok)); \ + ret = 0; \ + } \ + ret; \ +}) + static int inet4_pton(const char *src, uint16_t port, struct sockaddr_storage *addr) { @@ -453,6 +550,7 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) const char *transport = nvme_ctrl_get_transport(c); const char *hostnqn, *hostid, *hostkey, *ctrlkey; bool discover = false, discovery_nqn = false; + nvme_root_t r = h->r; if (!transport) { nvme_msg(h->r, LOG_ERR, "need a transport (-t) argument\n"); @@ -487,60 +585,60 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) if (!hostkey) hostkey = nvme_ctrl_get_dhchap_host_key(c); ctrlkey = nvme_ctrl_get_dhchap_key(c); - if (add_argument(argstr, "transport", transport) || - add_argument(argstr, "traddr", + if (add_argument(r, argstr, transport, transport) || + add_argument(r, argstr, traddr, nvme_ctrl_get_traddr(c)) || - add_argument(argstr, "host_traddr", + add_argument(r, argstr, host_traddr, cfg->host_traddr) || - add_argument(argstr, "host_iface", + add_argument(r, argstr, host_iface, cfg->host_iface) || - add_argument(argstr, "trsvcid", + add_argument(r, argstr, trsvcid, nvme_ctrl_get_trsvcid(c)) || - (hostnqn && add_argument(argstr, "hostnqn", hostnqn)) || - (hostid && add_argument(argstr, "hostid", hostid)) || + (hostnqn && add_argument(r, argstr, hostnqn, hostnqn)) || + (hostid && add_argument(r, argstr, hostid, hostid)) || (discover && !discovery_nqn && - add_bool_argument(argstr, "discovery", true)) || + add_bool_argument(r, argstr, discovery, true)) || (!discover && hostkey && - add_argument(argstr, "dhchap_secret", hostkey)) || + add_argument(r, argstr, dhchap_secret, hostkey)) || (!discover && ctrlkey && - add_argument(argstr, "dhchap_ctrl_secret", ctrlkey)) || + add_argument(r, argstr, dhchap_ctrl_secret, ctrlkey)) || (!discover && - add_int_argument(argstr, "nr_io_queues", + add_int_argument(r, argstr, nr_io_queues, cfg->nr_io_queues, false)) || (!discover && - add_int_argument(argstr, "nr_write_queues", + add_int_argument(r, argstr, nr_write_queues, cfg->nr_write_queues, false)) || (!discover && - add_int_argument(argstr, "nr_poll_queues", + add_int_argument(r, argstr, nr_poll_queues, cfg->nr_poll_queues, false)) || (!discover && - add_int_argument(argstr, "queue_size", + add_int_argument(r, argstr, queue_size, cfg->queue_size, false)) || - add_int_argument(argstr, "keep_alive_tmo", + add_int_argument(r, argstr, keep_alive_tmo, cfg->keep_alive_tmo, false) || - add_int_argument(argstr, "reconnect_delay", + add_int_argument(r, argstr, reconnect_delay, cfg->reconnect_delay, false) || (strcmp(transport, "loop") && - add_int_or_minus_one_argument(argstr, "ctrl_loss_tmo", + add_int_or_minus_one_argument(r, argstr, ctrl_loss_tmo, cfg->ctrl_loss_tmo)) || (strcmp(transport, "loop") && - add_int_argument(argstr, "fast_io_fail_tmo", + add_int_argument(r, argstr, fast_io_fail_tmo, cfg->fast_io_fail_tmo, false)) || (strcmp(transport, "loop") && - add_int_argument(argstr, "tos", cfg->tos, true)) || - add_int_argument(argstr, "keyring", cfg->keyring, false) || + add_int_argument(r, argstr, tos, cfg->tos, true)) || + add_int_argument(r, argstr, keyring, cfg->keyring, false) || (!strcmp(transport, "tcp") && - add_int_argument(argstr, "tls_key", cfg->tls_key, false)) || - add_bool_argument(argstr, "duplicate_connect", + add_int_argument(r, argstr, tls_key, cfg->tls_key, false)) || + add_bool_argument(r, argstr, duplicate_connect, cfg->duplicate_connect) || - add_bool_argument(argstr, "disable_sqflow", + add_bool_argument(r, argstr, disable_sqflow, cfg->disable_sqflow) || (!strcmp(transport, "tcp") && - add_bool_argument(argstr, "hdr_digest", cfg->hdr_digest)) || + add_bool_argument(r, argstr, hdr_digest, cfg->hdr_digest)) || (!strcmp(transport, "tcp") && - add_bool_argument(argstr, "data_digest", cfg->data_digest)) || + add_bool_argument(r, argstr, data_digest, cfg->data_digest)) || (!strcmp(transport, "tcp") && - add_bool_argument(argstr, "tls", cfg->tls))) { + add_bool_argument(r, argstr, tls, cfg->tls))) { free(*argstr); return -1; } @@ -548,6 +646,105 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) return 0; } +#define parse_option(r, v, name) \ + if (!strcmp(v, stringify(name))) { \ + r->options->name = true; \ + continue; \ + } + +static int __nvmf_supported_options(nvme_root_t r) +{ + char buf[0x1000], *options, *p, *v; + int fd, ret; + ssize_t len; + + if (r->options) + return 0; + + r->options = calloc(1, sizeof(*r->options)); + if (!r->options) + return -ENOMEM; + + fd = open(nvmf_dev, O_RDONLY); + if (fd < 0) { + nvme_msg(r, LOG_ERR, "Failed to open %s: %s\n", + nvmf_dev, strerror(errno)); + return -ENVME_CONNECT_OPEN; + } + + memset(buf, 0x0, sizeof(buf)); + len = read(fd, buf, sizeof(buf) - 1); + if (len < 0) { + if (errno == EINVAL) { + /* + * Older Linux kernels don't allow reading from nvmf_dev + * to get supported options, so use a default set + */ + nvme_msg(r, LOG_DEBUG, + "Cannot read %s, using default options\n", + nvmf_dev); + *r->options = default_supported_options; + ret = 0; + goto out_close; + } + + nvme_msg(r, LOG_ERR, "Failed to read from %s: %s\n", + nvmf_dev, strerror(errno)); + ret = -ENVME_CONNECT_READ; + goto out_close; + } + + buf[len] = '\0'; + options = buf; + + nvme_msg(r, LOG_DEBUG, "kernel supports: "); + + while ((p = strsep(&options, ",\n")) != NULL) { + if (!*p) + continue; + v = strsep(&p, "= "); + if (!v) + continue; + nvme_msg(r, LOG_DEBUG, "%s ", v); + + parse_option(r, v, cntlid); + parse_option(r, v, ctrl_loss_tmo); + parse_option(r, v, data_digest); + parse_option(r, v, dhchap_ctrl_secret); + parse_option(r, v, dhchap_secret); + parse_option(r, v, disable_sqflow); + parse_option(r, v, discovery); + parse_option(r, v, duplicate_connect); + parse_option(r, v, fast_io_fail_tmo); + parse_option(r, v, hdr_digest); + parse_option(r, v, host_iface); + parse_option(r, v, host_traddr); + parse_option(r, v, hostid); + parse_option(r, v, hostnqn); + parse_option(r, v, instance); + parse_option(r, v, keep_alive_tmo); + parse_option(r, v, keyring); + parse_option(r, v, nqn); + parse_option(r, v, nr_io_queues); + parse_option(r, v, nr_poll_queues); + parse_option(r, v, nr_write_queues); + parse_option(r, v, queue_size); + parse_option(r, v, reconnect_delay); + parse_option(r, v, tls); + parse_option(r, v, tls_key); + parse_option(r, v, tos); + parse_option(r, v, traddr); + parse_option(r, v, transport); + parse_option(r, v, trsvcid); + } + nvme_msg(r, LOG_DEBUG, "\n"); + ret = 0; + +out_close: + close(fd); + return ret; +} + static int __nvmf_add_ctrl(nvme_root_t r, const char *argstr) { int ret, fd, len = strlen(argstr); @@ -582,9 +779,12 @@ static int __nvmf_add_ctrl(nvme_root_t r, const char *argstr) case EOPNOTSUPP: ret = -ENVME_CONNECT_OPNOTSUPP; break; - case ECONNREFUSED : + case ECONNREFUSED: ret = -ENVME_CONNECT_CONNREFUSED; break; + case EADDRNOTAVAIL: + ret = -ENVME_CONNECT_ADDRNOTAVAIL; + break; default: ret = -ENVME_CONNECT_WRITE; break; @@ -622,6 +822,7 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, const struct nvme_fabrics_config *cfg) { nvme_subsystem_t s; + const char *root_app, *app; char *argstr; int ret; @@ -658,6 +859,41 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, } + root_app = nvme_root_get_application(h->r); + if (root_app) { + app = nvme_subsystem_get_application(s); + if (!app && nvme_ctrl_is_discovery_ctrl(c)) { + nvme_subsystem_t s; + nvme_ctrl_t fc; + + nvme_for_each_subsystem(h, s) { + fc = __nvme_lookup_ctrl(s, nvme_ctrl_get_transport(c), + nvme_ctrl_get_traddr(c), + NULL, + NULL, + nvme_ctrl_get_trsvcid(c), + NULL); + + if (fc) { + app = nvme_subsystem_get_application(s); + break; + } + } + } + /* + * configuration is managed by an application, + * refuse to act on subsystems which either have + * no application set or which habe a different + * application string. + */ + if (app && strcmp(app, root_app)) { + nvme_msg(h->r, LOG_INFO, "skip %s, not managed by %s\n", + nvme_subsystem_get_nqn(s), root_app); + errno = ENVME_CONNECT_IGNORED; + return -1; + } + } + nvme_ctrl_set_discovered(c, true); if (traddr_is_hostname(h->r, c)) { char *traddr = c->traddr; @@ -671,6 +907,9 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, free(traddr); } + ret = __nvmf_supported_options(h->r); + if (ret) + return ret; ret = build_options(h, c, &argstr); if (ret) return ret; @@ -836,9 +1075,10 @@ static struct nvmf_discovery_log *nvme_discovery_log(nvme_ctrl_t c, nvme_msg(r, LOG_DEBUG, "%s: get header (try %d/%d)\n", name, retries, max_retries); args->rae = true; + args->lpo = 0; args->len = size; args->log = log; - ret = nvme_get_log_page(fd, 4096, args); + ret = nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, args); if (ret) { nvme_msg(r, LOG_INFO, "%s: discover try %d/%d failed, error %d\n", @@ -865,15 +1105,33 @@ static struct nvmf_discovery_log *nvme_discovery_log(nvme_ctrl_t c, } nvme_msg(r, LOG_DEBUG, - "%s: get header and %" PRIu64 + "%s: get %" PRIu64 " records (length %d genctr %" PRIu64 ")\n", name, numrec, size, genctr); + args->rae = true; + args->lpo = sizeof(struct nvmf_discovery_log); + args->len = size - sizeof(struct nvmf_discovery_log); + args->log = log->entries; + ret = nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, args); + if (ret) { + nvme_msg(r, LOG_INFO, + "%s: discover try %d/%d failed, error %d\n", + name, retries, max_retries, errno); + goto out_free_log; + } + + /* + * If the log page was read with multiple Get Log Page commands, + * genctr must be checked afterwards to ensure atomicity + */ + nvme_msg(r, LOG_DEBUG, "%s: get header again\n", name); + args->rae = false; - args->len = size; + args->lpo = 0; + args->len = sizeof(struct nvmf_discovery_log); args->log = log; - ret = nvme_get_log_page(fd, 4096, args); - + ret = nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, args); if (ret) { nvme_msg(r, LOG_INFO, "%s: discover try %d/%d failed, error %d\n", @@ -888,7 +1146,8 @@ static struct nvmf_discovery_log *nvme_discovery_log(nvme_ctrl_t c, errno = EAGAIN; } else if (numrec != le64_to_cpu(log->numrec)) { nvme_msg(r, LOG_INFO, - "%s: could only fetch %" PRIu64 " of %" PRIu64 " records\n", + "%s: numrec changed unexpectedly " + "from %" PRIu64 " to %" PRIu64 "\n", name, numrec, le64_to_cpu(log->numrec)); errno = EBADSLT; } else { diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c index 2b5e09d..b9710b3 100644 --- a/src/nvme/ioctl.c +++ b/src/nvme/ioctl.c @@ -434,7 +434,7 @@ int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args) { __u64 offset = 0, xfer, data_len = args->len; __u64 start = args->lpo; - bool retain = true; + bool retain = args->rae; void *ptr = args->log; int ret; @@ -454,13 +454,10 @@ int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args) * last portion of this log page so the data remains latched * during the fetch sequence. */ - if (offset + xfer == data_len) - retain = args->rae; - args->lpo = start + offset; args->len = xfer; args->log = ptr; - args->rae = retain; + args->rae = offset + xfer < data_len || retain; ret = nvme_get_log(args); if (ret) return ret; @@ -1235,9 +1232,15 @@ int nvme_format_nvm(struct nvme_format_nvm_args *args) int nvme_ns_mgmt(struct nvme_ns_mgmt_args *args) { + const size_t size_v1 = sizeof_args(struct nvme_ns_mgmt_args, csi, __u64); + const size_t size_v2 = sizeof_args(struct nvme_ns_mgmt_args, data, __u64); __u32 cdw10 = NVME_SET(args->sel, NAMESPACE_MGMT_CDW10_SEL); __u32 cdw11 = NVME_SET(args->csi, NAMESPACE_MGMT_CDW11_CSI); - __u32 data_len = args->ns ? sizeof(*args->ns) : 0; + + if (args->args_size < size_v1 || args->args_size > size_v2) { + errno = EINVAL; + return -1; + } struct nvme_passthru_cmd cmd = { .nsid = args->nsid, @@ -1245,13 +1248,19 @@ int nvme_ns_mgmt(struct nvme_ns_mgmt_args *args) .cdw10 = cdw10, .cdw11 = cdw11, .timeout_ms = args->timeout, - .data_len = data_len, - .addr = (__u64)(uintptr_t)args->ns, }; - if (args->args_size < sizeof(*args)) { - errno = EINVAL; - return -1; + if (args->args_size == size_v2) { + if (args->data) { + cmd.data_len = sizeof(*args->data); + cmd.addr = (__u64)(uintptr_t)args->data; + } + } + else { + if (args->ns) { + cmd.data_len = sizeof(*args->ns); + cmd.addr = (__u64)(uintptr_t)args->ns; + } } return nvme_submit_admin_passthru(args->fd, &cmd, args->result); } @@ -1901,7 +1910,7 @@ int nvme_resv_report(struct nvme_resv_report_args *args) int nvme_io_mgmt_recv(struct nvme_io_mgmt_recv_args *args) { - __u32 cdw10 = (args->mo & 0xf) | (args->mos & 0xff << 16); + __u32 cdw10 = args->mo | (args->mos << 16); __u32 cdw11 = (args->data_len >> 2) - 1; struct nvme_passthru_cmd cmd = { @@ -1924,7 +1933,7 @@ int nvme_io_mgmt_recv(struct nvme_io_mgmt_recv_args *args) int nvme_io_mgmt_send(struct nvme_io_mgmt_send_args *args) { - __u32 cdw10 = (args->mo & 0xf) | ((args->mos & 0xff) << 16); + __u32 cdw10 = args->mo | (args->mos << 16); struct nvme_passthru_cmd cmd = { .opcode = nvme_cmd_io_mgmt_send, diff --git a/src/nvme/ioctl.h b/src/nvme/ioctl.h index 32e722e..4d843bc 100644 --- a/src/nvme/ioctl.h +++ b/src/nvme/ioctl.h @@ -3036,6 +3036,7 @@ int nvme_ns_mgmt(struct nvme_ns_mgmt_args *args); * @timeout: Override the default timeout to this value in milliseconds; * set to 0 to use the system default. * @csi: Command Set Identifier + * @data: Host Software Specified Fields that defines ns creation parameters * * On successful creation, the namespace exists in the subsystem, but is not * attached to any controller. Use the nvme_ns_attach_ctrls() to assign the @@ -3045,7 +3046,8 @@ int nvme_ns_mgmt(struct nvme_ns_mgmt_args *args); * &enum nvme_status_field) or -1 with errno set otherwise. */ static inline int nvme_ns_mgmt_create(int fd, struct nvme_id_ns *ns, - __u32 *nsid, __u32 timeout, __u8 csi) + __u32 *nsid, __u32 timeout, __u8 csi, + struct nvme_ns_mgmt_host_sw_specified *data) { struct nvme_ns_mgmt_args args = { .result = nsid, @@ -3056,6 +3058,9 @@ static inline int nvme_ns_mgmt_create(int fd, struct nvme_id_ns *ns, .nsid = NVME_NSID_NONE, .sel = NVME_NS_MGMT_SEL_CREATE, .csi = csi, + .rsvd1 = { 0, }, + .rsvd2 = NULL, + .data = data, }; return nvme_ns_mgmt(&args); @@ -3084,6 +3089,9 @@ static inline int nvme_ns_mgmt_delete(int fd, __u32 nsid) .nsid = nsid, .sel = NVME_NS_MGMT_SEL_DELETE, .csi = 0, + .rsvd1 = { 0, }, + .rsvd2 = NULL, + .data = NULL, }; return nvme_ns_mgmt(&args); diff --git a/src/nvme/json.c b/src/nvme/json.c index a74b5a4..7a5a69e 100644 --- a/src/nvme/json.c +++ b/src/nvme/json.c @@ -127,7 +127,7 @@ static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj) static void json_parse_subsys(nvme_host_t h, struct json_object *subsys_obj) { - struct json_object *nqn_obj, *port_array; + struct json_object *nqn_obj, *app_obj, *port_array; nvme_subsystem_t s; const char *nqn; int p; @@ -137,6 +137,12 @@ static void json_parse_subsys(nvme_host_t h, struct json_object *subsys_obj) return; nqn = json_object_get_string(nqn_obj); s = nvme_lookup_subsystem(h, NULL, nqn); + if (!s) + return; + app_obj = json_object_object_get(subsys_obj, "application"); + if (app_obj) + nvme_subsystem_set_application(s, json_object_get_string(app_obj)); + port_array = json_object_object_get(subsys_obj, "ports"); if (!port_array) return; @@ -187,18 +193,22 @@ static struct json_object *parse_json(nvme_root_t r, int fd) { char buf[JSON_FILE_BUF_SIZE]; struct json_object *obj = NULL; - struct printbuf *pb; + char *str = NULL; json_tokener *tok = NULL; int ret; + void *ptr = NULL; + int len = 0; + + while ((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { + str = realloc(ptr, len + ret); + if (!str) + goto out; + memcpy(&str[len], buf, ret); + len += ret; + ptr = str; + } - pb = printbuf_new(); - if (!pb) - return NULL; - - while ((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) - printbuf_memappend(pb, buf, ret); - - if (ret < 0) + if (ret < 0 || !len) goto out; tok = json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); @@ -208,14 +218,14 @@ static struct json_object *parse_json(nvme_root_t r, int fd) /* Enforce correctly formatted JSON */ tok->flags = JSON_TOKENER_STRICT; - obj = json_tokener_parse_ex(tok, pb->buf, printbuf_length(pb)); + obj = json_tokener_parse_ex(tok, str, len); if (!obj) nvme_msg(r, LOG_DEBUG, "JSON parsing failed: %s\n", json_util_get_last_err()); out: if (tok) json_tokener_free(tok); - printbuf_free(pb); + free(ptr); return obj; } @@ -350,7 +360,7 @@ static void json_update_subsys(struct json_object *subsys_array, nvme_subsystem_t s) { nvme_ctrl_t c; - const char *subsysnqn = nvme_subsystem_get_nqn(s); + const char *subsysnqn = nvme_subsystem_get_nqn(s), *app; struct json_object *subsys_obj = json_object_new_object(); struct json_object *port_array; @@ -360,6 +370,10 @@ static void json_update_subsys(struct json_object *subsys_array, json_object_object_add(subsys_obj, "nqn", json_object_new_string(subsysnqn)); + app = nvme_subsystem_get_application(s); + if (app) + json_object_object_add(subsys_obj, "application", + json_object_new_string(app)); port_array = json_object_new_array(); nvme_subsystem_for_each_ctrl(s, c) { json_update_port(port_array, c); diff --git a/src/nvme/mi.c b/src/nvme/mi.c index 391ba1a..3799f35 100644 --- a/src/nvme/mi.c +++ b/src/nvme/mi.c @@ -304,6 +304,11 @@ struct nvme_mi_ctrl *nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id) return ctrl; } +__u16 nvme_mi_ctrl_id(nvme_mi_ctrl_t ctrl) +{ + return ctrl->id; +} + int nvme_mi_scan_ep(nvme_mi_ep_t ep, bool force_rescan) { struct nvme_ctrl_list list; @@ -1077,14 +1082,19 @@ int nvme_mi_admin_set_features(nvme_mi_ctrl_t ctrl, int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl, struct nvme_ns_mgmt_args *args) { + const size_t size_v1 = sizeof_args(struct nvme_ns_mgmt_args, csi, __u64); + const size_t size_v2 = sizeof_args(struct nvme_ns_mgmt_args, data, __u64); struct nvme_mi_admin_resp_hdr resp_hdr; struct nvme_mi_admin_req_hdr req_hdr; struct nvme_mi_resp resp; struct nvme_mi_req req; int rc; + size_t data_len; - if (args->args_size < sizeof(*args)) - return -EINVAL; + if (args->args_size < size_v1 || args->args_size > size_v2) { + errno = EINVAL; + return -1; + } nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, nvme_admin_ns_mgmt); @@ -1092,10 +1102,23 @@ int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl, req_hdr.cdw1 = cpu_to_le32(args->nsid); req_hdr.cdw10 = cpu_to_le32(args->sel & 0xf); req_hdr.cdw11 = cpu_to_le32(args->csi << 24); - if (args->ns) { - req.data = args->ns; - req.data_len = sizeof(*args->ns); - req_hdr.dlen = cpu_to_le32(sizeof(*args->ns)); + + if (args->args_size == size_v2) { + if (args->data) { + req.data = args->data; + data_len = sizeof(*args->data); + } + } + else { + if (args->ns) { + req.data = args->ns; + data_len = sizeof(*args->ns); + } + } + + if (req.data) { + req.data_len = data_len; + req_hdr.dlen = cpu_to_le32(data_len); req_hdr.flags = 0x1; } diff --git a/src/nvme/mi.h b/src/nvme/mi.h index 12dbf6f..211cb29 100644 --- a/src/nvme/mi.h +++ b/src/nvme/mi.h @@ -655,6 +655,20 @@ nvme_mi_ctrl_t nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id); void nvme_mi_close_ctrl(nvme_mi_ctrl_t ctrl); /** + * nvme_mi_ctrl_id() - get the ID of a controller + * @ctrl: controller to query + * + * Retrieve the ID of the controller, as defined by hardware, and available + * in the Identify (Controller List) data. This is the value passed to + * @nvme_mi_init_ctrl, but may have been created internally via + * @nvme_mi_scan_ep. + * + * Return: the (locally-stored) ID of this controller. + */ +__u16 nvme_mi_ctrl_id(nvme_mi_ctrl_t ctrl); + + +/** * nvme_mi_endpoint_desc - Get a string describing a MI endpoint. * @ep: endpoint to describe * @@ -2451,6 +2465,7 @@ int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl, * @ns: New namespace parameters * @csi: Command Set Identifier for new NS * @nsid: Set to new namespace ID on create + * @data: Host Software Specified Fields that defines ns creation parameters * * Issues a Namespace Management (Create) command to @ctrl, to create a * new namespace specified by @ns, using command set @csi. On success, @@ -2460,8 +2475,8 @@ int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl, * &enum nvme_status_field) or -1 with errno set otherwise. */ static inline int nvme_mi_admin_ns_mgmt_create(nvme_mi_ctrl_t ctrl, - struct nvme_id_ns *ns, - __u8 csi, __u32 *nsid) + struct nvme_id_ns *ns, __u8 csi, __u32 *nsid, + struct nvme_ns_mgmt_host_sw_specified *data) { struct nvme_ns_mgmt_args args = { .result = nsid, @@ -2470,6 +2485,7 @@ static inline int nvme_mi_admin_ns_mgmt_create(nvme_mi_ctrl_t ctrl, .nsid = NVME_NSID_NONE, .sel = NVME_NS_MGMT_SEL_CREATE, .csi = csi, + .data = data, }; return nvme_mi_admin_ns_mgmt(ctrl, &args); diff --git a/src/nvme/nbft.c b/src/nvme/nbft.c new file mode 100644 index 0000000..a1e17cd --- /dev/null +++ b/src/nvme/nbft.c @@ -0,0 +1,726 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2021-2022, Dell Inc. or its subsidiaries. All Rights Reserved. + * + * Authors: Stuart Hayes <Stuart_Hayes@Dell.com> + * + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include <arpa/inet.h> +#include <ccan/endian/endian.h> + +#include "private.h" +#include "nbft.h" +#include "log.h" + + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +static __u8 csum(const __u8 *buffer, ssize_t length) +{ + int n; + __u8 sum = 0; + + for (n = 0; n < length; n++) + sum = (__u8)(sum + ((__u8 *)buffer)[n]); + return sum; +} + +static void format_ip_addr(char *buf, size_t buflen, __u8 *addr) +{ + struct in6_addr *addr_ipv6; + + addr_ipv6 = (struct in6_addr *)addr; + if (addr_ipv6->s6_addr32[0] == 0 && + addr_ipv6->s6_addr32[1] == 0 && + ntohl(addr_ipv6->s6_addr32[2]) == 0xffff) + /* ipv4 */ + inet_ntop(AF_INET, &(addr_ipv6->s6_addr32[3]), buf, buflen); + else + /* ipv6 */ + inet_ntop(AF_INET6, addr_ipv6, buf, buflen); +} + +static bool in_heap(struct nbft_header *header, struct nbft_heap_obj obj) +{ + if (le16_to_cpu(obj.length) == 0) + return true; + if (le32_to_cpu(obj.offset) < le32_to_cpu(header->heap_offset)) + return false; + if (le32_to_cpu(obj.offset) > + le32_to_cpu(header->heap_offset) + le32_to_cpu(header->heap_length)) + return false; + if (le32_to_cpu(obj.offset) + le16_to_cpu(obj.length) > + le32_to_cpu(header->heap_offset) + le32_to_cpu(header->heap_length)) + return false; + return true; +} + +/* + * Return transport_type string (NBFT Table 2) + */ +static char *trtype_to_string(__u8 transport_type) +{ + switch (transport_type) { + case 3: + return "tcp"; + default: + return "invalid"; + } +} + +#define verify(condition, message) \ + do { \ + if (!(condition)) { \ + nvme_msg(NULL, LOG_DEBUG, "file %s: " message "\n", \ + nbft->filename); \ + return -EINVAL; \ + } \ + } while (0) + +static int __get_heap_obj(struct nbft_header *header, const char *filename, + const char *descriptorname, const char *fieldname, + struct nbft_heap_obj obj, bool is_string, + char **output) +{ + if (le16_to_cpu(obj.length) == 0) + return -ENOENT; + + if (!in_heap(header, obj)) { + nvme_msg(NULL, LOG_DEBUG, + "file %s: field '%s' in descriptor '%s' has invalid offset or length\n", + filename, fieldname, descriptorname); + return -EINVAL; + } + + /* check that string is zero terminated correctly */ + *output = (char *)header + le32_to_cpu(obj.offset); + + if (is_string) { + if (strnlen(*output, le16_to_cpu(obj.length) + 1) < le16_to_cpu(obj.length)) + nvme_msg(NULL, LOG_DEBUG, + "file %s: string '%s' in descriptor '%s' is shorter (%zd) than specified length (%d)\n", + filename, fieldname, descriptorname, + strnlen(*output, le16_to_cpu(obj.length) + 1), + le16_to_cpu(obj.length)); + else if (strnlen(*output, le16_to_cpu(obj.length) + 1) > + le16_to_cpu(obj.length)) { + nvme_msg(NULL, LOG_DEBUG, + "file %s: string '%s' in descriptor '%s' is not zero terminated\n", + filename, fieldname, descriptorname); + return -EINVAL; + } + } + + return 0; +} + +#define get_heap_obj(descriptor, obj, is_string, output) \ + __get_heap_obj(header, nbft->filename, \ + stringify(descriptor), stringify(obj), \ + descriptor->obj, is_string, \ + output) + +static struct nbft_info_discovery *discovery_from_index(struct nbft_info *nbft, int i) +{ + struct nbft_info_discovery **d; + + for (d = nbft->discovery_list; d && *d; d++) { + if ((*d)->index == i) + return *d; + } + return NULL; +} + +static struct nbft_info_hfi *hfi_from_index(struct nbft_info *nbft, int i) +{ + struct nbft_info_hfi **h; + + for (h = nbft->hfi_list; h && *h; h++) { + if ((*h)->index == i) + return *h; + } + return NULL; +} + +static struct nbft_info_security *security_from_index(struct nbft_info *nbft, int i) +{ + struct nbft_info_security **s; + + for (s = nbft->security_list; s && *s; s++) { + if ((*s)->index == i) + return *s; + } + return NULL; +} + +static int read_ssns_exended_info(struct nbft_info *nbft, + struct nbft_info_subsystem_ns *ssns, + struct nbft_ssns_ext_info *raw_ssns_ei) +{ + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + + verify(raw_ssns_ei->structure_id == NBFT_DESC_SSNS_EXT_INFO, + "invalid ID in SSNS extended info descriptor"); + verify(raw_ssns_ei->version == 1, + "invalid version in SSNS extended info descriptor"); + verify(le16_to_cpu(raw_ssns_ei->ssns_index) == ssns->index, + "SSNS index doesn't match extended info descriptor index"); + + if (!(le32_to_cpu(raw_ssns_ei->flags) & NBFT_SSNS_EXT_INFO_VALID)) + return -EINVAL; + + if (le32_to_cpu(raw_ssns_ei->flags) & NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ) + ssns->asqsz = le16_to_cpu(raw_ssns_ei->asqsz); + ssns->controller_id = le16_to_cpu(raw_ssns_ei->cntlid); + get_heap_obj(raw_ssns_ei, dhcp_root_path_str_obj, 1, &ssns->dhcp_root_path_string); + + return 0; +} + +static int read_ssns(struct nbft_info *nbft, + struct nbft_ssns *raw_ssns, + struct nbft_info_subsystem_ns **s) +{ + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + struct nbft_info_subsystem_ns *ssns; + __u8 *ss_hfi_indexes = NULL; + __u8 *tmp = NULL; + int i, ret; + + if (!(le16_to_cpu(raw_ssns->flags) & NBFT_SSNS_VALID)) + return -EINVAL; + + verify(raw_ssns->structure_id == NBFT_DESC_SSNS, + "invalid ID in SSNS descriptor"); + + ssns = calloc(1, sizeof(*ssns)); + if (!ssns) + return -ENOMEM; + + ssns->index = le16_to_cpu(raw_ssns->index); + + /* transport type */ + verify(raw_ssns->trtype == NBFT_TRTYPE_TCP, + "invalid transport type in SSNS descriptor"); + strncpy(ssns->transport, trtype_to_string(raw_ssns->trtype), sizeof(ssns->transport)); + + /* transport specific flags */ + if (raw_ssns->trtype == NBFT_TRTYPE_TCP) { + if (le16_to_cpu(raw_ssns->trflags) & NBFT_SSNS_PDU_HEADER_DIGEST) + ssns->pdu_header_digest_required = true; + if (le16_to_cpu(raw_ssns->trflags) & NBFT_SSNS_DATA_DIGEST) + ssns->data_digest_required = true; + } + + /* primary discovery controller */ + if (raw_ssns->primary_discovery_ctrl_index) { + ssns->discovery = discovery_from_index(nbft, + raw_ssns->primary_discovery_ctrl_index); + if (!ssns->discovery) + nvme_msg(NULL, LOG_DEBUG, + "file %s: namespace %d discovery controller not found\n", + nbft->filename, ssns->index); + } + + /* subsystem transport address */ + ret = get_heap_obj(raw_ssns, subsys_traddr_obj, 0, (char **)&tmp); + if (ret) + goto fail; + + format_ip_addr(ssns->traddr, sizeof(ssns->traddr), tmp); + + /* subsystem transport service identifier */ + ret = get_heap_obj(raw_ssns, subsys_trsvcid_obj, 1, &ssns->trsvcid); + if (ret) + goto fail; + + /* subsystem port ID */ + ssns->subsys_port_id = le16_to_cpu(raw_ssns->subsys_port_id); + + /* NSID, NID type, & NID */ + ssns->nsid = le32_to_cpu(raw_ssns->nsid); + ssns->nid_type = raw_ssns->nidt; + ssns->nid = raw_ssns->nid; + + /* security profile */ + if (raw_ssns->security_desc_index) { + ssns->security = security_from_index(nbft, raw_ssns->security_desc_index); + if (!ssns->security) + nvme_msg(NULL, LOG_DEBUG, + "file %s: namespace %d security controller not found\n", + nbft->filename, ssns->index); + } + + /* HFI descriptors */ + ret = get_heap_obj(raw_ssns, secondary_hfi_assoc_obj, 0, (char **)&ss_hfi_indexes); + if (ret) + goto fail; + + ssns->hfis = calloc(le16_to_cpu(raw_ssns->secondary_hfi_assoc_obj.length) + 2, + sizeof(*ssns->hfis)); + if (!ssns->hfis) { + ret = -ENOMEM; + goto fail; + } + ssns->hfis[0] = hfi_from_index(nbft, raw_ssns->primary_hfi_desc_index); + if (!ssns->hfis[0]) { + nvme_msg(NULL, LOG_DEBUG, + "file %s: SSNS %d: HFI %d not found\n", + nbft->filename, ssns->index, raw_ssns->primary_hfi_desc_index); + ret = -EINVAL; + goto fail; + } + for (i = 0; i < le16_to_cpu(raw_ssns->secondary_hfi_assoc_obj.length); i++) { + ssns->hfis[i + 1] = hfi_from_index(nbft, ss_hfi_indexes[i]); + if (ss_hfi_indexes[i] && !ssns->hfis[i + 1]) + nvme_msg(NULL, LOG_DEBUG, + "file %s: SSNS %d HFI %d not found\n", + nbft->filename, ssns->index, ss_hfi_indexes[i]); + else + ssns->num_hfis++; + } + + /* SSNS NQN */ + ret = get_heap_obj(raw_ssns, subsys_ns_nqn_obj, 1, &ssns->subsys_nqn); + if (ret) + goto fail; + + /* SSNS extended info */ + if (le16_to_cpu(raw_ssns->flags) & NBFT_SSNS_EXTENDED_INFO_IN_USE) { + struct nbft_ssns_ext_info *ssns_extended_info; + + if (!get_heap_obj(raw_ssns, ssns_extended_info_desc_obj, 0, + (char **)&ssns_extended_info)) + read_ssns_exended_info(nbft, ssns, ssns_extended_info); + } + + *s = ssns; + return 0; + +fail: + free(ssns); + return ret; +} + +static int read_hfi_info_tcp(struct nbft_info *nbft, + struct nbft_hfi_info_tcp *raw_hfi_info_tcp, + struct nbft_info_hfi *hfi) +{ + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + + if ((raw_hfi_info_tcp->flags & NBFT_HFI_INFO_TCP_VALID) == 0) + return -EINVAL; + + verify(raw_hfi_info_tcp->structure_id == NBFT_DESC_HFI_TRINFO, + "invalid ID in HFI transport descriptor"); + verify(raw_hfi_info_tcp->version == 1, + "invalid version in HFI transport descriptor"); + if (le16_to_cpu(raw_hfi_info_tcp->hfi_index) != hfi->index) + nvme_msg(NULL, LOG_DEBUG, + "file %s: HFI descriptor index %d does not match index in HFI transport descriptor\n", + nbft->filename, hfi->index); + + hfi->tcp_info.pci_sbdf = le32_to_cpu(raw_hfi_info_tcp->pci_sbdf); + memcpy(hfi->tcp_info.mac_addr, raw_hfi_info_tcp->mac_addr, + sizeof(raw_hfi_info_tcp->mac_addr)); + hfi->tcp_info.vlan = le16_to_cpu(raw_hfi_info_tcp->vlan); + hfi->tcp_info.ip_origin = raw_hfi_info_tcp->ip_origin; + format_ip_addr(hfi->tcp_info.ipaddr, sizeof(hfi->tcp_info.ipaddr), + raw_hfi_info_tcp->ip_address); + hfi->tcp_info.subnet_mask_prefix = raw_hfi_info_tcp->subnet_mask_prefix; + format_ip_addr(hfi->tcp_info.gateway_ipaddr, sizeof(hfi->tcp_info.ipaddr), + raw_hfi_info_tcp->ip_gateway); + hfi->tcp_info.route_metric = le16_to_cpu(raw_hfi_info_tcp->route_metric); + format_ip_addr(hfi->tcp_info.primary_dns_ipaddr, + sizeof(hfi->tcp_info.primary_dns_ipaddr), + raw_hfi_info_tcp->primary_dns); + format_ip_addr(hfi->tcp_info.secondary_dns_ipaddr, + sizeof(hfi->tcp_info.secondary_dns_ipaddr), + raw_hfi_info_tcp->secondary_dns); + if (raw_hfi_info_tcp->flags & NBFT_HFI_INFO_TCP_DHCP_OVERRIDE) { + hfi->tcp_info.dhcp_override = true; + format_ip_addr(hfi->tcp_info.dhcp_server_ipaddr, + sizeof(hfi->tcp_info.dhcp_server_ipaddr), + raw_hfi_info_tcp->dhcp_server); + } + get_heap_obj(raw_hfi_info_tcp, host_name_obj, 1, &hfi->tcp_info.host_name); + if (raw_hfi_info_tcp->flags & NBFT_HFI_INFO_TCP_GLOBAL_ROUTE) + hfi->tcp_info.this_hfi_is_default_route = true; + + return 0; +} + +static int read_hfi(struct nbft_info *nbft, + struct nbft_hfi *raw_hfi, + struct nbft_info_hfi **h) +{ + int ret; + struct nbft_info_hfi *hfi; + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + + if (!(raw_hfi->flags & NBFT_HFI_VALID)) + return -EINVAL; + + verify(raw_hfi->structure_id == NBFT_DESC_HFI, + "invalid ID in HFI descriptor"); + + hfi = calloc(1, sizeof(struct nbft_info_hfi)); + if (!hfi) + return -ENOMEM; + + hfi->index = raw_hfi->index; + + /* + * read HFI transport descriptor for this HFI + */ + if (raw_hfi->trtype == NBFT_TRTYPE_TCP) { + /* TCP */ + struct nbft_hfi_info_tcp *raw_hfi_info_tcp; + + strncpy(hfi->transport, trtype_to_string(raw_hfi->trtype), + sizeof(hfi->transport)); + + ret = get_heap_obj(raw_hfi, trinfo_obj, 0, (char **)&raw_hfi_info_tcp); + if (ret) + goto fail; + + ret = read_hfi_info_tcp(nbft, raw_hfi_info_tcp, hfi); + if (ret) + goto fail; + } else { + nvme_msg(NULL, LOG_DEBUG, + "file %s: invalid transport type %d\n", + nbft->filename, raw_hfi->trtype); + ret = -EINVAL; + goto fail; + } + + *h = hfi; + return 0; + +fail: + free(hfi); + return ret; +} + +static int read_discovery(struct nbft_info *nbft, + struct nbft_discovery *raw_discovery, + struct nbft_info_discovery **d) +{ + struct nbft_info_discovery *discovery; + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + + if (!(raw_discovery->flags & NBFT_DISCOVERY_VALID)) + return -EINVAL; + + verify(raw_discovery->structure_id == NBFT_DESC_DISCOVERY, + "invalid ID in discovery descriptor"); + + discovery = calloc(1, sizeof(struct nbft_info_discovery)); + if (!discovery) + return -ENOMEM; + + discovery->index = raw_discovery->index; + + if (get_heap_obj(raw_discovery, discovery_ctrl_addr_obj, 1, &discovery->uri)) + return -EINVAL; + + if (get_heap_obj(raw_discovery, discovery_ctrl_nqn_obj, 1, &discovery->nqn)) + return -EINVAL; + + discovery->hfi = hfi_from_index(nbft, raw_discovery->hfi_index); + if (raw_discovery->hfi_index && !discovery->hfi) + nvme_msg(NULL, LOG_DEBUG, + "file %s: discovery %d HFI not found\n", + nbft->filename, discovery->index); + + discovery->security = security_from_index(nbft, raw_discovery->sec_index); + if (raw_discovery->sec_index && !discovery->security) + nvme_msg(NULL, LOG_DEBUG, + "file %s: discovery %d security descriptor not found\n", + nbft->filename, discovery->index); + + *d = discovery; + return 0; +} + +static int read_security(struct nbft_info *nbft, + struct nbft_security *raw_security, + struct nbft_info_security **s) +{ + return -EINVAL; +} + +static void read_hfi_descriptors(struct nbft_info *nbft, int num_hfi, + struct nbft_hfi *raw_hfi_array, int hfi_len) +{ + int i, cnt; + + nbft->hfi_list = calloc(num_hfi + 1, sizeof(struct nbft_info_hfi)); + for (i = 0, cnt = 0; i < num_hfi; i++) { + if (read_hfi(nbft, &raw_hfi_array[i], &nbft->hfi_list[cnt]) == 0) + cnt++; + } +} + +static void read_security_descriptors(struct nbft_info *nbft, int num_sec, + struct nbft_security *raw_sec_array, int sec_len) +{ + int i, cnt; + + nbft->security_list = calloc(num_sec + 1, sizeof(struct nbft_info_security)); + for (i = 0, cnt = 0; i < num_sec; i++) { + if (read_security(nbft, &raw_sec_array[i], &nbft->security_list[cnt]) == 0) + cnt++; + } +} + +static void read_discovery_descriptors(struct nbft_info *nbft, int num_disc, + struct nbft_discovery *raw_disc_array, int disc_len) +{ + int i, cnt; + + nbft->discovery_list = calloc(num_disc + 1, sizeof(struct nbft_info_discovery)); + for (i = 0, cnt = 0; i < num_disc; i++) { + if (read_discovery(nbft, &raw_disc_array[i], &nbft->discovery_list[cnt]) == 0) + cnt++; + } +} + +static void read_ssns_descriptors(struct nbft_info *nbft, int num_ssns, + struct nbft_ssns *raw_ssns_array, int ssns_len) +{ + int i, cnt; + + nbft->subsystem_ns_list = calloc(num_ssns + 1, sizeof(struct nbft_info_subsystem_ns)); + for (i = 0, cnt = 0; i < num_ssns; i++) { + if (read_ssns(nbft, &raw_ssns_array[i], &nbft->subsystem_ns_list[cnt]) == 0) + cnt++; + } +} + +/** + * parse_raw_nbft - parses raw ACPI NBFT table and fill in abstracted nbft_info structure + * @nbft: nbft_info struct containing only raw_nbft and raw_nbft_size + * + * Returns 0 on success, errno otherwise. + */ +static int parse_raw_nbft(struct nbft_info *nbft) +{ + __u8 *raw_nbft = nbft->raw_nbft; + int raw_nbft_size = nbft->raw_nbft_size; + + struct nbft_header *header; + struct nbft_control *control; + struct nbft_host *host; + + verify(raw_nbft_size >= sizeof(struct nbft_header) + sizeof(struct nbft_control), + "table is too short"); + verify(csum(raw_nbft, raw_nbft_size) == 0, "invalid checksum"); + + /* + * header + */ + header = (struct nbft_header *)raw_nbft; + + verify(strncmp(header->signature, NBFT_HEADER_SIG, 4) == 0, "invalid signature"); + verify(le32_to_cpu(header->length) <= raw_nbft_size, "length in header exceeds table length"); + verify(header->major_revision == 1, "unsupported major revision"); + verify(header->minor_revision == 0, "unsupported minor revision"); + verify(le32_to_cpu(header->heap_length) + le32_to_cpu(header->heap_offset) <= + le32_to_cpu(header->length), "heap exceeds table length"); + + /* + * control + */ + control = (struct nbft_control *)(raw_nbft + sizeof(struct nbft_header)); + + if ((control->flags & NBFT_CONTROL_VALID) == 0) + return 0; + verify(control->structure_id == NBFT_DESC_CONTROL, + "invalid ID in control structure"); + + /* + * host + */ + verify(le32_to_cpu(control->hdesc.offset) + sizeof(struct nbft_host) <= + le32_to_cpu(header->length) && + le32_to_cpu(control->hdesc.offset) >= sizeof(struct nbft_host), + "host descriptor offset/length is invalid"); + host = (struct nbft_host *)(raw_nbft + le32_to_cpu(control->hdesc.offset)); + + verify(host->flags & NBFT_HOST_VALID, "host descriptor valid flag not set"); + verify(host->structure_id == NBFT_DESC_HOST, "invalid ID in HOST descriptor"); + nbft->host.id = (unsigned char *) &(host->host_id); + if (get_heap_obj(host, host_nqn_obj, 1, &nbft->host.nqn) != 0) + return -EINVAL; + nbft->host.host_id_configured = host->flags & NBFT_HOST_HOSTID_CONFIGURED; + nbft->host.host_nqn_configured = host->flags & NBFT_HOST_HOSTNQN_CONFIGURED; + + /* + * HFI + */ + if (control->num_hfi > 0) { + struct nbft_hfi *raw_hfi_array; + + verify(le32_to_cpu(control->hfio) + sizeof(struct nbft_hfi) * + control->num_hfi <= le32_to_cpu(header->length), + "invalid hfi descriptor list offset"); + raw_hfi_array = (struct nbft_hfi *)(raw_nbft + le32_to_cpu(control->hfio)); + read_hfi_descriptors(nbft, control->num_hfi, raw_hfi_array, + le16_to_cpu(control->hfil)); + } + + /* + * security + */ + if (control->num_sec > 0) { + struct nbft_security *raw_security_array; + + verify(le32_to_cpu(control->seco) + le16_to_cpu(control->secl) * + control->num_sec <= le32_to_cpu(header->length), + "invalid security profile desciptor list offset"); + raw_security_array = (struct nbft_security *)(raw_nbft + + le32_to_cpu(control->seco)); + read_security_descriptors(nbft, control->num_sec, + raw_security_array, + le16_to_cpu(control->secl)); + } + + /* + * discovery + */ + if (control->num_disc > 0) { + struct nbft_discovery *raw_discovery_array; + + verify(le32_to_cpu(control->disco) + le16_to_cpu(control->discl) * + control->num_disc <= le32_to_cpu(header->length), + "invalid discovery profile descriptor list offset"); + raw_discovery_array = (struct nbft_discovery *)(raw_nbft + + le32_to_cpu(control->disco)); + read_discovery_descriptors(nbft, control->num_disc, raw_discovery_array, + le16_to_cpu(control->discl)); + } + + /* + * subsystem namespace + */ + if (control->num_ssns > 0) { + struct nbft_ssns *raw_ssns_array; + + verify(le32_to_cpu(control->ssnso) + le16_to_cpu(control->ssnsl) * + control->num_ssns <= le32_to_cpu(header->length), + "invalid subsystem namespace descriptor list offset"); + raw_ssns_array = (struct nbft_ssns *)(raw_nbft + + le32_to_cpu(control->ssnso)); + read_ssns_descriptors(nbft, control->num_ssns, raw_ssns_array, + le16_to_cpu(control->ssnsl)); + } + + return 0; +} + +void nvme_nbft_free(struct nbft_info *nbft) +{ + struct nbft_info_hfi **hfi; + struct nbft_info_security **sec; + struct nbft_info_discovery **disc; + struct nbft_info_subsystem_ns **ns; + + for (hfi = nbft->hfi_list; hfi && *hfi; hfi++) + free(*hfi); + free(nbft->hfi_list); + for (disc = nbft->discovery_list; disc && *disc; disc++) + free(*disc); + free(nbft->discovery_list); + for (sec = nbft->security_list; sec && *sec; sec++) + free(*sec); + free(nbft->security_list); + for (ns = nbft->subsystem_ns_list; ns && *ns; ns++) { + free((*ns)->hfis); + free(*ns); + } + free(nbft->subsystem_ns_list); + free(nbft->raw_nbft); + free(nbft->filename); + free(nbft); +} + +int nvme_nbft_read(struct nbft_info **nbft, const char *filename) +{ + __u8 *raw_nbft = NULL; + size_t raw_nbft_size; + FILE *raw_nbft_fp = NULL; + int i; + + /* + * read in raw nbft file + */ + raw_nbft_fp = fopen(filename, "rb"); + if (raw_nbft_fp == NULL) { + nvme_msg(NULL, LOG_ERR, "Failed to open %s: %s\n", + filename, strerror(errno)); + errno = EINVAL; + return 1; + } + + i = fseek(raw_nbft_fp, 0L, SEEK_END); + if (i) { + nvme_msg(NULL, LOG_ERR, "Failed to read from %s: %s\n", + filename, strerror(errno)); + fclose(raw_nbft_fp); + errno = EINVAL; + return 1; + } + + raw_nbft_size = ftell(raw_nbft_fp); + rewind(raw_nbft_fp); + + raw_nbft = malloc(raw_nbft_size); + if (!raw_nbft) { + nvme_msg(NULL, LOG_ERR, "Failed to allocate memory for NBFT table"); + fclose(raw_nbft_fp); + errno = ENOMEM; + return 1; + } + + i = fread(raw_nbft, sizeof(*raw_nbft), raw_nbft_size, raw_nbft_fp); + if (i != raw_nbft_size) { + nvme_msg(NULL, LOG_ERR, "Failed to read from %s: %s\n", + filename, strerror(errno)); + fclose(raw_nbft_fp); + free(raw_nbft); + errno = EINVAL; + return 1; + } + fclose(raw_nbft_fp); + + /* + * alloc new struct nbft_info, add raw nbft & filename to it, and add it to the list + */ + *nbft = calloc(1, sizeof(struct nbft_info)); + if (!*nbft) { + nvme_msg(NULL, LOG_ERR, "Could not allocate memory for NBFT\n"); + free(raw_nbft); + errno = ENOMEM; + return 1; + } + + (*nbft)->filename = strdup(filename); + (*nbft)->raw_nbft = raw_nbft; + (*nbft)->raw_nbft_size = raw_nbft_size; + + if (parse_raw_nbft(*nbft)) { + nvme_msg(NULL, LOG_ERR, "Failed to parse %s\n", filename); + nvme_nbft_free(*nbft); + errno = EINVAL; + return 1; + } + return 0; +} diff --git a/src/nvme/nbft.h b/src/nvme/nbft.h new file mode 100644 index 0000000..6012e16 --- /dev/null +++ b/src/nvme/nbft.h @@ -0,0 +1,1238 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2021-2022, Dell Inc. or its subsidiaries. All Rights Reserved. + * + * Authors: Stuart Hayes <Stuart_Hayes@Dell.com> + * + */ +#ifndef _NBFT_H +#define _NBFT_H + +#include <sys/types.h> +#include "util.h" + +/* + * ACPI NBFT table structures (TP8012 Boot Specification rev. 1.0) + */ + +/** + * enum nbft_desc_type - NBFT Elements - Descriptor Types (Figure 5) + * @NBFT_DESC_HEADER: Header: an ACPI structure header with some additional + * NBFT specific info. + * @NBFT_DESC_CONTROL: Control Descriptor: indicates the location of host, + * HFI, SSNS, security, and discovery descriptors. + * @NBFT_DESC_HOST: Host Descriptor: host information. + * @NBFT_DESC_HFI: HFI Descriptor: an indexable table of HFI Descriptors, + * one for each fabric interface on the host. + * @NBFT_DESC_SSNS: Subsystem Namespace Descriptor: an indexable table + * of SSNS Descriptors. + * @NBFT_DESC_SECURITY: Security Descriptor: an indexable table of Security + * descriptors. + * @NBFT_DESC_DISCOVERY: Discovery Descriptor: an indexable table of Discovery + * Descriptors. + * @NBFT_DESC_HFI_TRINFO: HFI Transport Descriptor: indicated by an HFI Descriptor, + * corresponds to a specific transport for a single HFI. + * @NBFT_DESC_RESERVED_8: Reserved. + * @NBFT_DESC_SSNS_EXT_INFO: SSNS Extended Info Descriptor: indicated by an SSNS + * Descriptor if required. + */ +enum nbft_desc_type { + NBFT_DESC_HEADER = 0, + NBFT_DESC_CONTROL = 1, + NBFT_DESC_HOST = 2, + NBFT_DESC_HFI = 3, + NBFT_DESC_SSNS = 4, + NBFT_DESC_SECURITY = 5, + NBFT_DESC_DISCOVERY = 6, + NBFT_DESC_HFI_TRINFO = 7, + NBFT_DESC_RESERVED_8 = 8, + NBFT_DESC_SSNS_EXT_INFO = 9, +}; + +/** + * enum nbft_trtype - NBFT Interface Transport Types (Figure 7) + * @NBFT_TRTYPE_TCP: NVMe/TCP (802.3 + TCP/IP). String Designator "tcp". + */ +enum nbft_trtype { + NBFT_TRTYPE_TCP = 3, +}; + +#define NBFT_HEADER_SIG "NBFT" + +/** + * struct nbft_heap_obj - NBFT Header Driver Signature + * @offset: Offset in bytes of the heap object, if any, from byte offset 0h + * of the NBFT Table Header. + * @length: Length in bytes of the heap object, if any. + */ +struct nbft_heap_obj { + __le32 offset; + __le16 length; +} __attribute__((packed)); + +/** + * struct nbft_header - NBFT Table - Header (Figure 8) + * @signature: Signature: An ASCII string representation of the table + * identifier. This field shall be set to the value 4E424654h + * (i.e. "NBFT", see #NBFT_HEADER_SIG). + * @length: Length: The length of the table, in bytes, including the + * header, starting from offset 0h. This field is used to record + * the size of the entire table. + * @major_revision: Major Revision: The major revision of the structure + * corresponding to the Signature field. Larger major revision + * numbers should not be assumed backward compatible to lower + * major revision numbers with the same signature. + * @checksum: Checksum: The entire table, including the Checksum field, + * shall sum to 0h to be considered valid. + * @oem_id: OEMID shall be populated by the NBFT driver writer by + * an OEM-supplied string that identifies the OEM. All + * trailing bytes shall be NULL. + * @oem_table_id: OEM Table ID: This field shall be populated by the NBFT + * driver writer with an OEM-supplied string that the OEM + * uses to identify the particular data table. This field is + * particularly useful when defining a definition block to + * distinguish definition block functions. The OEM assigns + * each dissimilar table a new OEM Table ID. + * @oem_revision: OEM Revision: An OEM-supplied revision number. Larger + * numbers are assumed to be newer revisions. + * @creator_id: Creator ID: Vendor ID of utility that created the table. + * For instance, this may be the ID for the ASL Compiler. + * @creator_revision: Creator Revision: Revision of utility that created the + * table. For instance, this may be the ID for the ASL Compiler. + * @heap_offset: Heap Offset (HO): This field indicates the offset in bytes + * of the heap, if any, from byte offset 0h of the NBFT + * Table Header. + * @heap_length: Heap Length (HL): The length of the heap, if any. + * @driver_dev_path_sig: Driver Signature Heap Object Reference: This field indicates + * the offset in bytes of a heap object containing the Driver + * Signature, if any, from byte offset 0h of the NBFT Table + * Header. + * @minor_revision: Minor Revision: The minor revision of the structure + * corresponding to the Signature field. If the major revision + * numbers are the same, any minor revision number differences + * shall be backwards compatible with the same signature. + * @reserved: Reserved. + */ +struct nbft_header { + char signature[4]; + __le32 length; + __u8 major_revision; + __u8 checksum; + char oem_id[6]; + char oem_table_id[8]; + __le32 oem_revision; + __le32 creator_id; + __le32 creator_revision; + __le32 heap_offset; + __le32 heap_length; + struct nbft_heap_obj driver_dev_path_sig; + __u8 minor_revision; + __u8 reserved[13]; +}; + +/** + * struct nbft_control - NBFT Table - Control Descriptor (Figure 8) + * @structure_id: Structure ID: This field specifies the element (refer to + * &enum nbft_desc_type). This field shall be set to 1h (i.e., + * Control, #NBFT_DESC_CONTROL). + * @major_revision: Major Revision: The major revision of the structure corresponding + * to the Signature field. Larger major revision numbers should + * not be assumed backward compatible to lower major revision + * numbers with the same signature. + * @minor_revision: Minor Revision: The minor revision of the structure corresponding + * to the signature field. If the major revision numbers are + * the same, any minor revision number differences shall be backwards + * compatible with the same signature. + * @reserved1: Reserved. + * @csl: Control Structure Length (CSL): This field indicates the length + * in bytes of the Control Descriptor. + * @flags: Flags, see &enum nbft_control_flags. + * @reserved2: Reserved. + * @hdesc: Host Descriptor (HDESC): This field indicates the location + * and length of the Host Descriptor (see &struct nbft_host). + * @hsv: Host Descriptor Version (HSV): This field indicates the version + * of the Host Descriptor. + * @reserved3: Reserved. + * @hfio: HFI Descriptor List Offset (HFIO): If this field is set to + * a non-zero value, then this field indicates the offset in bytes + * of the HFI Descriptor List, if any, from byte offset 0h of the + * NBFT Table Header. If the @num_hfi field is cleared to 0h, + * then this field is reserved. + * @hfil: HFI Descriptor Length (HFIL): This field indicates the length + * in bytes of each HFI Descriptor, if any. If the @num_hfi field + * is cleared to 0h, then this field is reserved. + * @hfiv: HFI Descriptor Version (HFIV): This field indicates the version + * of each HFI Descriptor. + * @num_hfi: Number of Host Fabric Interface Descriptors (NumHFI): This field + * indicates the number of HFI Descriptors (see &struct nbft_hfi) + * in the HFI Descriptor List, if any. If no interfaces have been + * configured, then this field shall be cleared to 0h. + * @ssnso: SSNS Descriptor List Offset (SSNSO):: This field indicates + * the offset in bytes of the SSNS Descriptor List, if any, from + * byte offset 0h of the NBFT Table Header. If the @num_ssns field + * is cleared to 0h, then this field is reserved. + * @ssnsl: SSNS Descriptor Length (SSNSL): This field indicates the length + * in bytes of each SSNS Descriptor, if any. If the @num_ssns + * field is cleared to 0h, then this field is reserved. + * @ssnsv: SSNS Descriptor Version (SSNSV): This field indicates the version + * of the SSNS Descriptor. + * @num_ssns: Number of Subsystem and Namespace Descriptors (NumSSNS): This + * field indicates the number of Subsystem Namespace (SSNS) + * Descriptors (see &struct nbft_ssns) in the SSNS Descriptor List, + * if any. + * @seco: Security Profile Descriptor List Offset (SECO): This field + * indicates the offset in bytes of the Security Profile Descriptor + * List, if any, from byte offset 0h of the NBFT Table Header. + * If the @num_sec field is cleared to 0h, then this field + * is reserved. + * @secl: Security Profile Descriptor Length (SECL): This field indicates + * the length in bytes of each Security Profile Descriptor, if any. + * If the @num_sec field is cleared to 0h, then this field + * is reserved. + * @secv: Security Profile Descriptor Version (SECV): This field indicates + * the version of the Security Profile Descriptor. + * @num_sec: Number of Security Profile Descriptors (NumSec): This field + * indicates the number of Security Profile Descriptors + * (see &struct nbft_security), if any, in the Security Profile + * Descriptor List. + * @disco: Discovery Descriptor Offset (DISCO): This field indicates + * the offset in bytes of the Discovery Descriptor List, if any, + * from byte offset 0h of the NBFT Table Header. If the @num_disc + * field is cleared to 0h, then this field is reserved. + * @discl: Discovery Descriptor Length (DISCL): This field indicates + * the length in bytes of each Discovery Descriptor, if any. + * If the @num_disc field is cleared to 0h, then this field + * is reserved. + * @discv: Discovery Descriptor Version (DISCV): This field indicates + * the version of the Discovery Descriptor. + * @num_disc: Number of Discovery Descriptors (NumDisc): This field indicates + * the number of Discovery Descriptors (see &struct nbft_discovery), + * if any, in the Discovery Descriptor List, if any. + * @reserved4: Reserved. + */ +struct nbft_control { + __u8 structure_id; + __u8 major_revision; + __u8 minor_revision; + __u8 reserved1; + __le16 csl; + __u8 flags; + __u8 reserved2; + struct nbft_heap_obj hdesc; + __u8 hsv; + __u8 reserved3; + __le32 hfio; + __le16 hfil; + __u8 hfiv; + __u8 num_hfi; + __le32 ssnso; + __le16 ssnsl; + __u8 ssnsv; + __u8 num_ssns; + __le32 seco; + __le16 secl; + __u8 secv; + __u8 num_sec; + __le32 disco; + __le16 discl; + __u8 discv; + __u8 num_disc; + __u8 reserved4[16]; +}; + +/** + * enum nbft_control_flags - Control Descriptor Flags + * @NBFT_CONTROL_VALID: Block Valid: indicates that the structure is valid. + */ +enum nbft_control_flags { + NBFT_CONTROL_VALID = 1 << 0, +}; + +/** + * struct nbft_host - Host Descriptor (Figure 9) + * @structure_id: Structure ID: This field shall be set to 2h (i.e., + * Host Descriptor; #NBFT_DESC_HOST). + * @flags: Host Flags, see &enum nbft_host_flags. + * @host_id: Host ID: This field shall be set to the Host Identifier. This + * field shall not be empty if the NBFT and NVMe Boot are supported + * by the Platform. + * @host_nqn_obj: Host NQN Heap Object Reference: this field indicates a heap + * object containing a Host NQN. This object shall not be empty + * if the NBFT and NVMe Boot are supported by the Platform. + * @reserved: Reserved. + */ +struct nbft_host { + __u8 structure_id; + __u8 flags; + __u8 host_id[16]; + struct nbft_heap_obj host_nqn_obj; + __u8 reserved[8]; +}; + +/** + * enum nbft_host_flags - Host Flags + * @NBFT_HOST_VALID: Descriptor Valid: If set to 1h, then this + * descriptor is valid. If cleared to 0h, then + * this descriptor is reserved. + * @NBFT_HOST_HOSTID_CONFIGURED: HostID Configured: If set to 1h, then the + * Host ID field contains an administratively-configured + * value. If cleared to 0h, then the Host ID + * field contains a driver default value. + * @NBFT_HOST_HOSTNQN_CONFIGURED: Host NQN Configured: If set to 1h, then the + * Host NQN indicated by the Host NQN Heap Object + * Reference field (&struct nbft_host.host_nqn) + * contains an administratively-configured value. + * If cleared to 0h, then the Host NQN indicated + * by the Host NQN Offset field contains a driver + * default value. + * @NBFT_HOST_PRIMARY_ADMIN_MASK: Mask to get Primary Administrative Host Descriptor: + * indicates whether the Host Descriptor in this + * NBFT was selected as the primary NBFT for + * administrative purposes of platform identity + * as a hint to the OS. If multiple NBFT tables + * are present, only one NBFT should be administratively + * selected. There is no enforcement mechanism + * for this to be coordinated between multiple NBFT + * tables, but this field should be set to Selected + * (#NBFT_HOST_PRIMARY_ADMIN_SELECTED) if + * more than one NBFT is present. + * @NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED: Not Indicated by Driver: The driver that created + * this NBFT provided no administrative priority + * hint for this NBFT. + * @NBFT_HOST_PRIMARY_ADMIN_UNSELECTED: Unselected: The driver that created this NBFT + * explicitly indicated that this NBFT should + * not be prioritized over any other NBFT. + * @NBFT_HOST_PRIMARY_ADMIN_SELECTED: Selected: The driver that created this NBFT + * explicitly indicated that this NBFT should + * be prioritized over any other NBFT. + */ +enum nbft_host_flags { + NBFT_HOST_VALID = 1 << 0, + NBFT_HOST_HOSTID_CONFIGURED = 1 << 1, + NBFT_HOST_HOSTNQN_CONFIGURED = 1 << 2, + NBFT_HOST_PRIMARY_ADMIN_MASK = 0x18, + NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED = 0x00, + NBFT_HOST_PRIMARY_ADMIN_UNSELECTED = 0x08, + NBFT_HOST_PRIMARY_ADMIN_SELECTED = 0x10, +}; + +/** + * struct nbft_hfi - Host Fabric Interface (HFI) Descriptor (Figure 11) + * @structure_id: Structure ID: This field shall be set to 3h (i.e., Host Fabric + * Interface Descriptor; #NBFT_DESC_HFI). + * @index: HFI Descriptor Index: This field indicates the number of this + * HFI Descriptor in the Host Fabric Interface Descriptor List. + * @flags: HFI Descriptor Flags, see &enum nbft_hfi_flags. + * @trtype: HFI Transport Type, see &enum nbft_trtype. + * @reserved1: Reserved. + * @trinfo_obj: HFI Transport Info Descriptor Heap Object Reference: If this + * field is set to a non-zero value, then this field indicates + * the location and size of a heap object containing + * a HFI Transport Info. + * @reserved2: Reserved. + */ +struct nbft_hfi { + __u8 structure_id; + __u8 index; + __u8 flags; + __u8 trtype; + __u8 reserved1[12]; + struct nbft_heap_obj trinfo_obj; + __u8 reserved2[10]; +}; + +/** + * enum nbft_hfi_flags - HFI Descriptor Flags + * @NBFT_HFI_VALID: Descriptor Valid: If set to 1h, then this descriptor is valid. + * If cleared to 0h, then this descriptor is reserved. + */ +enum nbft_hfi_flags { + NBFT_HFI_VALID = 1 << 0, +}; + +/** + * struct nbft_hfi_info_tcp - HFI Transport Info Descriptor - NVMe/TCP (Figure 13) + * @structure_id: Structure ID: This field shall be set to 7h (i.e., + * HFI Transport Info; #NBFT_DESC_HFI_TRINFO). + * @version: Version: This field shall be set to 1h. + * @trtype: HFI Transport Type, see &enum nbft_trtype: This field + * shall be set to 03h (i.e., NVMe/TCP; #NBFT_TRTYPE_TCP). + * @trinfo_version: Transport Info Version: Implementations compliant to this + * specification shall set this field to 1h. + * @hfi_index: HFI Descriptor Index: The value of the HFI Descriptor Index + * field of the HFI Descriptor (see &struct nbft_hfi.index) + * whose HFI Transport Info Descriptor Heap Object Reference + * field indicates this HFI Transport Info Descriptor. + * @flags: HFI Transport Flags, see &enum nbft_hfi_info_tcp_flags. + * @pci_sbdf: PCI Express Routing ID for the HFI Transport Function: + * This field indicates the PCI Express Routing ID as specified + * in the PCI Express Base Specification. + * @mac_addr: MAC Address: The MAC address of this HFI, in EUI-48TM format, + * as defined in the IEEE Guidelines for Use of Extended Unique + * Identifiers. This field shall be set to a non-zero value. + * @vlan: VLAN: If this field is set to a non-zero value, then this + * field contains the VLAN identifier if the VLAN associated + * with this HFI, as defined in IEEE 802.1q-2018. If no VLAN + * is associated with this HFI, then this field shall be cleared + * to 0h. + * @ip_origin: IP Origin: If this field is set to a non-zero value, then + * this field indicates the source of Ethernet L3 configuration + * information used by the driver for this interface. Valid + * values are defined in the Win 32 API: NL_PREFIX_ORIGIN + * enumeration specification. This field should be cleared + * to 0h if the IP Origin field is unused by driver. + * @ip_address: IP Address: This field indicates the IPv4 or IPv6 address + * of this HFI. This field shall be set to a non-zero value. + * @subnet_mask_prefix: Subnet Mask Prefix: This field indicates the IPv4 or IPv6 + * subnet mask in CIDR routing prefix notation. + * @ip_gateway: IP Gateway: If this field is set to a non-zero value, this + * field indicates the IPv4 or IPv6 address of the IP gateway + * for this HFI. If this field is cleared to 0h, then + * no IP gateway is specified. + * @reserved1: Reserved. + * @route_metric: Route Metric: If this field is set to a non-zero value, + * this field indicates the cost value for the route indicated + * by this HF. This field contains the value utilized by the + * pre-OS driver when chosing among all available routes. Lower + * values relate to higher priority. Refer to IETF RFC 4249. + * If the pre-OS driver supports routing and did not configure + * a specific route metric for this interface, then the pre-OS + * driver should set this value to 500. If the pre-OS driver + * does not support routing, then this field should be cleared + * to 0h. + * @primary_dns: Primary DNS: If this field is set to a non-zero value, + * this field indicates the IPv4 or IPv6 address of the + * Primary DNS server for this HFI, if any, from byte offset + * 0h of the NBFT Table Header. If this field is cleared to 0h, + * then no Primary DNS is specified. + * @secondary_dns: Secondary DNS: If this field is set to a non-zero value, + * this field indicates the IPv4 or IPv6 address of + * the Secondary DNS server for this HFI, if any, from byte + * offset 0h of the NBFT Table Header. If this field is + * cleared to 0h, then no Secondary DNS is specified. + * @dhcp_server: DHCP Server: If the DHCP Override bit is set to 1h, then + * this field indicates the IPv4 or IPv6 address of the DHCP + * server used to assign this HFI address. If that bit is + * cleared to 0h, then this field is reserved. + * @host_name_obj: Host Name Heap Object Reference: If this field is set + * to a non-zero value, then this field indicates the location + * and size of a heap object containing a Host Name string. + * @reserved2: Reserved. + */ +struct nbft_hfi_info_tcp { + __u8 structure_id; + __u8 version; + __u8 trtype; + __u8 trinfo_version; + __le16 hfi_index; + __u8 flags; + __le32 pci_sbdf; + __u8 mac_addr[6]; + __le16 vlan; + __u8 ip_origin; + __u8 ip_address[16]; + __u8 subnet_mask_prefix; + __u8 ip_gateway[16]; + __u8 reserved1; + __le16 route_metric; + __u8 primary_dns[16]; + __u8 secondary_dns[16]; + __u8 dhcp_server[16]; + struct nbft_heap_obj host_name_obj; + __u8 reserved2[18]; +} __attribute__((packed)); + +/** + * enum nbft_hfi_info_tcp_flags - HFI Transport Flags + * @NBFT_HFI_INFO_TCP_VALID: Descriptor Valid: if set to 1h, then this + * descriptor is valid. If cleared to 0h, then + * this descriptor is reserved. + * @NBFT_HFI_INFO_TCP_GLOBAL_ROUTE: Global Route vs. Link Local Override Flag: + * if set to 1h, then the BIOS utilized this + * interface described by HFI to be the default + * route with highest priority. If cleared to 0h, + * then routes are local to their own scope. + * @NBFT_HFI_INFO_TCP_DHCP_OVERRIDE: DHCP Override: if set to 1, then HFI information + * was populated by consuming the DHCP on this + * interface. If cleared to 0h, then the HFI + * information was set administratively by + * a configuration interface to the driver and + * pre-OS envrionment. + */ +enum nbft_hfi_info_tcp_flags { + NBFT_HFI_INFO_TCP_VALID = 1 << 0, + NBFT_HFI_INFO_TCP_GLOBAL_ROUTE = 1 << 1, + NBFT_HFI_INFO_TCP_DHCP_OVERRIDE = 1 << 2, +}; + +/** + * struct nbft_ssns - Subsystem Namespace (SSNS) Descriptor (Figure 15) + * @structure_id: Structure ID: This field shall be set to 4h + * (i.e., SSNS; #NBFT_DESC_SSNS). + * @index: SSNS Descriptor Index: This field indicates the number + * of this Subsystem Namespace Descriptor in the + * Subsystem Namespace Descriptor List. + * @flags: SSNS Flags, see &enum nbft_ssns_flags. + * @trtype: Transport Type, see &enum nbft_trtype. + * @trflags: Transport Specific Flags, see &enum nbft_ssns_trflags. + * @primary_discovery_ctrl_index: Primary Discovery Controller Index: The Discovery + * Descriptor Index field of the Discovery Descriptor + * (see &struct nbft_discovery) that is associated with + * this SSNS Descriptor. If a Discovery controller was + * used to establish this record this value shall + * be set to a non-zero value. If this namespace was + * associated with multiple Discovery controllers, + * those Discovery controllers shall have records + * in the Discovery Descriptor to facilitate multi-path + * rediscovery as required. If no Discovery controller + * was utilized to inform this namespace record, + * this field shall be cleared to 0h. + * @reserved1: Reserved. + * @subsys_traddr_obj: Subsystem Transport Address Heap Object Reference: + * This field indicates the location and size of a heap + * object containing the Subsystem Transport Address. + * For IP based transports types, shall be an IP Address. + * @subsys_trsvcid_obj: Subsystem Transport Service Identifier Heap Object Reference: + * This field indicates the location and size of a heap + * object containing an array of bytes indicating + * the Subsystem Transport Service Identifier. + * See &enum nbft_trtype. + * @subsys_port_id: Subsystem Port ID: Port in the NVM subsystem + * associated with this transport address used by + * the pre-OS driver. + * @nsid: Namespace ID: This field indicates the namespace + * identifier (NSID) of the namespace indicated by + * this descriptor. This field shall be cleared to 0h + * if not specified by the user. If this value is cleared + * to 0h, then consumers of the NBFT shall rely + * on the NID. + * @nidt: Namespace Identifier Type (NIDT): This field + * contains the value of the Namespace Identifier Type (NIDT) + * field in the Namespace Identification Descriptor + * for the namespace indicated by this descriptor. + * If a namespace supports multiple NIDT entries + * for uniqueness, the order of preference is NIDT field + * value of 3h (i.e., UUID) before 2h (i.e., NSGUID), + * and 2h before 1h (i.e., EUI-64). + * @nid: Namespace Identifier (NID): This field contains + * the value of the Namespace Identifier (NID) field + * in the Namespace Identification Descriptor for + * the namespace indicated by this descriptor. + * @security_desc_index: Security Profile Descriptor Index: If the Use Security + * Flag bit in the SSNS Flags field is set to 1h, then + * this field indicates the value of the Security Profile + * Descriptor Index field of the Security Profile + * Descriptor (see &struct nbft_security) associated + * with this namespace. If the Use Security Flag bit + * is cleared to 0h, then no Security Profile Descriptor + * is associated with this namespace and this field + * is reserved. + * @primary_hfi_desc_index: Primary HFI Descriptor Index: This field indicates + * the value of the HFI Descriptor Index field of the + * HFI Descriptor (see &struct nbft_hfi) for the + * interface associated with this namespace. If multiple + * HFIs are associated with this record, subsequent + * interfaces should be populated in the Secondary + * HFI Associations field. + * @reserved2: Reserved. + * @secondary_hfi_assoc_obj: Secondary HFI Associations Heap Object Reference: + * If this field is set to a non-zero value, then + * this field indicates an array of bytes, in which + * each byte contains the value of the HFI Descriptor + * Index field of an HFI Descriptor in the HFI Descriptor + * List. If this field is cleared to 0h, then no + * secondary HFI associations are specified. + * @subsys_ns_nqn_obj: Subsystem and Namespace NQN Heap Object Reference: + * This field indicates the location and size of + * a heap object containing the Subsystem and Namespace NQN. + * @ssns_extended_info_desc_obj: SSNS Extended Information Descriptor Heap Object + * Reference: If the SSNS Extended Info In-use Flag + * bit is set to 1h, then this field indicates the + * offset in bytes of a heap object containing an + * SSNS Extended Information Descriptor + * (see &struct nbft_ssns_ext_info) heap object + * from byte offset 0h of the NBFT Table Header. + * If the SSNS Extended Info In-use Flag bit is cleared + * to 0h, then this field is reserved. + * @reserved3: Reserved. + */ +struct nbft_ssns { + __u8 structure_id; + __le16 index; + __le16 flags; + __u8 trtype; + __le16 trflags; + __u8 primary_discovery_ctrl_index; + __u8 reserved1; + struct nbft_heap_obj subsys_traddr_obj; + struct nbft_heap_obj subsys_trsvcid_obj; + __le16 subsys_port_id; + __le32 nsid; + __u8 nidt; + __u8 nid[16]; + __u8 security_desc_index; + __u8 primary_hfi_desc_index; + __u8 reserved2; + struct nbft_heap_obj secondary_hfi_assoc_obj; + struct nbft_heap_obj subsys_ns_nqn_obj; + struct nbft_heap_obj ssns_extended_info_desc_obj; + __u8 reserved3[62]; +} __attribute__((packed)); + +/** + * enum nbft_ssns_flags - Subsystem and Namespace Specific Flags Field (Figure 16) + * @NBFT_SSNS_VALID: Descriptor Valid: If set to 1h, then this descriptor + * is valid. If cleared to 0h, then this descriptor + * is not valid. A host that supports NVMe-oF Boot, + * but does not currently have a remote Subsystem + * and Namespace assigned may clear this bit to 0h. + * @NBFT_SSNS_NON_BOOTABLE_ENTRY: Non-bootable Entry Flag: If set to 1h, this flag + * indicates that this SSNS Descriptor contains + * a namespace of administrative purpose to the boot + * process, but the pre-OS may not have established + * connectivity to or evaluated the contents of this + * Descriptor. Such namespaces may contain supplemental + * data deemed relevant by the Administrator as part + * of the pre-OS to OS hand off. This may include + * properties such as a UEFI device path that may + * not have been created for this namespace. This means + * an OS runtime may still require the contents + * of such a namespace to complete later stages + * of boot. If cleared to 0h, then this namespace did + * not have any special administrative intent. + * @NBFT_SSNS_USE_SECURITY_FIELD: Use Security Flag: If set to 1h, then there is + * a Security Profile Descriptor associated with this + * SSNS record and the Security Profile Descriptor Index + * field is valid. If cleared to 0h, then there is + * no Security Profile Descriptor associated with this + * SSNS record and the Security Profile Descriptor Index + * field is not valid. + * @NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE: DHCP Root-Path Override Flag: If set to 1h, then + * this SSNS descriptor was populated by consuming + * the DHCP Root-Path on this interface. If cleared + * to 0h, then the DHCP Root-Path was not used + * in populating the SSNS descriptor. + * @NBFT_SSNS_EXTENDED_INFO_IN_USE: SSNS Extended Info In-use Flag: If set to 1h, + * then the SSNS Extended Information Offset field + * and the SSNS Extended Information Length field + * are valid. This flag, if set to 1h, indicates + * that a Subsystem and Namespace Extended Information + * Descriptor corresponding to this descriptor is present. + * @NBFT_SSNS_SEPARATE_DISCOVERY_CTRL: Separate Discovery Controller Flag: If set to 1h, + * then the Discovery controller associated with + * this volume is on a different transport address + * than the specified in the Subsystem Transport + * Address Heap Object Reference. If cleared to 0h, + * then the Discovery controller is the same as the + * Subsystem Transport Address Heap Object Reference. + * @NBFT_SSNS_DISCOVERED_NAMESPACE: Discovered Namespace Flag: If set to 1h, then + * this namespace was acquired through discovery. + * If cleared to 0h, then this namespace was + * explicitly configured in the system. + * @NBFT_SSNS_UNAVAIL_NAMESPACE_MASK: Mask to get Unavailable Namespace Flag: This + * field indicates the availability of the namespace + * at a specific point in time. Such use is only + * a hint and its use does not guarantee the availability + * of that referenced namespace at any future point in time. + * @NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND: Not Indicated by Driver: No information is provided. + * @NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL: Available: A referenced namespace described by this + * flag was previously accessible by the pre-OS driver. + * @NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL: Unavailable: This namespace was administratively + * configured but unattempted, unavailable or + * inaccessible when establishing connectivity + * by the pre-OS driver. + */ +enum nbft_ssns_flags { + NBFT_SSNS_VALID = 1 << 0, + NBFT_SSNS_NON_BOOTABLE_ENTRY = 1 << 1, + NBFT_SSNS_USE_SECURITY_FIELD = 1 << 2, + NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE = 1 << 3, + NBFT_SSNS_EXTENDED_INFO_IN_USE = 1 << 4, + NBFT_SSNS_SEPARATE_DISCOVERY_CTRL = 1 << 5, + NBFT_SSNS_DISCOVERED_NAMESPACE = 1 << 6, + NBFT_SSNS_UNAVAIL_NAMESPACE_MASK = 0x0180, + NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND = 0x0000, + NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL = 0x0080, + NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL = 0x0100, +}; + +/** + * enum nbft_ssns_trflags - SSNS Transport Specific Flags Field (Figure 17) + * @NBFT_SSNS_TRFLAG_VALID: Transport Specific Flags in Use: If set to 1h, then + * this descriptor is valid. If cleared to 0h, then + * this descriptor is not valid. + * @NBFT_SSNS_PDU_HEADER_DIGEST: PDU Header Digest (HDGST) Flag: If set to 1h, then + * the host or administrator required the connection + * described by this Subsystem and Namespace Descriptor + * to use the NVM Header Digest Enabled. A consumer + * of this information should attempt to use NVM Header + * Digest when recreating this connection if enabled. + * If cleared to 0h, then the host or administrator + * did not require the connection described by this + * Subsystem and Namespace Descriptor to use the + * NVM Header Digest Enabled. + * @NBFT_SSNS_DATA_DIGEST: Data Digest (DDGST) Flag: If set to 1h, then + * the host or administrator required the connection + * described by this Subsystem and Namespace Descriptor + * to use the NVM Data Digest Enabled. If cleared + * to 0h, then the host or administrator did not + * require the connection described by this Subsystem + * and Namespace Descriptor to use the NVM Data Digest + * Enabled. A consumer of this field should attempt + * to use NVM Data Digest when recreating this + * connection if enabled. + */ +enum nbft_ssns_trflags { + NBFT_SSNS_TRFLAG_VALID = 1 << 0, + NBFT_SSNS_PDU_HEADER_DIGEST = 1 << 1, + NBFT_SSNS_DATA_DIGEST = 1 << 2, +}; + +/** + * struct nbft_ssns_ext_info - Subsystem and Namespace Extended Information + * Descriptor (Figure 19) + * @structure_id: Structure ID: This field shall be set to 9h + * (i.e., SSNS Extended Info; #NBFT_DESC_SSNS_EXT_INFO). + * @version: Version: This field shall be set to 1h. + * @ssns_index: SSNS Descriptor Index: This field indicates the value + * of the SSNS Descriptor Index field of the Subsystem + * and Namespace Descriptor (see &struct nbft_ssns) whose + * SSNS Extended Information Descriptor Heap Object + * Reference field indicates this descriptor. + * @flags: Flags, see &enum nbft_ssns_ext_info_flags. + * @cntlid: Controller ID: The controller identifier of the first + * controller associated with the Admin Queue by the driver. + * If a controller identifier is not administratively + * specified or direct configuration is not supported + * by the driver, then this field shall be cleared to 0h. + * @asqsz: Admin Submission Queue Size (ASQSZ): The Admin Submission + * Queue Size utilized for the respective SSNS by the driver. + * @dhcp_root_path_str_obj: DHCP Root Path String Heap Object Reference: If the + * SSNS DHCP Root Path Override (#NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE) + * flag bit is set to 1h, then this field indicates + * the offset in bytes of a heap object containing + * an DHCP Root Path String used by the driver. If the + * SNSS DHCP Root Path Override flag bit is cleared to 0h, + * then this field is reserved. + */ +struct nbft_ssns_ext_info { + __u8 structure_id; + __u8 version; + __le16 ssns_index; + __le32 flags; + __le16 cntlid; + __le16 asqsz; + struct nbft_heap_obj dhcp_root_path_str_obj; +} __attribute__((packed)); + +/** + * enum nbft_ssns_ext_info_flags - Subsystem and Namespace Extended Information + * Descriptor Flags + * @NBFT_SSNS_EXT_INFO_VALID: Descriptor Valid: If set to 1h, then this descriptor + * is valid. If cleared to 0h, then this descriptor + * is reserved. + * @NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ: Administrative ASQSZ: If set to 1h, then the value + * of the ASQSZ field was provided by administrative + * configuration for this SSNS record. If cleared + * to 0h, then the value of the ASQSZ field was + * either obtained by discovery or assumed + * by the driver. + */ +enum nbft_ssns_ext_info_flags { + NBFT_SSNS_EXT_INFO_VALID = 1 << 0, + NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ = 1 << 1, +}; + +/** + * struct nbft_security - Security Profile Descriptor (Figure 21) + * @structure_id: Structure ID: This field shall be set to 5h + * (i.e., Security; #NBFT_DESC_SECURITY). + * @index: Security Profile Descriptor Index: This field indicates + * the number of this Security Profile Descriptor in the + * Security Profile Descriptor List. + * @flags: Security Profile Descriptor Flags, see &enum nbft_security_flags. + * @secret_type: Secret Type, see &enum nbft_security_secret_type. + * @reserved1: Reserved. + * @sec_chan_alg_obj: Secure Channel Algorithm Heap Object Reference: If the + * Security Policy List field is set to 1h, then this field + * indicates the location and size of a heap object containing + * a list of secure channel algorithms. The list is an array + * of bytes and the values are defined in the Security Type + * (SECTYPE) field in the Transport Specific Address Subtype + * Definition in the NVMe TCP Transport Specification. + * If the Security Policy List field is cleared to 0h, then + * this field is reserved. + * @auth_proto_obj: Authentication Protocols Heap Object Reference: If the + * Authentication Policy List field is set to 1h, then this + * field indicates the location and size of a heap object + * containing a list of authentication protocol identifiers. + * If the Authentication Policy List field is cleared to 0h, + * then this field is reserved. + * @cipher_suite_obj: Cipher Suite Offset Heap Object Reference: If the Cipher + * Suites Restricted by Policy bit is set to 1h, then this + * field indicates the location and size of a heap object + * containing a list of cipher suite identifiers. The list, + * if any, is an array of bytes and the values are defined + * in the IANA TLS Parameters Registry. If the Cipher Suites + * Restricted by Policy bit is cleared to 0h, then this field + * is reserved. + * @dh_grp_obj: DH Groups Heap Object Reference: If the Authentication DH Groups + * Restricted by Policy List bit is set to 1h, then this field + * indicates the location and size of a heap object containing + * a list of DH-HMAC-CHAP Diffie-Hellman (DH) group identifiers. + * If the Authentication DH Groups Restricted by Policy List + * bit is cleared to 0h, then this field is reserved. + * @sec_hash_func_obj: Secure Hash Functions Offset Heap Object Reference: If the + * Secure Hash Functions Policy List bit is set to 1h, then + * this field indicates the offset in bytes of a heap object + * containing a list of DH-HMAC-CHAP hash function identifiers. + * The list is an array of bytes and the values are defined + * in the NVM Express Base Specification. If the Secure Hash + * Functions Policy List bit is cleared to 0h, then this + * field is reserved. + * @sec_keypath_obj: Secret Keypath Offset Heap Object Reference: if this field + * is set to a non-zero value, then this field indicates + * the location and size of a heap object containing a URI. + * The type of the URI is specified in the Secret Type field. + * If this field is cleared to 0h, then this field is reserved. + * @reserved2: Reserved. + */ +struct nbft_security { + __u8 structure_id; + __u8 index; + __le16 flags; + __u8 secret_type; + __u8 reserved1; + struct nbft_heap_obj sec_chan_alg_obj; + struct nbft_heap_obj auth_proto_obj; + struct nbft_heap_obj cipher_suite_obj; + struct nbft_heap_obj dh_grp_obj; + struct nbft_heap_obj sec_hash_func_obj; + struct nbft_heap_obj sec_keypath_obj; + __u8 reserved2[22]; +}; + +/** + * enum nbft_security_flags - Security Profile Descriptor Flags (Figure 22) + * @NBFT_SECURITY_VALID: Descriptor Valid: If set to 1h, then + * this descriptor is valid. If cleared + * to 0h, then this descriptor is not valid. + * @NBFT_SECURITY_IN_BAND_AUTH_MASK: Mask to get the In-Band Authentication + * Required field. + * @NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED: In-band authentication is not supported + * by the NVM subsystem. + * @NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED: In-band authentication is supported by + * the NVM subsystem and is not required. + * @NBFT_SECURITY_IN_BAND_AUTH_REQUIRED: In-band authentication is supported by + * the NVM subsystem and is required. + * @NBFT_SECURITY_AUTH_POLICY_LIST_MASK: Mask to get the Authentication Policy List + * flag: This field indicates whether + * authentication protocols were indicated + * by policy from driver defaults or + * administrative configuration. + * @NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED: Authentication Protocols Heap Object Reference + * field Offset and Length are reserved. + * @NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER: Authentication Protocols Offset field and + * the Authentication Protocols Length field + * indicate a list of authentication protocols + * used by the driver. + * @NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN: Authentication Protocols Offset field and + * the Authentication Protocols Length field + * indicate a list of authentication protocols + * that were administratively set and used + * by the driver. + * @NBFT_SECURITY_SEC_CHAN_NEG_MASK: Mask to get the Secure Channel Negotiation + * Required flag: This field indicates whether + * secure channel negotiation (e.g. TLS) + * is required. + * @NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED: Secure channel negotiation is not supported + * by the NVM subsystem. + * @NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED: Secure channel negotiation is supported + * by the NVM subsystem and is not required. + * @NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED: Secure channel negotiation is supported + * by the NVM subsystem and is required. + * @NBFT_SECURITY_SEC_POLICY_LIST_MASK: Mask to get the Security Policy List flag: + * This field indicates whether secure channel + * protocols were indicated by policy from driver + * defaults or administrative configuration. + * @NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED: The Offset field and Length field in the + * Secure Channel Algorithm Heap Object Reference + * field are reserved. + * @NBFT_SECURITY_SEC_POLICY_LIST_DRIVER: The Heap Object specified by the Secure Channel + * Algorithm Heap Object Reference field indicates + * a list of authentication protocols used + * by the driver. + * @NBFT_SECURITY_SEC_POLICY_LIST_ADMIN: The Heap Object specified by the Secure Channel + * Algorithm Heap Object Reference field indicates + * a list of authentication protocols that were + * administratively set and used by the driver. + * @NBFT_SECURITY_CIPHER_RESTRICTED: Cipher Suites Restricted by Policy: If set to 1h, + * then the Cipher Suite Offset field and the + * Ciper Suite Length field indicate a list + * of supported cipher suites by the driver. + * If cleared to 0h, then the Cipher Suite Offset + * field and the Cipher Suite Length field + * are reserved. + * @NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED: Authentication DH Groups Restricted + * by Policy List: If set to 1h, then connections + * shall use one of the authentication DH groups + * in the Authentication DH Groups List is required. + * If cleared to 0h, then no Authentication DH Groups + * List is indicated and use of an authentication + * DH Group is not required. + * @NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST: Secure Hash Functions Policy List: If set to 1h, + * then connections shall use one of the secure + * hash functions in the Secure Hash Functions + * Policy List is required. If cleared to 0h, + * then no Secure Hash Functions Policy + * List is indicated and use of a secure + * hash function is not required. + */ +enum nbft_security_flags { + NBFT_SECURITY_VALID = 1 << 0, + NBFT_SECURITY_IN_BAND_AUTH_MASK = 0x0006, + NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED = 0x0000, + NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED = 0x0002, + NBFT_SECURITY_IN_BAND_AUTH_REQUIRED = 0x0004, + NBFT_SECURITY_AUTH_POLICY_LIST_MASK = 0x0018, + NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED = 0x0000, + NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER = 0x0008, + NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN = 0x0010, + NBFT_SECURITY_SEC_CHAN_NEG_MASK = 0x0060, + NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED = 0x0000, + NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED = 0x0020, + NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED = 0x0040, + NBFT_SECURITY_SEC_POLICY_LIST_MASK = 0x0180, + NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED = 0x0000, + NBFT_SECURITY_SEC_POLICY_LIST_DRIVER = 0x0080, + NBFT_SECURITY_SEC_POLICY_LIST_ADMIN = 0x0100, + NBFT_SECURITY_CIPHER_RESTRICTED = 1 << 9, + NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED = 1 << 10, + NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST = 1 << 11, +}; + +/** + * enum nbft_security_secret_type - Security Profile Descriptor Secret Type + * @NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI: Redfish Host Interface URI: + * If set to 1h, then the Secret Keypath + * Object Reference is a URI pointing + * to a Redfish Key Collection Object + * that contains the PSK. + */ +enum nbft_security_secret_type { + NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI = 1 << 1, +}; + +/** + * struct nbft_discovery - Discovery Descriptor (Figure 24) + * @structure_id: Structure ID: This field shall be set to 6h + * (i.e., Discovery Descriptor; #NBFT_DESC_DISCOVERY). + * @flags: Discovery Descriptor Flags, see &enum nbft_discovery_flags. + * @index: Discovery Descriptor Index: This field indicates + * the number of this Discovery Descriptor in + * the Discovery Descriptor List. + * @hfi_index: HFI Descriptor Index: This field indicates the value + * of the HFI Descriptor Index field of the HFI Descriptor + * associated with this Discovery Descriptor. If multiple + * HFIs share a common Discovery controller, there shall + * be multiple Discovery Descriptor entries with one per HFI. + * @sec_index: Security Profile Descriptor Index: This field indicates + * the value of the Security Profile Descriptor Index + * field of the Security Descriptor associated with + * this Discovery Descriptor. + * @reserved1: Reserved. + * @discovery_ctrl_addr_obj: Discovery Controller Address Heap Object Reference: + * This field indicates the location and size of a heap + * object containing a URI which indicates an NVMe Discovery + * controller associated with this Discovery Descriptor. + * If this field is cleared to 0h, then no URI is specified. + * @discovery_ctrl_nqn_obj: Discovery Controller NQN Heap Object Reference: + * If set to a non-zero value, this field indicates + * the location and size of a heap object containing + * an NVMe Discovery controller NQN. If the NVMe Discovery + * controller referenced by this record requires secure + * authentication with a well known Subsystem NQN, this + * field indicates the unique NQN for that NVMe Discovery + * controller. This record is involved formatted as an NQN + * string. If this field is cleared to 0h, then this + * field is reserved and the OS shall use the well + * known discovery NQN for this record. + * @reserved2: Reserved. + */ +struct nbft_discovery { + __u8 structure_id; + __u8 flags; + __u8 index; + __u8 hfi_index; + __u8 sec_index; + __u8 reserved1; + struct nbft_heap_obj discovery_ctrl_addr_obj; + struct nbft_heap_obj discovery_ctrl_nqn_obj; + __u8 reserved2[14]; +}; + +/** + * enum nbft_discovery_flags - Discovery Descriptor Flags + * @NBFT_DISCOVERY_VALID: Descriptor Valid: if set to 1h, then this descriptor + * is valid. If cleared to 0h, then this descriptor + * is reserved. + */ +enum nbft_discovery_flags { + NBFT_DISCOVERY_VALID = 1 << 0, +}; + +/* + * End of NBFT ACPI table definitions + */ + + +/* + * Convenient NBFT table parser ('nbft_info' prefix) + */ + +/** + * enum nbft_info_primary_admin_host_flag - Primary Administrative Host Descriptor Flags + * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED: Not Indicated by Driver: The driver + * that created this NBFT provided no + * administrative priority hint for + * this NBFT. + * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED: Unselected: The driver that created + * this NBFT explicitly indicated that + * this NBFT should not be prioritized + * over any other NBFT. + * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED: Selected: The driver that created + * this NBFT explicitly indicated that + * this NBFT should be prioritized over + * any other NBFT. + * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED: Reserved. + */ +enum nbft_info_primary_admin_host_flag { + NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED, + NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED, + NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED, + NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED, +}; + +/** + * struct nbft_info_host - Host Descriptor + * @id: Host ID (raw UUID, length = 16 bytes). + * @nqn: Host NQN. + * @host_id_configured: HostID Configured Flag: value of True indicates that @id + * contains administratively-configured value, or driver + * default value if False. + * @host_nqn_configured: Host NQN Configured Flag: value of True indicates that + * @nqn contains administratively-configured value, + * or driver default value if False. + * @primary: Primary Administrative Host Descriptor, see + * &enum nbft_info_primary_admin_host_flag. + */ +struct nbft_info_host { + unsigned char *id; + char *nqn; + bool host_id_configured; + bool host_nqn_configured; + enum nbft_info_primary_admin_host_flag primary; +}; + +/** + * struct nbft_info_hfi_info_tcp - HFI Transport Info Descriptor - NVMe/TCP + * @pci_sbdf: PCI Express Routing ID for the HFI Transport Function. + * @mac_addr: MAC Address: The MAC address of this HFI, + * in EUI-48TM format. + * @vlan: The VLAN identifier if the VLAN is associated with + * this HFI, as defined in IEEE 802.1q-2018 or zeroes + * if no VLAN is associated with this HFI. + * @ip_origin: The source of Ethernet L3 configuration information + * used by the driver or 0 if not used. + * @ipaddr: The IPv4 or IPv6 address of this HFI. + * @subnet_mask_prefix: The IPv4 or IPv6 subnet mask in CIDR routing prefix + * notation. + * @gateway_ipaddr: The IPv4 or IPv6 address of the IP gateway for this + * HFI or zeroes if no IP gateway is specified. + * @route_metric: The cost value for the route indicated by this HFI. + * @primary_dns_ipaddr: The IPv4 or IPv6 address of the Primary DNS server + * for this HFI. + * @secondary_dns_ipaddr: The IPv4 or IPv6 address of the Secondary DNS server + * for this HFI. + * @dhcp_server_ipaddr: The IPv4 or IPv6 address of the DHCP server used + * to assign this HFI address. + * @host_name: The Host Name string. + * @this_hfi_is_default_route: If True, then the BIOS utilized this interface + * described by HFI to be the default route with highest + * priority. If False, then routes are local to their + * own scope. + * @dhcp_override: If True, then HFI information was populated + * by consuming the DHCP on this interface. If False, + * then the HFI information was set administratively + * by a configuration interface to the driver and + * pre-OS envrionment. + */ +struct nbft_info_hfi_info_tcp { + __u32 pci_sbdf; + __u8 mac_addr[6]; + __u16 vlan; + __u8 ip_origin; + char ipaddr[40]; + __u8 subnet_mask_prefix; + char gateway_ipaddr[40]; + __u16 route_metric; + char primary_dns_ipaddr[40]; + char secondary_dns_ipaddr[40]; + char dhcp_server_ipaddr[40]; + char *host_name; + bool this_hfi_is_default_route; + bool dhcp_override; +}; + +/** + * struct nbft_info_hfi - Host Fabric Interface (HFI) Descriptor + * @index: HFI Descriptor Index: indicates the number of this HFI Descriptor + * in the Host Fabric Interface Descriptor List. + * @transport: Transport Type string (e.g. 'tcp'). + * @tcp_info: The HFI Transport Info Descriptor, see &struct nbft_info_hfi_info_tcp. + */ +struct nbft_info_hfi { + int index; + char transport[8]; + struct nbft_info_hfi_info_tcp tcp_info; +}; + +/** + * struct nbft_info_discovery - Discovery Descriptor + * @index: The number of this Discovery Descriptor in the Discovery + * Descriptor List. + * @security: The Security Profile Descriptor, see &struct nbft_info_security. + * @hfi: The HFI Descriptor associated with this Discovery Descriptor. + * See &struct nbft_info_hfi. + * @uri: A URI which indicates an NVMe Discovery controller associated + * with this Discovery Descriptor. + * @nqn: An NVMe Discovery controller NQN. + */ +struct nbft_info_discovery { + int index; + struct nbft_info_security *security; + struct nbft_info_hfi *hfi; + char *uri; + char *nqn; +}; + +/** + * struct nbft_info_security - Security Profile Descriptor + * @index: The number of this Security Profile Descriptor in the Security + * Profile Descriptor List. + */ +struct nbft_info_security { + int index; + /* TODO add fields */ +}; + +/** + * enum nbft_info_nid_type - Namespace Identifier Type (NIDT) + * @NBFT_INFO_NID_TYPE_NONE: No identifier available. + * @NBFT_INFO_NID_TYPE_EUI64: The EUI-64 identifier. + * @NBFT_INFO_NID_TYPE_NGUID: The NSGUID identifier. + * @NBFT_INFO_NID_TYPE_NS_UUID: The UUID identifier. + */ +enum nbft_info_nid_type { + NBFT_INFO_NID_TYPE_NONE = 0, + NBFT_INFO_NID_TYPE_EUI64 = 1, + NBFT_INFO_NID_TYPE_NGUID = 2, + NBFT_INFO_NID_TYPE_NS_UUID = 3, +}; + +/** + * struct nbft_info_subsystem_ns - Subsystem Namespace (SSNS) info + * @index: SSNS Descriptor Index in the descriptor list. + * @discovery: Primary Discovery Controller associated with + * this SSNS Descriptor. + * @security: Security Profile Descriptor associated with + * this namespace. + * @num_hfis: Number of HFIs. + * @hfis: List of HFIs associated with this namespace. + * Includes the primary HFI at the first position + * and all secondary HFIs. This array is null-terminated. + * @transport: Transport Type string (e.g. 'tcp'). + * @traddr: Subsystem Transport Address. + * @trsvcid: Subsystem Transport Service Identifier. + * @subsys_port_id: The Subsystem Port ID. + * @nsid: The Namespace ID of this descriptor or when @nid + * should be used instead. + * @nid_type: Namespace Identifier Type, see &enum nbft_info_nid_type. + * @nid: The Namespace Identifier value. + * @subsys_nqn: Subsystem and Namespace NQN. + * @pdu_header_digest_required: PDU Header Digest (HDGST) Flag: the use of NVM Header + * Digest Enabled is required. + * @data_digest_required: Data Digest (DDGST) Flag: the use of NVM Data Digest + * Enabled is required. + * @controller_id: Controller ID (SSNS Extended Information Descriptor): + * The controller ID associated with the Admin Queue + * or 0 if not supported. + * @asqsz: Admin Submission Queue Size (SSNS Extended Information + * Descriptor) or 0 if not supported. + * @dhcp_root_path_string: DHCP Root Path Override string (SSNS Extended + * Information Descriptor). + */ +struct nbft_info_subsystem_ns { + int index; + struct nbft_info_discovery *discovery; + struct nbft_info_security *security; + int num_hfis; + struct nbft_info_hfi **hfis; + char transport[8]; + char traddr[40]; + char *trsvcid; + __u16 subsys_port_id; + __u32 nsid; + enum nbft_info_nid_type nid_type; + __u8 *nid; + char *subsys_nqn; + bool pdu_header_digest_required; + bool data_digest_required; + int controller_id; + int asqsz; + char *dhcp_root_path_string; +}; + +/** + * struct nbft_info - The parsed NBFT table data. + * @filename: Path to the NBFT table. + * @raw_nbft: The original NBFT table contents. + * @raw_nbft_size: Size of @raw_nbft. + * @host: The Host Descriptor (should match other NBFTs). + * @hfi_list: The HFI Descriptor List (null-terminated array). + * @security_list: The Security Profile Descriptor List (null-terminated array). + * @discovery_list: The Discovery Descriptor List (null-terminated array). + * @subsystem_ns_list: The SSNS Descriptor List (null-terminated array). + */ +struct nbft_info { + char *filename; + __u8 *raw_nbft; + ssize_t raw_nbft_size; + struct nbft_info_host host; + struct nbft_info_hfi **hfi_list; + struct nbft_info_security **security_list; + struct nbft_info_discovery **discovery_list; + struct nbft_info_subsystem_ns **subsystem_ns_list; +}; + +/** + * nvme_nbft_read() - Read and parse contents of an ACPI NBFT table + * + * @nbft: Parsed NBFT table data. + * @filename: Filename of the raw NBFT table to read. + * + * Read and parse the specified NBFT file into a struct nbft_info. + * Free with nvme_nbft_free(). + * + * Return: 0 on success, errno otherwise. + */ +int nvme_nbft_read(struct nbft_info **nbft, const char *filename); + +/** + * nvme_nbft_free() - Free the struct nbft_info and its contents + * @nbft: Parsed NBFT table data. + */ +void nvme_nbft_free(struct nbft_info *nbft); + +#endif diff --git a/src/nvme/private.h b/src/nvme/private.h index a6ded21..809b3bb 100644 --- a/src/nvme/private.h +++ b/src/nvme/private.h @@ -84,6 +84,7 @@ struct nvme_ctrl { char *dhchap_ctrl_key; char *cntrltype; char *dctype; + char *phy_slot; bool discovery_ctrl; bool unique_discovery_ctrl; bool discovered; @@ -104,6 +105,7 @@ struct nvme_subsystem { char *serial; char *firmware; char *subsystype; + char *application; }; struct nvme_host { @@ -120,8 +122,41 @@ struct nvme_host { * value */ }; +struct nvme_fabric_options { + bool cntlid; + bool ctrl_loss_tmo; + bool data_digest; + bool dhchap_ctrl_secret; + bool dhchap_secret; + bool disable_sqflow; + bool discovery; + bool duplicate_connect; + bool fast_io_fail_tmo; + bool hdr_digest; + bool host_iface; + bool host_traddr; + bool hostid; + bool hostnqn; + bool instance; + bool keep_alive_tmo; + bool keyring; + bool nqn; + bool nr_io_queues; + bool nr_poll_queues; + bool nr_write_queues; + bool queue_size; + bool reconnect_delay; + bool tls; + bool tls_key; + bool tos; + bool traddr; + bool transport; + bool trsvcid; +}; + struct nvme_root { char *config_file; + char *application; struct list_head hosts; struct list_head endpoints; /* MI endpoints */ FILE *fp; @@ -130,6 +165,7 @@ struct nvme_root { bool log_timestamp; bool modified; bool mi_probe_enabled; + struct nvme_fabric_options *options; }; int nvme_set_attr(const char *dir, const char *attr, const char *value); diff --git a/src/nvme/tree.c b/src/nvme/tree.c index 3484463..a2ac069 100644 --- a/src/nvme/tree.c +++ b/src/nvme/tree.c @@ -34,6 +34,8 @@ #include "log.h" #include "private.h" +const char *nvme_slots_sysfs_dir = "/sys/bus/pci/slots"; + static struct nvme_host *default_host; static void __nvme_free_host(nvme_host_t h); @@ -48,6 +50,34 @@ static int nvme_ctrl_scan_namespace(nvme_root_t r, struct nvme_ctrl *c, char *name); static int nvme_ctrl_scan_path(nvme_root_t r, struct nvme_ctrl *c, char *name); +/** + * Compare two C strings and handle NULL pointers gracefully. + * Return true if both pointers are equal (including both set to NULL). + * Return false if one and only one of the two pointers is NULL. + * Perform string comparisong only if both pointers are not NULL and + * return true if both strings are the same, false otherwise. + */ +static bool streq0(const char *s1, const char *s2) +{ + if (s1 == s2) + return true; + if (!s1 || !s2) + return false; + return !strcmp(s1, s2); +} + +/** + * Same as streq0() but ignore the case of the characters. + */ +static bool streqcase0(const char *s1, const char *s2) +{ + if (s1 == s2) + return true; + if (!s1 || !s2) + return false; + return !strcasecmp(s1, s2); +} + static inline void nvme_free_dirents(struct dirent **d, int i) { while (i-- > 0) @@ -197,6 +227,19 @@ int nvme_dump_tree(nvme_root_t r) return json_dump_tree(r); } +const char *nvme_root_get_application(nvme_root_t r) +{ + return r->application; +} + +void nvme_root_set_application(nvme_root_t r, const char *a) +{ + if (r->application) + free(r->application); + if (a) + r->application = strdup(a); +} + nvme_host_t nvme_first_host(nvme_root_t r) { return list_top(&r->hosts, struct nvme_host, entry); @@ -288,10 +331,13 @@ void nvme_free_tree(nvme_root_t r) { struct nvme_host *h, *_h; + free(r->options); nvme_for_each_host_safe(r, h, _h) __nvme_free_host(h); if (r->config_file) free(r->config_file); + if (r->application) + free(r->application); free(r); } @@ -315,6 +361,19 @@ const char *nvme_subsystem_get_type(nvme_subsystem_t s) return s->subsystype; } +const char *nvme_subsystem_get_application(nvme_subsystem_t s) +{ + return s->application; +} + +void nvme_subsystem_set_application(nvme_subsystem_t s, const char *a) +{ + if (s->application) + free(s->application); + if (a) + s->application = strdup(a); +} + nvme_ctrl_t nvme_subsystem_first_ctrl(nvme_subsystem_t s) { return list_top(&s->ctrls, struct nvme_ctrl, entry); @@ -390,6 +449,8 @@ static void __nvme_free_subsystem(struct nvme_subsystem *s) free(s->firmware); if (s->subsystype) free(s->subsystype); + if (s->application) + free(s->application); free(s); } @@ -435,6 +496,12 @@ struct nvme_subsystem *nvme_lookup_subsystem(struct nvme_host *h, if (name && s->name && strcmp(s->name, name)) continue; + if (h->r->application) { + if (!s->application) + continue; + if (strcmp(h->r->application, s->application)) + continue; + } return s; } return nvme_alloc_subsystem(h, name, subsysnqn); @@ -541,6 +608,8 @@ static int nvme_init_subsystem(nvme_subsystem_t s, const char *name) } s->name = strdup(name); s->sysfs_dir = (char *)path; + if (s->h->r->application) + s->application = strdup(s->h->r->application); return 0; } @@ -755,6 +824,11 @@ const char *nvme_ctrl_get_address(nvme_ctrl_t c) return c->address ? c->address : ""; } +const char *nvme_ctrl_get_phy_slot(nvme_ctrl_t c) +{ + return c->phy_slot ? c->phy_slot : ""; +} + const char *nvme_ctrl_get_firmware(nvme_ctrl_t c) { return c->firmware; @@ -942,6 +1016,7 @@ void nvme_deconfigure_ctrl(nvme_ctrl_t c) FREE_CTRL_ATTR(c->address); FREE_CTRL_ATTR(c->dctype); FREE_CTRL_ATTR(c->cntrltype); + FREE_CTRL_ATTR(c->phy_slot); } int nvme_disconnect_ctrl(nvme_ctrl_t c) @@ -1072,22 +1147,28 @@ nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport, { struct nvme_ctrl *c; + bool (*addreq)(const char *, const char *); + + if (!strcmp(transport, "tcp") || !strcmp(transport, "rdma")) + addreq = nvme_ipaddrs_eq; /* IP address compare for TCP/RDMA */ + else + addreq = streqcase0; /* Case-insensitive for FC (n/a for loop) */ c = p ? nvme_subsystem_next_ctrl(s, p) : nvme_subsystem_first_ctrl(s); for (; c != NULL; c = nvme_subsystem_next_ctrl(s, c)) { - if (strcmp(c->transport, transport)) + if (!streq0(c->transport, transport)) continue; if (traddr && c->traddr && - strcasecmp(c->traddr, traddr)) + !addreq(c->traddr, traddr)) continue; if (host_traddr && c->cfg.host_traddr && - strcmp(c->cfg.host_traddr, host_traddr)) + !addreq(c->cfg.host_traddr, host_traddr)) continue; if (host_iface && c->cfg.host_iface && - strcmp(c->cfg.host_iface, host_iface)) + !streq0(c->cfg.host_iface, host_iface)) continue; if (trsvcid && c->trsvcid && - strcmp(c->trsvcid, trsvcid)) + !streq0(c->trsvcid, trsvcid)) continue; return c; } @@ -1183,6 +1264,53 @@ static char *nvme_ctrl_lookup_subsystem_name(nvme_root_t r, return subsys_name; } +static char *nvme_ctrl_lookup_phy_slot(nvme_root_t r, const char *address) +{ + char *target_addr; + char *addr; + char *path; + int found = 0; + int ret; + DIR *slots_dir; + struct dirent *entry; + + if (!address) + return NULL; + + slots_dir = opendir(nvme_slots_sysfs_dir); + if (!slots_dir) { + nvme_msg(r, LOG_WARNING, "failed to open slots dir %s\n", + nvme_slots_sysfs_dir); + return NULL; + } + + target_addr = strndup(address, 10); + while (!(entry = readdir(slots_dir))) { + if (entry->d_type == DT_DIR && + strncmp(entry->d_name, ".", 1) != 0 && + strncmp(entry->d_name, "..", 2) != 0) { + ret = asprintf(&path, "/sys/bus/pci/slots/%s", entry->d_name); + if (ret < 0) { + errno = ENOMEM; + return NULL; + } + addr = nvme_get_attr(path, "address"); + if (strcmp(addr, target_addr) == 0) { + found = 1; + free(path); + free(addr); + break; + } + free(path); + free(addr); + } + } + free(target_addr); + if (found) + return strdup(entry->d_name); + return NULL; +} + static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, const char *name) { @@ -1224,6 +1352,7 @@ static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, } c->cntrltype = nvme_get_ctrl_attr(c, "cntrltype"); c->dctype = nvme_get_ctrl_attr(c, "dctype"); + c->phy_slot = nvme_ctrl_lookup_phy_slot(r, c->address); errno = 0; /* cleanup after nvme_get_ctrl_attr() */ return 0; @@ -1481,7 +1610,7 @@ static int nvme_bytes_to_lba(nvme_ns_t n, off_t offset, size_t count, int bs; bs = nvme_ns_get_lba_size(n); - if (!count || offset & bs || count & bs) { + if (!count || offset & (bs - 1) || count & (bs - 1)) { errno = EINVAL; return -1; } diff --git a/src/nvme/tree.h b/src/nvme/tree.h index e4a5126..bcf3636 100644 --- a/src/nvme/tree.h +++ b/src/nvme/tree.h @@ -45,6 +45,23 @@ typedef bool (*nvme_scan_filter_t)(nvme_subsystem_t, nvme_ctrl_t, nvme_root_t nvme_create_root(FILE *fp, int log_level); /** + * nvme_root_set_application - Specify managing application + * @r: &nvme_root_t object + * @a: Application string + * + * Sets the managing application string for @r. + */ +void nvme_root_set_application(nvme_root_t r, const char *a); + +/** + * nvme_root_get_application - Get managing application + * @r: &nvme_root_t object + * + * Returns the managing application string for @r or NULL if not set. + */ +const char *nvme_root_get_application(nvme_root_t r); + +/** * nvme_free_tree() - Free root object * @r: &nvme_root_t object * @@ -785,6 +802,15 @@ const char *nvme_ctrl_get_sysfs_dir(nvme_ctrl_t c); const char *nvme_ctrl_get_address(nvme_ctrl_t c); /** + * nvme_ctrl_get_phy_slot() - PCI physical slot number of a controller + * @c: Controller instance + * + * Return: PCI physical slot number of @c or empty string if slot + * number is not present. + */ +const char *nvme_ctrl_get_phy_slot(nvme_ctrl_t c); + +/** * nvme_ctrl_get_firmware() - Firmware string of a controller * @c: Controller instance * @@ -1105,6 +1131,23 @@ const char *nvme_subsystem_get_name(nvme_subsystem_t s); const char *nvme_subsystem_get_type(nvme_subsystem_t s); /** + * nvme_subsystem_get_application() - Return the application string + * @s: nvme_subsystem_t object + * + * Return: Managing application string or NULL if not set. + */ +const char *nvme_subsystem_get_application(nvme_subsystem_t s); + +/** + * nvme_subsystem_set_application() - Set the application string + * @s: nvme_subsystem_t object + * @a: application string + * + * Sets the managing application string for @s. + */ +void nvme_subsystem_set_application(nvme_subsystem_t s, const char *a); + +/** * nvme_scan_topology() - Scan NVMe topology and apply filter * @r: nvme_root_t object * @f: filter to apply diff --git a/src/nvme/types.h b/src/nvme/types.h index eed50ef..3bf2237 100644 --- a/src/nvme/types.h +++ b/src/nvme/types.h @@ -5941,6 +5941,13 @@ struct nvme_mi_vpd_hdr { * @NVME_SC_ADMIN_CMD_MEDIA_NOT_READY: Admin Command Media Not Ready: The Admin * command requires access to media and * the media is not ready. + * @NVME_SC_FDP_DISABLED: Command is not allowed when + * Flexible Data Placement is disabled. + * @NVME_SC_INVALID_PLACEMENT_HANDLE_LIST: The Placement Handle List is invalid + * due to invalid Reclaim Unit Handle Identifier or + * valid Reclaim Unit Handle Identifier but restricted or + * the Placement Handle List number of entries exceeded the + * maximum number allowed. * @NVME_SC_LBA_RANGE: LBA Out of Range: The command references * an LBA that exceeds the size of the namespace. * @NVME_SC_CAP_EXCEEDED: Capacity Exceeded: Execution of the @@ -6285,6 +6292,8 @@ enum nvme_status_field { NVME_SC_TRAN_TPORT_ERROR = 0x22, NVME_SC_PROHIBITED_BY_CMD_AND_FEAT = 0x23, NVME_SC_ADMIN_CMD_MEDIA_NOT_READY = 0x24, + NVME_SC_FDP_DISABLED = 0x29, + NVME_SC_INVALID_PLACEMENT_HANDLE_LIST = 0x2A, NVME_SC_LBA_RANGE = 0x80, NVME_SC_CAP_EXCEEDED = 0x81, NVME_SC_NS_NOT_READY = 0x82, @@ -7642,4 +7651,90 @@ enum nvme_io_mgmt_send_mo { NVME_IO_MGMT_SEND_RUH_UPDATE = 0x1, }; +#ifndef SWIG +/** + * struct nvme_ns_mgmt_host_sw_specified - Namespace management Host Software + * Specified Fields. + * @nsze: Namespace Size indicates the total size of the namespace in + * logical blocks. The number of logical blocks is based on the + * formatted LBA size. + * @ncap: Namespace Capacity indicates the maximum number of logical blocks + * that may be allocated in the namespace at any point in time. The + * number of logical blocks is based on the formatted LBA size. + * @rsvd16: Reserved + * @flbas: Formatted LBA Size, see &enum nvme_id_ns_flbas. + * @rsvd27: Reserved + * @dps: End-to-end Data Protection Type Settings, see + * &enum nvme_id_ns_dps. + * @nmic: Namespace Multi-path I/O and Namespace Sharing Capabilities, see + * &enum nvme_id_ns_nmic. + * @rsvd31: Reserved + * @anagrpid: ANA Group Identifier indicates the ANA Group Identifier of the + * ANA group of which the namespace is a member. + * @rsvd96: Reserved + * @nvmsetid: NVM Set Identifier indicates the NVM Set with which this + * namespace is associated. + * @endgid: Endurance Group Identifier indicates the Endurance Group with + * which this namespace is associated. + * @rsvd104: Reserved + * @lbstm: Logical Block Storage Tag Mask Identifies the mask for the + * Storage Tag field for the protection information + * @nphndls: Number of Placement Handles specifies the number of Placement + * Handles included in the Placement Handle List + * @rsvd394: Reserved + * @rsvd499: Reserved for I/O Command Sets that extend this specification. + * @zns: rsvd499( Zoned Namespace Command Set specific field ) + * @znsco: Zoned Namespace Create Options + * Bits 7-1: Reserved. + * Bits 0: Allocate ZRWA Resources (AZR): If set to ‘1’, then the + * namespace is to be created with the number of ZRWA resource specified + * in the RNUMZRWA field of this data structure. If cleared to ‘0’, then + * no ZRWA resources are allocated to the namespace to be created. If + * the ZRWASUP bit is cleared to ‘0’, then this field shall be ignored + * by the controller. + * @rar: Requested Active Resources specifies the number of active + * resources to be allocated to the created namespace. + * @ror: Requested Open Resources specifies the number of open resources + * to be allocated to the created namespace. + * @rnumzrwa: Requested Number of ZRWA Resources specifies the number of ZRWA + * resources to be allocated to the created namespace. + * see &struct nvme_ns_mgmt_host_sw_specified_zns. + * @phndl: Placement Handle Associated RUH : This field specifies the Reclaim + * Unit Handle Identifier to be associated with the Placement Handle + * value. If the Flexible Data Placement capability is not supported or + * not enabled in specified Endurance Group, then the controller shall + * ignore this field. + * @rsvd768: Reserved + */ +struct nvme_ns_mgmt_host_sw_specified { + __le64 nsze; + __le64 ncap; + __u8 rsvd16[10]; + __u8 flbas; + __u8 rsvd27[2]; + __u8 dps; + __u8 nmic; + __u8 rsvd31[61]; + __le32 anagrpid; + __u8 rsvd96[4]; + __le16 nvmsetid; + __le16 endgid; + __u8 rsvd104[280]; + __le64 lbstm; + __le16 nphndls; + __u8 rsvd394[105]; + union { + __u8 rsvd499[13]; + struct { + __u8 znsco; + __le32 rar; + __le32 ror; + __le32 rnumzrwa; + } __attribute__((packed)) zns; + }; + __le16 phndl[128]; + __u8 rsvd768[3328]; +}; +#endif /* SWIG */ + #endif /* _LIBNVME_TYPES_H */ diff --git a/src/nvme/util.c b/src/nvme/util.c index e7cbc8a..143cc31 100644 --- a/src/nvme/util.c +++ b/src/nvme/util.c @@ -8,6 +8,7 @@ */ #include <stdio.h> +#include <stdbool.h> #include <string.h> #include <errno.h> @@ -566,8 +567,9 @@ static const char * const libnvme_status[] = { [ENVME_CONNECT_INVAL] = "invalid arguments/configuration", [ENVME_CONNECT_ADDRINUSE] = "hostnqn already in use", [ENVME_CONNECT_NODEV] = "invalid interface", - [ENVME_CONNECT_OPNOTSUPP] ="not supported", + [ENVME_CONNECT_OPNOTSUPP] = "not supported", [ENVME_CONNECT_CONNREFUSED] = "connection refused", + [ENVME_CONNECT_ADDRNOTAVAIL] = "cannot assign requested address", }; const char *nvme_errno_to_string(int status) @@ -577,7 +579,7 @@ const char *nvme_errno_to_string(int status) return s; } -#ifdef HAVE_LIBNSS +#ifdef HAVE_NETDB char *hostname2traddr(struct nvme_root *r, const char *traddr) { struct addrinfo *host_info, hints = {.ai_family = AF_UNSPEC}; @@ -621,9 +623,7 @@ free_addrinfo: freeaddrinfo(host_info); return ret_traddr; } - -#else /* !HAVE_LIBNSS */ - +#else /* HAVE_NETDB */ char *hostname2traddr(struct nvme_root *r, const char *traddr) { nvme_msg(NULL, LOG_ERR, "No support for hostname IP address resolution; " \ @@ -632,7 +632,7 @@ char *hostname2traddr(struct nvme_root *r, const char *traddr) errno = -ENOTSUP; return NULL; } -#endif /* HAVE_LIBNSS */ +#endif /* HAVE_NETDB */ char *startswith(const char *s, const char *prefix) { @@ -904,3 +904,71 @@ int nvme_uuid_random(unsigned char uuid[NVME_UUID_LEN]) return 0; } + +#ifdef HAVE_NETDB +bool nvme_ipaddrs_eq(const char *addr1, const char *addr2) +{ + bool result = false; + struct addrinfo *info1 = NULL, hint1 = { .ai_flags=AI_NUMERICHOST, .ai_family=AF_UNSPEC }; + struct addrinfo *info2 = NULL, hint2 = { .ai_flags=AI_NUMERICHOST, .ai_family=AF_UNSPEC }; + + if (addr1 == addr2) + return true; + + if (!addr1 || !addr2) + return false; + + if (getaddrinfo(addr1, 0, &hint1, &info1) || !info1) + goto ipaddrs_eq_fail; + + if (getaddrinfo(addr2, 0, &hint2, &info2) || !info2) + goto ipaddrs_eq_fail; + + if (info1->ai_family == AF_INET && info2->ai_family == AF_INET) { + struct sockaddr_in *sockaddr1 = (struct sockaddr_in *)(info1->ai_addr); + struct sockaddr_in *sockaddr2 = (struct sockaddr_in *)(info2->ai_addr); + result = sockaddr1->sin_addr.s_addr == sockaddr2->sin_addr.s_addr; + } else if (info1->ai_family == AF_INET6 && info2->ai_family == AF_INET6) { + struct sockaddr_in6 *sockaddr1 = (struct sockaddr_in6 *)(info1->ai_addr); + struct sockaddr_in6 *sockaddr2 = (struct sockaddr_in6 *)(info2->ai_addr); + result = !memcmp(&sockaddr1->sin6_addr, &sockaddr2->sin6_addr, sizeof(struct in6_addr)); + } else { + struct sockaddr_in *sockaddr_v4; + struct sockaddr_in6 *sockaddr_v6; + switch (info1->ai_family) { + case AF_INET: + sockaddr_v6 = (struct sockaddr_in6 *)(info2->ai_addr); + if (IN6_IS_ADDR_V4MAPPED(&sockaddr_v6->sin6_addr)) { + sockaddr_v4 = (struct sockaddr_in *)(info1->ai_addr); + result = sockaddr_v4->sin_addr.s_addr == sockaddr_v6->sin6_addr.s6_addr32[3]; + } + break; + + case AF_INET6: + sockaddr_v6 = (struct sockaddr_in6 *)(info1->ai_addr); + if (IN6_IS_ADDR_V4MAPPED(&sockaddr_v6->sin6_addr)) { + sockaddr_v4 = (struct sockaddr_in *)(info2->ai_addr); + result = sockaddr_v4->sin_addr.s_addr == sockaddr_v6->sin6_addr.s6_addr32[3]; + } + break; + + default: ; + } + } + +ipaddrs_eq_fail: + if (info1) + freeaddrinfo(info1); + if (info2) + freeaddrinfo(info2); + return result; +} +#else /* HAVE_NETDB */ +bool nvme_ipaddrs_eq(const char *addr1, const char *addr2) +{ + nvme_msg(NULL, LOG_ERR, "no support for hostname ip address resolution; " \ + "recompile with libnss support.\n"); + + return false; +} +#endif /* HAVE_NETDB */ diff --git a/src/nvme/util.h b/src/nvme/util.h index 961da18..9d6faf3 100644 --- a/src/nvme/util.h +++ b/src/nvme/util.h @@ -36,7 +36,9 @@ * @ENVME_CONNECT_ADDRINUSE: hostnqn already in use * @ENVME_CONNECT_NODEV: invalid interface * @ENVME_CONNECT_OPNOTSUPP: not supported - * @ENVME_CONNECT_CONNREFUSED: connection refused + * @ENVME_CONNECT_CONNREFUSED: connection refused + * @ENVME_CONNECT_ADDRNOTAVAIL: cannot assign requested address + * @ENVME_CONNECT_IGNORED: connect attempt is ignored due to configuration */ enum nvme_connect_err { ENVME_CONNECT_RESOLVE = 1000, @@ -57,6 +59,8 @@ enum nvme_connect_err { ENVME_CONNECT_NODEV, ENVME_CONNECT_OPNOTSUPP, ENVME_CONNECT_CONNREFUSED, + ENVME_CONNECT_ADDRNOTAVAIL, + ENVME_CONNECT_IGNORED, }; /** @@ -626,4 +630,13 @@ int nvme_uuid_from_string(const char *str, unsigned char uuid[NVME_UUID_LEN]); */ int nvme_uuid_random(unsigned char uuid[NVME_UUID_LEN]); +/** + * nvme_ipaddrs_eq - Check if 2 IP addresses are equal. + * @addr1: IP address (can be IPv4 or IPv6) + * @addr2: IP address (can be IPv4 or IPv6) + * + * Return: true if addr1 == addr2. false otherwise. + */ +bool nvme_ipaddrs_eq(const char *addr1, const char *addr2); + #endif /* _LIBNVME_UTIL_H */ |