diff --git a/.gitignore b/.gitignore index e28004b..03375fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/libnvme-1.2.tar.gz +SOURCES/libnvme-1.4.tar.gz diff --git a/.libnvme.metadata b/.libnvme.metadata index c278f6a..0377cce 100644 --- a/.libnvme.metadata +++ b/.libnvme.metadata @@ -1 +1 @@ -32d5f81b7af835e5596cb390f7dd2ac889414e1d SOURCES/libnvme-1.2.tar.gz +1708e8659912cb695c3ac618a28518027f5833db SOURCES/libnvme-1.4.tar.gz diff --git a/SOURCES/0001-fabrics-Do-not-pass-unsupported-options-to-kernel.patch b/SOURCES/0001-fabrics-Do-not-pass-unsupported-options-to-kernel.patch new file mode 100644 index 0000000..1e95098 --- /dev/null +++ b/SOURCES/0001-fabrics-Do-not-pass-unsupported-options-to-kernel.patch @@ -0,0 +1,399 @@ +From d123131f2e542e5a4c046cb65a68fc1fb97ea384 Mon Sep 17 00:00:00 2001 +From: Daniel Wagner +Date: Wed, 12 Apr 2023 11:59:45 +0200 +Subject: [PATCH] fabrics: Do not pass unsupported options to kernel + +The kernel API might not support all options libnvme is supporting. +Filter out all options which the kernel doesn't support. + +Signed-off-by: Daniel Wagner +--- + src/nvme/fabrics.c | 216 ++++++++++++++++++++++++++++++++++++++------- + src/nvme/private.h | 33 +++++++ + src/nvme/tree.c | 1 + + 3 files changed, 220 insertions(+), 30 deletions(-) + +diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c +index 3c32e27..f9c26fe 100644 +--- a/src/nvme/fabrics.c ++++ b/src/nvme/fabrics.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + #include "fabrics.h" + #include "linux.h" +@@ -261,7 +262,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 +278,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 +294,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 +310,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 +326,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 +519,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 +554,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 +615,92 @@ 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; ++ size_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) { ++ 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); +@@ -671,6 +824,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; +diff --git a/src/nvme/private.h b/src/nvme/private.h +index a6ded21..47ce7ca 100644 +--- a/src/nvme/private.h ++++ b/src/nvme/private.h +@@ -120,6 +120,38 @@ 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; + struct list_head hosts; +@@ -130,6 +162,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 6b58483..c649408 100644 +--- a/src/nvme/tree.c ++++ b/src/nvme/tree.c +@@ -288,6 +288,7 @@ 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) +-- +2.31.1 + diff --git a/SOURCES/0001-fabrics-Fix-bad-UUID-size-introduced-in-recent-UUID-.patch b/SOURCES/0001-fabrics-Fix-bad-UUID-size-introduced-in-recent-UUID-.patch deleted file mode 100644 index c5fc11a..0000000 --- a/SOURCES/0001-fabrics-Fix-bad-UUID-size-introduced-in-recent-UUID-.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 78ce3528d00bb433c661fd24672a1b5c6795b59f Mon Sep 17 00:00:00 2001 -From: Martin Belanger -Date: Fri, 18 Nov 2022 10:41:32 -0500 -Subject: [PATCH] fabrics: Fix bad UUID size introduced in recent UUID changes -Content-type: text/plain - -71c25d1cf741 ("util: Add simple UUID type") introduced a regression in -nvmf_get_tel(). nvmf_get_tel() returns the lenght of the binary -representation. Hence use NVME_UUID_LEN instead. - -Signed-off-by: Martin Belanger -[dwagner: massaged commit message] -Signed-off-by: Daniel Wagner ---- - src/nvme/fabrics.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c -index f943090..36bdc2d 100644 ---- a/src/nvme/fabrics.c -+++ b/src/nvme/fabrics.c -@@ -1127,7 +1127,7 @@ static __u32 nvmf_get_tel(const char *hostsymname) - __u16 len; - - /* Host ID is mandatory */ -- tel += nvmf_exat_size(NVME_UUID_LEN_STRING); -+ tel += nvmf_exat_size(NVME_UUID_LEN); - - /* Symbolic name is optional */ - len = hostsymname ? strlen(hostsymname) : 0; --- -2.31.1 - diff --git a/SOURCES/0002-nbft-add-NBFT-v1.0-table-support.patch b/SOURCES/0002-nbft-add-NBFT-v1.0-table-support.patch new file mode 100644 index 0000000..97f53ab --- /dev/null +++ b/SOURCES/0002-nbft-add-NBFT-v1.0-table-support.patch @@ -0,0 +1,2082 @@ +From 28ad0e9f6ff0bdd56c6afae73072299c4cd71c3a Mon Sep 17 00:00:00 2001 +From: Stuart Hayes +Date: Thu, 31 Mar 2022 13:47:11 -0500 +Subject: [PATCH] nbft: add NBFT v1.0 table support + +Added support for parsing and printing the contents +of the NBFT table (per NVMe-oF boot specification v1.0). + +Signed-off-by: Stuart Hayes +Signed-off-by: Martin Belanger +Signed-off-by: Martin Wilck +Signed-off-by: Tomas Bzatek +Signed-off-by: John Meneghini +--- + doc/meson.build | 3 +- + src/libnvme.h | 1 + + src/libnvme.map | 2 + + src/meson.build | 2 + + src/nvme/nbft.c | 739 ++++++++++++++++++++++++++++ + src/nvme/nbft.h | 1238 +++++++++++++++++++++++++++++++++++++++++++++++ + 6 files changed, 1984 insertions(+), 1 deletion(-) + create mode 100644 src/nvme/nbft.c + create mode 100644 src/nvme/nbft.h + +diff --git a/doc/meson.build b/doc/meson.build +index edbc3be..480db17 100644 +--- a/doc/meson.build ++++ b/doc/meson.build +@@ -9,14 +9,15 @@ + # + + api_files = [ ++ 'fabrics.h', + 'filters.h', + 'ioctl.h', + 'linux.h', + 'log.h', + 'mi.h', ++ 'nbft.h', + 'tree.h', + 'types.h', +- 'fabrics.h', + 'util.h' + ] + +diff --git a/src/libnvme.h b/src/libnvme.h +index 6be9058..2c7fe3a 100644 +--- a/src/libnvme.h ++++ b/src/libnvme.h +@@ -17,6 +17,7 @@ extern "C" { + #include "nvme/types.h" + #include "nvme/linux.h" + #include "nvme/ioctl.h" ++#include "nvme/nbft.h" + #include "nvme/fabrics.h" + #include "nvme/filters.h" + #include "nvme/tree.h" +diff --git a/src/libnvme.map b/src/libnvme.map +index a1294f4..6aa9fd0 100644 +--- a/src/libnvme.map ++++ b/src/libnvme.map +@@ -7,6 +7,8 @@ LIBNVME_1_4 { + nvme_lookup_key; + nvme_set_keyring; + nvme_insert_tls_key; ++ nvme_nbft_read; ++ nvme_nbft_free; + }; + + LIBNVME_1_3 { +diff --git a/src/meson.build b/src/meson.build +index 3732f8c..e8b667c 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -7,6 +7,7 @@ + # + sources = [ + 'nvme/cleanup.c', ++ 'nvme/nbft.c', + 'nvme/fabrics.c', + 'nvme/filters.c', + 'nvme/ioctl.c', +@@ -125,6 +126,7 @@ install_headers([ + 'nvme/ioctl.h', + 'nvme/linux.h', + 'nvme/log.h', ++ 'nvme/nbft.h', + 'nvme/tree.h', + 'nvme/types.h', + 'nvme/util.h', +diff --git a/src/nvme/nbft.c b/src/nvme/nbft.c +new file mode 100644 +index 0000000..f91d21b +--- /dev/null ++++ b/src/nvme/nbft.c +@@ -0,0 +1,739 @@ ++// 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 ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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) == le16_to_cpu(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 (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; ++ ++ /* ++ * 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; ++} ++ ++/** ++ * nvme_nbft_free() - Free the struct nbft_info and its contents ++ * @nbft: Parsed NBFT table data. ++ */ ++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); ++} ++ ++/** ++ * 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 nbft_free(). ++ * ++ * Return: 0 on success, errno otherwise. ++ */ ++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..c3caa85 +--- /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 ++ * ++ */ ++#ifndef _NBFT_H ++#define _NBFT_H ++ ++#include ++#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 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 +-- +2.39.1 + diff --git a/SOURCES/0003-nbft-Move-added-symbols-to-LIBNVME_1_5.patch b/SOURCES/0003-nbft-Move-added-symbols-to-LIBNVME_1_5.patch new file mode 100644 index 0000000..23689ea --- /dev/null +++ b/SOURCES/0003-nbft-Move-added-symbols-to-LIBNVME_1_5.patch @@ -0,0 +1,37 @@ +From 670ec98ebc986e62267145abb059b66ac5e51380 Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Thu, 13 Apr 2023 15:39:28 +0200 +Subject: [PATCH] nbft: Move added symbols to LIBNVME_1_5 + +--- + src/libnvme.map | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/libnvme.map b/src/libnvme.map +index 6aa9fd0..28de595 100644 +--- a/src/libnvme.map ++++ b/src/libnvme.map +@@ -1,5 +1,11 @@ + # SPDX-License-Identifier: LGPL-2.1-or-later + ++LIBNVME_1_5 { ++ global: ++ nvme_nbft_read; ++ nvme_nbft_free; ++}; ++ + LIBNVME_1_4 { + global: + nvme_lookup_keyring; +@@ -7,8 +13,6 @@ LIBNVME_1_4 { + nvme_lookup_key; + nvme_set_keyring; + nvme_insert_tls_key; +- nvme_nbft_read; +- nvme_nbft_free; + }; + + LIBNVME_1_3 { +-- +2.39.1 + diff --git a/SOURCES/0004-nbft-Fix-nbft_ssns_flags-endianness-test.patch b/SOURCES/0004-nbft-Fix-nbft_ssns_flags-endianness-test.patch new file mode 100644 index 0000000..783a2cb --- /dev/null +++ b/SOURCES/0004-nbft-Fix-nbft_ssns_flags-endianness-test.patch @@ -0,0 +1,36 @@ +From 26e4343c2ba2db7a3c5696bbf61bb87942ac02bb Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Thu, 13 Apr 2023 17:28:42 +0200 +Subject: [PATCH] nbft: Fix nbft_ssns_flags endianness test + +Missing flags endianness conversion leading to ssns_ext_info +not being parsed on s390x and armhf. +--- + src/nvme/nbft.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/nvme/nbft.c b/src/nvme/nbft.c +index f91d21b..940dd8e 100644 +--- a/src/nvme/nbft.c ++++ b/src/nvme/nbft.c +@@ -169,7 +169,7 @@ static int read_ssns_exended_info(struct nbft_info *nbft, + "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) == le16_to_cpu(ssns->index), ++ 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)) +@@ -292,7 +292,7 @@ static int read_ssns(struct nbft_info *nbft, + goto fail; + + /* SSNS extended info */ +- if (raw_ssns->flags & NBFT_SSNS_EXTENDED_INFO_IN_USE) { ++ 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, +-- +2.39.1 + diff --git a/SOURCES/0005-nbft-Parse-the-HOSTID-HOSTNQN-_CONFIGURED-flags.patch b/SOURCES/0005-nbft-Parse-the-HOSTID-HOSTNQN-_CONFIGURED-flags.patch new file mode 100644 index 0000000..b9ff1ae --- /dev/null +++ b/SOURCES/0005-nbft-Parse-the-HOSTID-HOSTNQN-_CONFIGURED-flags.patch @@ -0,0 +1,25 @@ +From 1617d1a3f42a25a2e99073811174609abcffc34d Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Thu, 13 Apr 2023 18:27:39 +0200 +Subject: [PATCH] nbft: Parse the {HOSTID,HOSTNQN}_CONFIGURED flags + +--- + src/nvme/nbft.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/nvme/nbft.c b/src/nvme/nbft.c +index 940dd8e..c0af2b3 100644 +--- a/src/nvme/nbft.c ++++ b/src/nvme/nbft.c +@@ -560,6 +560,8 @@ static int parse_raw_nbft(struct nbft_info *nbft) + 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 +-- +2.39.1 + diff --git a/SOURCES/0006-nbft-Doc-typo-Use-nvme_nbft_free-instead-of-nbft_fre.patch b/SOURCES/0006-nbft-Doc-typo-Use-nvme_nbft_free-instead-of-nbft_fre.patch new file mode 100644 index 0000000..11f560d --- /dev/null +++ b/SOURCES/0006-nbft-Doc-typo-Use-nvme_nbft_free-instead-of-nbft_fre.patch @@ -0,0 +1,26 @@ +From 00b48dd3c217a9271c1888e8dbeb4aa9d307e5bf Mon Sep 17 00:00:00 2001 +From: Martin Belanger +Date: Thu, 13 Apr 2023 09:27:04 -0400 +Subject: [PATCH] nbft: Doc typo - Use nvme_nbft_free() instead of nbft_free() + +Signed-off-by: Martin Belanger +--- + src/nvme/nbft.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/nvme/nbft.c b/src/nvme/nbft.c +index c0af2b3..a085768 100644 +--- a/src/nvme/nbft.c ++++ b/src/nvme/nbft.c +@@ -663,7 +663,7 @@ void nvme_nbft_free(struct nbft_info *nbft) + * @filename: Filename of the raw NBFT table to read. + * + * Read and parse the specified NBFT file into a struct nbft_info. +- * Free with nbft_free(). ++ * Free with nvme_nbft_free(). + * + * Return: 0 on success, errno otherwise. + */ +-- +2.39.1 + diff --git a/SOURCES/0007-NBFT-Remove-documentation-from-nbft.c-since-it-s-als.patch b/SOURCES/0007-NBFT-Remove-documentation-from-nbft.c-since-it-s-als.patch new file mode 100644 index 0000000..e4daaff --- /dev/null +++ b/SOURCES/0007-NBFT-Remove-documentation-from-nbft.c-since-it-s-als.patch @@ -0,0 +1,64 @@ +From 961606f0d0547c3eebd47b79c363ab28c95a94ea Mon Sep 17 00:00:00 2001 +From: Martin Belanger +Date: Fri, 14 Apr 2023 11:19:23 -0400 +Subject: [PATCH] NBFT: Remove documentation from nbft.c since it's also in + nbft.h + +Also, replace nbft_free() by nvme_nbft_free() in documentation +found in nbft.h. + +Signed-off-by: Martin Belanger +--- + src/nvme/nbft.c | 15 --------------- + src/nvme/nbft.h | 2 +- + 2 files changed, 1 insertion(+), 16 deletions(-) + +diff --git a/src/nvme/nbft.c b/src/nvme/nbft.c +index a085768..a1e17cd 100644 +--- a/src/nvme/nbft.c ++++ b/src/nvme/nbft.c +@@ -626,10 +626,6 @@ static int parse_raw_nbft(struct nbft_info *nbft) + return 0; + } + +-/** +- * nvme_nbft_free() - Free the struct nbft_info and its contents +- * @nbft: Parsed NBFT table data. +- */ + void nvme_nbft_free(struct nbft_info *nbft) + { + struct nbft_info_hfi **hfi; +@@ -656,17 +652,6 @@ void nvme_nbft_free(struct nbft_info *nbft) + free(nbft); + } + +-/** +- * 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) + { + __u8 *raw_nbft = NULL; +diff --git a/src/nvme/nbft.h b/src/nvme/nbft.h +index c3caa85..6012e16 100644 +--- a/src/nvme/nbft.h ++++ b/src/nvme/nbft.h +@@ -1223,7 +1223,7 @@ struct nbft_info { + * @filename: Filename of the raw NBFT table to read. + * + * Read and parse the specified NBFT file into a struct nbft_info. +- * Free with nbft_free(). ++ * Free with nvme_nbft_free(). + * + * Return: 0 on success, errno otherwise. + */ +-- +2.39.1 + diff --git a/SOURCES/0008-fabrics-check-genctr-after-getting-discovery-entries.patch b/SOURCES/0008-fabrics-check-genctr-after-getting-discovery-entries.patch new file mode 100644 index 0000000..9510007 --- /dev/null +++ b/SOURCES/0008-fabrics-check-genctr-after-getting-discovery-entries.patch @@ -0,0 +1,105 @@ +From 4204cb3c79219926f750ede2d7e8b23a3852e72d Mon Sep 17 00:00:00 2001 +From: Caleb Sander +Date: Fri, 12 May 2023 10:49:46 -0600 +Subject: [PATCH] fabrics: check genctr after getting discovery entries +Content-type: text/plain + +From the NVMe base spec (version 2.0c, section 5.16.1.23): +If the host reads the Discovery Log Page using multiple Get Log Page +commands the host should ensure that there has not been a change in the +contents of the data. The host should read the Discovery Log Page +contents in order (i.e., with increasing Log Page Offset values) and +then re-read the Generation Counter after the entire log page is +transferred. If the Generation Counter does not match the original value +read, the host should discard the log page read as the entries may be +inconsistent. + +nvme_get_log_page() will issue multiple Get Log Page commands +to fetch the discovery log page if it exceeds 4 KB. +Since GENCTR is at the start of the log page, this ordering is possible: +- GENCTR is read by a Get Log Page command for the first 4 KB +- The log page is modified, changing GENCTR +- Other Get Log Page commands read the remainder of the log page +So the check that GENCTR hasn't changed will incorrectly pass, +despite the log page having been modified. +This can lead to inconsistent, missing, or duplicate log page entries. + +Ensure a GENCTR update is not missed +by fetching log page header again after all entries. + +Also use NVME_LOG_PAGE_PDU_SIZE to match other nvme_get_log_page() calls +instead of hard-coding the 4 KB max transfer length. +And ensure LPO is correctly reset if the log page is read again. + +Signed-off-by: Caleb Sander +--- + src/nvme/fabrics.c | 32 ++++++++++++++++++++++++++------ + 1 file changed, 26 insertions(+), 6 deletions(-) + +diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c +index 1762898..eaee29b 100644 +--- a/src/nvme/fabrics.c ++++ b/src/nvme/fabrics.c +@@ -1036,9 +1036,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", +@@ -1065,15 +1066,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", +@@ -1088,7 +1107,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 { +-- +2.39.3 + diff --git a/SOURCES/0009-ioctl-fix-RAE-bit-on-last-Get-Log-Page-command.patch b/SOURCES/0009-ioctl-fix-RAE-bit-on-last-Get-Log-Page-command.patch new file mode 100644 index 0000000..7d3da41 --- /dev/null +++ b/SOURCES/0009-ioctl-fix-RAE-bit-on-last-Get-Log-Page-command.patch @@ -0,0 +1,49 @@ +From 777b52152f8137048b72edc12ad2ae998df4c30a Mon Sep 17 00:00:00 2001 +From: Caleb Sander +Date: Fri, 12 May 2023 09:43:22 -0600 +Subject: [PATCH] ioctl: fix RAE bit on last Get Log Page command +Content-type: text/plain + +If nvme_get_log_page() requires multiple Get Log Page commands +because the total log length exceeds the transfer length, +args->rae is overwritten, causing the RAE bit to be set in all commands. +Retrieve the value of args->rae before overwriting it +so the RAE bit is set as requested in the last command. + +Fixes: c23dbd4 ("linux: Change nvme_get_log_page to use nvme_get_log_args parm") +Signed-off-by: Caleb Sander +--- + src/nvme/ioctl.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c +index 6f9d724..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; +-- +2.39.3 + diff --git a/SPECS/libnvme.spec b/SPECS/libnvme.spec index da747c4..48a0abd 100644 --- a/SPECS/libnvme.spec +++ b/SPECS/libnvme.spec @@ -3,20 +3,31 @@ Name: libnvme Summary: Linux-native nvme device management library -Version: 1.2 -Release: 2%{?dist} +Version: 1.4 +Release: 7%{?dist} License: LGPLv2+ URL: https://github.com/linux-nvme/libnvme Source0: %{url}/archive/v%{version_no_tilde}/%{name}-%{version_no_tilde}.tar.gz -Patch0: 0001-fabrics-Fix-bad-UUID-size-introduced-in-recent-UUID-.patch +Patch0: 0001-fabrics-Do-not-pass-unsupported-options-to-kernel.patch +Patch1: 0002-nbft-add-NBFT-v1.0-table-support.patch +Patch2: 0003-nbft-Move-added-symbols-to-LIBNVME_1_5.patch +Patch3: 0004-nbft-Fix-nbft_ssns_flags-endianness-test.patch +Patch4: 0005-nbft-Parse-the-HOSTID-HOSTNQN-_CONFIGURED-flags.patch +Patch5: 0006-nbft-Doc-typo-Use-nvme_nbft_free-instead-of-nbft_fre.patch +Patch6: 0007-NBFT-Remove-documentation-from-nbft.c-since-it-s-als.patch +Patch7: 0008-fabrics-check-genctr-after-getting-discovery-entries.patch +Patch8: 0009-ioctl-fix-RAE-bit-on-last-Get-Log-Page-command.patch BuildRequires: gcc gcc-c++ BuildRequires: swig BuildRequires: python3-devel -BuildRequires: meson >= 0.48.0 +BuildRequires: meson >= 0.50.0 BuildRequires: json-c-devel >= 0.13 BuildRequires: openssl-devel +BuildRequires: keyutils-libs-devel + +Requires: keyutils-libs %description Provides type definitions for NVMe specification structures, @@ -45,7 +56,6 @@ This package contains the reference manual for %{name}. Summary: Python3 bindings for libnvme Requires: %{name}%{?_isa} = %{version}-%{release} Provides: python3-nvme = %{version}-%{release} -Obsoletes: python3-nvme < 1.0~rc7 %{?python_provide:%python_provide python3-libnvme} %description -n python3-libnvme @@ -55,7 +65,7 @@ This package contains Python bindings for libnvme. %autosetup -p1 -n %{name}-%{version_no_tilde} %build -%meson -Dpython=true -Ddocs=all -Ddocs-build=true -Dhtmldir=%{_pkgdocdir} +%meson -Dpython=enabled -Dlibdbus=disabled -Ddocs=all -Ddocs-build=true -Dhtmldir=%{_pkgdocdir} %meson_build %install @@ -71,9 +81,9 @@ mv %{buildroot}/usr/*.rst %{buildroot}%{_pkgdocdir}/ %files %license COPYING ccan/licenses/* %{_libdir}/libnvme.so.1 -%{_libdir}/libnvme.so.1.2.0 +%{_libdir}/libnvme.so.1.4.0 %{_libdir}/libnvme-mi.so.1 -%{_libdir}/libnvme-mi.so.1.2.0 +%{_libdir}/libnvme-mi.so.1.4.0 %files devel %{_libdir}/libnvme.so @@ -93,6 +103,27 @@ mv %{buildroot}/usr/*.rst %{buildroot}%{_pkgdocdir}/ %{python3_sitearch}/libnvme/* %changelog +* Mon Jul 17 2023 John Meneghini - 1.4-7 +- Fix BZ#2223429 + +* Mon Jun 05 2023 Maurizio Lombardi - 1.4-6 +- Rebuild for BZ2212307 + +* Tue May 16 2023 Maurizio Lombardi - 1.4-5 +- Add support to NBFT (BZ2188516) + +* Mon May 08 2023 Maurizio Lombardi - 1.4-4 +- Fix BZ#2190206 + +* Fri May 05 2023 Maurizio Lombardi - 1.4-3 +- Fix Jira RHEL-258 + +* Thu Apr 06 2023 Maurizio Lombardi - 1.4-2 +- Rebuild the package + +* Mon Apr 03 2023 Maurizio Lombardi - 1.4-1 +- Update to version 1.4 + * Thu Jan 12 2023 John Meneghini - 1.2-2 - Fix BZ2158264