summaryrefslogtreecommitdiffstats
path: root/src/nvme
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvme')
-rw-r--r--src/nvme/api-types.h6
-rw-r--r--src/nvme/fabrics.c333
-rw-r--r--src/nvme/ioctl.c35
-rw-r--r--src/nvme/ioctl.h10
-rw-r--r--src/nvme/json.c40
-rw-r--r--src/nvme/mi.c35
-rw-r--r--src/nvme/mi.h20
-rw-r--r--src/nvme/nbft.c726
-rw-r--r--src/nvme/nbft.h1238
-rw-r--r--src/nvme/private.h36
-rw-r--r--src/nvme/tree.c141
-rw-r--r--src/nvme/tree.h43
-rw-r--r--src/nvme/types.h95
-rw-r--r--src/nvme/util.c80
-rw-r--r--src/nvme/util.h15
15 files changed, 2768 insertions, 85 deletions
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 */