Blob Blame History Raw
From 28ad0e9f6ff0bdd56c6afae73072299c4cd71c3a Mon Sep 17 00:00:00 2001
From: Stuart Hayes <stuart_hayes@dell.com>
Date: Thu, 31 Mar 2022 13:47:11 -0500
Subject: [PATCH 01/18] 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 <stuart_hayes@dell.com>
Signed-off-by: Martin Belanger <martin.belanger@dell.com>
Signed-off-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
Signed-off-by: John Meneghini <jmeneghi@redhat.com>
---
 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 <Stuart_Hayes@Dell.com>
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <arpa/inet.h>
+#include <ccan/endian/endian.h>
+
+#include "private.h"
+#include "nbft.h"
+#include "log.h"
+
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+static __u8 csum(const __u8 *buffer, ssize_t length)
+{
+	int n;
+	__u8 sum = 0;
+
+	for (n = 0; n < length; n++)
+		sum = (__u8)(sum + ((__u8 *)buffer)[n]);
+	return sum;
+}
+
+static void format_ip_addr(char *buf, size_t buflen, __u8 *addr)
+{
+	struct in6_addr *addr_ipv6;
+
+	addr_ipv6 = (struct in6_addr *)addr;
+	if (addr_ipv6->s6_addr32[0] == 0 &&
+	    addr_ipv6->s6_addr32[1] == 0 &&
+	    ntohl(addr_ipv6->s6_addr32[2]) == 0xffff)
+		/* ipv4 */
+		inet_ntop(AF_INET, &(addr_ipv6->s6_addr32[3]), buf, buflen);
+	else
+		/* ipv6 */
+		inet_ntop(AF_INET6, addr_ipv6, buf, buflen);
+}
+
+static bool in_heap(struct nbft_header *header, struct nbft_heap_obj obj)
+{
+	if (le16_to_cpu(obj.length) == 0)
+		return true;
+	if (le32_to_cpu(obj.offset) < le32_to_cpu(header->heap_offset))
+		return false;
+	if (le32_to_cpu(obj.offset) >
+	    le32_to_cpu(header->heap_offset) + le32_to_cpu(header->heap_length))
+		return false;
+	if (le32_to_cpu(obj.offset) + le16_to_cpu(obj.length) >
+	    le32_to_cpu(header->heap_offset) + le32_to_cpu(header->heap_length))
+		return false;
+	return true;
+}
+
+/*
+ *  Return transport_type string (NBFT Table 2)
+ */
+static char *trtype_to_string(__u8 transport_type)
+{
+	switch (transport_type) {
+	case 3:
+		return "tcp";
+	default:
+		return "invalid";
+	}
+}
+
+#define verify(condition, message)					\
+	do {								\
+		if (!(condition)) {					\
+			nvme_msg(NULL, LOG_DEBUG, "file %s: " message "\n", \
+				 nbft->filename);			\
+			return -EINVAL;					\
+		}							\
+	} while (0)
+
+static int __get_heap_obj(struct nbft_header *header, const char *filename,
+			  const char *descriptorname, const char *fieldname,
+			  struct nbft_heap_obj obj, bool is_string,
+			  char **output)
+{
+	if (le16_to_cpu(obj.length) == 0)
+		return -ENOENT;
+
+	if (!in_heap(header, obj)) {
+		nvme_msg(NULL, LOG_DEBUG,
+			 "file %s: field '%s' in descriptor '%s' has invalid offset or length\n",
+			 filename, fieldname, descriptorname);
+		return -EINVAL;
+	}
+
+	/* check that string is zero terminated correctly */
+	*output = (char *)header + le32_to_cpu(obj.offset);
+
+	if (is_string) {
+		if (strnlen(*output, le16_to_cpu(obj.length) + 1) < le16_to_cpu(obj.length))
+			nvme_msg(NULL, LOG_DEBUG,
+				"file %s: string '%s' in descriptor '%s' is shorter (%zd) than specified length (%d)\n",
+				filename, fieldname, descriptorname,
+				strnlen(*output, le16_to_cpu(obj.length) + 1),
+					le16_to_cpu(obj.length));
+		else if (strnlen(*output, le16_to_cpu(obj.length) + 1) >
+			le16_to_cpu(obj.length)) {
+			nvme_msg(NULL, LOG_DEBUG,
+				 "file %s: string '%s' in descriptor '%s' is not zero terminated\n",
+				 filename, fieldname, descriptorname);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+#define get_heap_obj(descriptor, obj, is_string, output)	\
+	__get_heap_obj(header, nbft->filename,			\
+		       stringify(descriptor), stringify(obj),	\
+		       descriptor->obj, is_string,		\
+		       output)
+
+static struct nbft_info_discovery *discovery_from_index(struct nbft_info *nbft, int i)
+{
+	struct nbft_info_discovery **d;
+
+	for (d = nbft->discovery_list; d && *d; d++) {
+		if ((*d)->index == i)
+			return *d;
+	}
+	return NULL;
+}
+
+static struct nbft_info_hfi *hfi_from_index(struct nbft_info *nbft, int i)
+{
+	struct nbft_info_hfi **h;
+
+	for (h = nbft->hfi_list; h && *h; h++) {
+		if ((*h)->index == i)
+			return *h;
+	}
+	return NULL;
+}
+
+static struct nbft_info_security *security_from_index(struct nbft_info *nbft, int i)
+{
+	struct nbft_info_security **s;
+
+	for (s = nbft->security_list; s && *s; s++) {
+		if ((*s)->index == i)
+			return *s;
+	}
+	return NULL;
+}
+
+static int read_ssns_exended_info(struct nbft_info *nbft,
+				  struct nbft_info_subsystem_ns *ssns,
+				  struct nbft_ssns_ext_info *raw_ssns_ei)
+{
+	struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft;
+
+	verify(raw_ssns_ei->structure_id == NBFT_DESC_SSNS_EXT_INFO,
+	       "invalid ID in SSNS extended info descriptor");
+	verify(raw_ssns_ei->version == 1,
+	       "invalid version in SSNS extended info descriptor");
+	verify(le16_to_cpu(raw_ssns_ei->ssns_index) == 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 <Stuart_Hayes@Dell.com>
+ *
+ */
+#ifndef _NBFT_H
+#define _NBFT_H
+
+#include <sys/types.h>
+#include "util.h"
+
+/*
+ *  ACPI NBFT table structures (TP8012 Boot Specification rev. 1.0)
+ */
+
+/**
+ * enum nbft_desc_type - NBFT Elements - Descriptor Types (Figure 5)
+ * @NBFT_DESC_HEADER:	     Header: an ACPI structure header with some additional
+ *			     NBFT specific info.
+ * @NBFT_DESC_CONTROL:	     Control Descriptor: indicates the location of host,
+ *			     HFI, SSNS, security, and discovery descriptors.
+ * @NBFT_DESC_HOST:	     Host Descriptor: host information.
+ * @NBFT_DESC_HFI:	     HFI Descriptor: an indexable table of HFI Descriptors,
+ *			     one for each fabric interface on the host.
+ * @NBFT_DESC_SSNS:	     Subsystem Namespace Descriptor: an indexable table
+ *			     of SSNS Descriptors.
+ * @NBFT_DESC_SECURITY:	     Security Descriptor: an indexable table of Security
+ *			     descriptors.
+ * @NBFT_DESC_DISCOVERY:     Discovery Descriptor: an indexable table of Discovery
+ *			     Descriptors.
+ * @NBFT_DESC_HFI_TRINFO:    HFI Transport Descriptor: indicated by an HFI Descriptor,
+ *			     corresponds to a specific transport for a single HFI.
+ * @NBFT_DESC_RESERVED_8:    Reserved.
+ * @NBFT_DESC_SSNS_EXT_INFO: SSNS Extended Info Descriptor: indicated by an SSNS
+ *			     Descriptor if required.
+ */
+enum nbft_desc_type {
+	NBFT_DESC_HEADER	= 0,
+	NBFT_DESC_CONTROL	= 1,
+	NBFT_DESC_HOST		= 2,
+	NBFT_DESC_HFI		= 3,
+	NBFT_DESC_SSNS		= 4,
+	NBFT_DESC_SECURITY	= 5,
+	NBFT_DESC_DISCOVERY	= 6,
+	NBFT_DESC_HFI_TRINFO	= 7,
+	NBFT_DESC_RESERVED_8	= 8,
+	NBFT_DESC_SSNS_EXT_INFO	= 9,
+};
+
+/**
+ * enum nbft_trtype - NBFT Interface Transport Types (Figure 7)
+ * @NBFT_TRTYPE_TCP: NVMe/TCP (802.3 + TCP/IP). String Designator "tcp".
+ */
+enum nbft_trtype {
+	NBFT_TRTYPE_TCP	= 3,
+};
+
+#define NBFT_HEADER_SIG		"NBFT"
+
+/**
+ * struct nbft_heap_obj - NBFT Header Driver Signature
+ * @offset: Offset in bytes of the heap object, if any, from byte offset 0h
+ *	    of the NBFT Table Header.
+ * @length: Length in bytes of the heap object, if any.
+ */
+struct nbft_heap_obj {
+	__le32 offset;
+	__le16 length;
+} __attribute__((packed));
+
+/**
+ * struct nbft_header - NBFT Table - Header (Figure 8)
+ * @signature:		 Signature: An ASCII string representation of the table
+ *			 identifier. This field shall be set to the value 4E424654h
+ *			 (i.e. "NBFT", see #NBFT_HEADER_SIG).
+ * @length:		 Length: The length of the table, in bytes, including the
+ *			 header, starting from offset 0h. This field is used to record
+ *			 the size of the entire table.
+ * @major_revision:	 Major Revision: The major revision of the structure
+ *			 corresponding to the Signature field. Larger major revision
+ *			 numbers should not be assumed backward compatible to lower
+ *			 major revision numbers with the same signature.
+ * @checksum:		 Checksum: The entire table, including the Checksum field,
+ *			 shall sum to 0h to be considered valid.
+ * @oem_id:		 OEMID shall be populated by the NBFT driver writer by
+ *			 an OEM-supplied string that identifies the OEM. All
+ *			 trailing bytes shall be NULL.
+ * @oem_table_id:	 OEM Table ID: This field shall be populated by the NBFT
+ *			 driver writer with an OEM-supplied string that the OEM
+ *			 uses to identify the particular data table. This field is
+ *			 particularly useful when defining a definition block to
+ *			 distinguish definition block functions. The OEM assigns
+ *			 each dissimilar table a new OEM Table ID.
+ * @oem_revision:	 OEM Revision: An OEM-supplied revision number. Larger
+ *			 numbers are assumed to be newer revisions.
+ * @creator_id:		 Creator ID: Vendor ID of utility that created the table.
+ *			 For instance, this may be the ID for the ASL Compiler.
+ * @creator_revision:	 Creator Revision: Revision of utility that created the
+ *			 table. For instance, this may be the ID for the ASL Compiler.
+ * @heap_offset:	 Heap Offset (HO): This field indicates the offset in bytes
+ *			 of the heap, if any, from byte offset 0h of the NBFT
+ *			 Table Header.
+ * @heap_length:	 Heap Length (HL): The length of the heap, if any.
+ * @driver_dev_path_sig: Driver Signature Heap Object Reference: This field indicates
+ *			 the offset in bytes of a heap object containing the Driver
+ *			 Signature, if any, from byte offset 0h of the NBFT Table
+ *			 Header.
+ * @minor_revision:	 Minor Revision: The minor revision of the structure
+ *			 corresponding to the Signature field. If the major revision
+ *			 numbers are the same, any minor revision number differences
+ *			 shall be backwards compatible with the same signature.
+ * @reserved:		 Reserved.
+ */
+struct nbft_header {
+	char signature[4];
+	__le32 length;
+	__u8 major_revision;
+	__u8 checksum;
+	char oem_id[6];
+	char oem_table_id[8];
+	__le32 oem_revision;
+	__le32 creator_id;
+	__le32 creator_revision;
+	__le32 heap_offset;
+	__le32 heap_length;
+	struct nbft_heap_obj driver_dev_path_sig;
+	__u8 minor_revision;
+	__u8 reserved[13];
+};
+
+/**
+ * struct nbft_control - NBFT Table - Control Descriptor (Figure 8)
+ * @structure_id:   Structure ID: This field specifies the element (refer to
+ *		    &enum nbft_desc_type). This field shall be set to 1h (i.e.,
+ *		    Control, #NBFT_DESC_CONTROL).
+ * @major_revision: Major Revision: The major revision of the structure corresponding
+ *		    to the Signature field. Larger major revision numbers should
+ *		    not be assumed backward compatible to lower major revision
+ *		    numbers with the same signature.
+ * @minor_revision: Minor Revision: The minor revision of the structure corresponding
+ *		    to the signature field. If the major revision numbers are
+ *		    the same, any minor revision number differences shall be backwards
+ *		    compatible with the same signature.
+ * @reserved1:	    Reserved.
+ * @csl:	    Control Structure Length (CSL): This field indicates the length
+ *		    in bytes of the Control Descriptor.
+ * @flags:	    Flags, see &enum nbft_control_flags.
+ * @reserved2:	    Reserved.
+ * @hdesc:	    Host Descriptor (HDESC): This field indicates the location
+ *		    and length of the Host Descriptor (see &struct nbft_host).
+ * @hsv:	    Host Descriptor Version (HSV): This field indicates the version
+ *		    of the Host Descriptor.
+ * @reserved3:	    Reserved.
+ * @hfio:	    HFI Descriptor List Offset (HFIO): If this field is set to
+ *		    a non-zero value, then this field indicates the offset in bytes
+ *		    of the HFI Descriptor List, if any, from byte offset 0h of the
+ *		    NBFT Table Header. If the @num_hfi field is cleared to 0h,
+ *		    then this field is reserved.
+ * @hfil:	    HFI Descriptor Length (HFIL): This field indicates the length
+ *		    in bytes of each HFI Descriptor, if any. If the @num_hfi field
+ *		    is cleared to 0h, then this field is reserved.
+ * @hfiv:	    HFI Descriptor Version (HFIV): This field indicates the version
+ *		    of each HFI Descriptor.
+ * @num_hfi:	    Number of Host Fabric Interface Descriptors (NumHFI): This field
+ *		    indicates the number of HFI Descriptors (see &struct nbft_hfi)
+ *		    in the HFI Descriptor List, if any. If no interfaces have been
+ *		    configured, then this field shall be cleared to 0h.
+ * @ssnso:	    SSNS Descriptor List Offset (SSNSO):: This field indicates
+ *		    the offset in bytes of the SSNS Descriptor List, if any, from
+ *		    byte offset 0h of the NBFT Table Header. If the @num_ssns field
+ *		    is cleared to 0h, then this field is reserved.
+ * @ssnsl:	    SSNS Descriptor Length (SSNSL): This field indicates the length
+ *		    in bytes of each SSNS Descriptor, if any. If the @num_ssns
+ *		    field is cleared to 0h, then this field is reserved.
+ * @ssnsv:	    SSNS Descriptor Version (SSNSV): This field indicates the version
+ *		    of the SSNS Descriptor.
+ * @num_ssns:	    Number of Subsystem and Namespace Descriptors (NumSSNS): This
+ *		    field indicates the number of Subsystem Namespace (SSNS)
+ *		    Descriptors (see &struct nbft_ssns) in the SSNS Descriptor List,
+ *		    if any.
+ * @seco:	    Security Profile Descriptor List Offset (SECO): This field
+ *		    indicates the offset in bytes of the Security Profile Descriptor
+ *		    List, if any, from byte offset 0h of the NBFT Table Header.
+ *		    If the @num_sec field is cleared to 0h, then this field
+ *		    is reserved.
+ * @secl:	    Security Profile Descriptor Length (SECL): This field indicates
+ *		    the length in bytes of each Security Profile Descriptor, if any.
+ *		    If the @num_sec field is cleared to 0h, then this field
+ *		    is reserved.
+ * @secv:	    Security Profile Descriptor Version (SECV): This field indicates
+ *		    the version of the Security Profile Descriptor.
+ * @num_sec:	    Number of Security Profile Descriptors (NumSec): This field
+ *		    indicates the number of Security Profile Descriptors
+ *		    (see &struct nbft_security), if any, in the Security Profile
+ *		    Descriptor List.
+ * @disco:	    Discovery Descriptor Offset (DISCO): This field indicates
+ *		    the offset in bytes of the Discovery Descriptor List, if any,
+ *		    from byte offset 0h of the NBFT Table Header. If the @num_disc
+ *		    field is cleared to 0h, then this field is reserved.
+ * @discl:	    Discovery Descriptor Length (DISCL): This field indicates
+ *		    the length in bytes of each Discovery Descriptor, if any.
+ *		    If the @num_disc field is cleared to 0h, then this field
+ *		    is reserved.
+ * @discv:	    Discovery Descriptor Version (DISCV): This field indicates
+ *		    the version of the Discovery Descriptor.
+ * @num_disc:	    Number of Discovery Descriptors (NumDisc): This field indicates
+ *		    the number of Discovery Descriptors (see &struct nbft_discovery),
+ *		    if any, in the Discovery Descriptor List, if any.
+ * @reserved4:	    Reserved.
+ */
+struct nbft_control {
+	__u8 structure_id;
+	__u8 major_revision;
+	__u8 minor_revision;
+	__u8 reserved1;
+	__le16 csl;
+	__u8 flags;
+	__u8 reserved2;
+	struct nbft_heap_obj hdesc;
+	__u8 hsv;
+	__u8 reserved3;
+	__le32 hfio;
+	__le16 hfil;
+	__u8 hfiv;
+	__u8 num_hfi;
+	__le32 ssnso;
+	__le16 ssnsl;
+	__u8 ssnsv;
+	__u8 num_ssns;
+	__le32 seco;
+	__le16 secl;
+	__u8 secv;
+	__u8 num_sec;
+	__le32 disco;
+	__le16 discl;
+	__u8 discv;
+	__u8 num_disc;
+	__u8 reserved4[16];
+};
+
+/**
+ * enum nbft_control_flags - Control Descriptor Flags
+ * @NBFT_CONTROL_VALID:	Block Valid: indicates that the structure is valid.
+ */
+enum nbft_control_flags {
+	NBFT_CONTROL_VALID	= 1 << 0,
+};
+
+/**
+ * struct nbft_host - Host Descriptor (Figure 9)
+ * @structure_id: Structure ID: This field shall be set to 2h (i.e.,
+ *		  Host Descriptor; #NBFT_DESC_HOST).
+ * @flags:	  Host Flags, see &enum nbft_host_flags.
+ * @host_id:	  Host ID: This field shall be set to the Host Identifier. This
+ *		  field shall not be empty if the NBFT and NVMe Boot are supported
+ *		  by the Platform.
+ * @host_nqn_obj: Host NQN Heap Object Reference: this field indicates a heap
+ *		  object containing a Host NQN. This object shall not be empty
+ *		  if the NBFT and NVMe Boot are supported by the Platform.
+ * @reserved:	  Reserved.
+ */
+struct nbft_host {
+	__u8 structure_id;
+	__u8 flags;
+	__u8 host_id[16];
+	struct nbft_heap_obj host_nqn_obj;
+	__u8 reserved[8];
+};
+
+/**
+ * enum nbft_host_flags - Host Flags
+ * @NBFT_HOST_VALID:			   Descriptor Valid: If set to 1h, then this
+ *					   descriptor is valid. If cleared to 0h, then
+ *					   this descriptor is reserved.
+ * @NBFT_HOST_HOSTID_CONFIGURED:	   HostID Configured: If set to 1h, then the
+ *					   Host ID field contains an administratively-configured
+ *					   value. If cleared to 0h, then the Host ID
+ *					   field contains a driver default value.
+ * @NBFT_HOST_HOSTNQN_CONFIGURED:	   Host NQN Configured: If set to 1h, then the
+ *					   Host NQN indicated by the Host NQN Heap Object
+ *					   Reference field (&struct nbft_host.host_nqn)
+ *					   contains an administratively-configured value.
+ *					   If cleared to 0h, then the Host NQN indicated
+ *					   by the Host NQN Offset field contains a driver
+ *					   default value.
+ * @NBFT_HOST_PRIMARY_ADMIN_MASK:	   Mask to get Primary Administrative Host Descriptor:
+ *					   indicates whether the Host Descriptor in this
+ *					   NBFT was selected as the primary NBFT for
+ *					   administrative purposes of platform identity
+ *					   as a hint to the OS. If multiple NBFT tables
+ *					   are present, only one NBFT should be administratively
+ *					   selected. There is no enforcement mechanism
+ *					   for this to be coordinated between multiple NBFT
+ *					   tables, but this field should be set to Selected
+ *					   (#NBFT_HOST_PRIMARY_ADMIN_SELECTED) if
+ *					   more than one NBFT is present.
+ * @NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED: Not Indicated by Driver: The driver that created
+ *					   this NBFT provided no administrative priority
+ *					   hint for this NBFT.
+ * @NBFT_HOST_PRIMARY_ADMIN_UNSELECTED:	   Unselected: The driver that created this NBFT
+ *					   explicitly indicated that this NBFT should
+ *					   not be prioritized over any other NBFT.
+ * @NBFT_HOST_PRIMARY_ADMIN_SELECTED:	   Selected: The driver that created this NBFT
+ *					   explicitly indicated that this NBFT should
+ *					   be prioritized over any other NBFT.
+ */
+enum nbft_host_flags {
+	NBFT_HOST_VALID				= 1 << 0,
+	NBFT_HOST_HOSTID_CONFIGURED		= 1 << 1,
+	NBFT_HOST_HOSTNQN_CONFIGURED		= 1 << 2,
+	NBFT_HOST_PRIMARY_ADMIN_MASK		= 0x18,
+	NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED	= 0x00,
+	NBFT_HOST_PRIMARY_ADMIN_UNSELECTED	= 0x08,
+	NBFT_HOST_PRIMARY_ADMIN_SELECTED	= 0x10,
+};
+
+/**
+ * struct nbft_hfi - Host Fabric Interface (HFI) Descriptor (Figure 11)
+ * @structure_id: Structure ID: This field shall be set to 3h (i.e., Host Fabric
+ *		  Interface Descriptor; #NBFT_DESC_HFI).
+ * @index:	  HFI Descriptor Index: This field indicates the number of this
+ *		  HFI Descriptor in the Host Fabric Interface Descriptor List.
+ * @flags:	  HFI Descriptor Flags, see &enum nbft_hfi_flags.
+ * @trtype:	  HFI Transport Type, see &enum nbft_trtype.
+ * @reserved1:	  Reserved.
+ * @trinfo_obj:	  HFI Transport Info Descriptor Heap Object Reference: If this
+ *		  field is set to a non-zero value, then this field indicates
+ *		  the location and size of a heap object containing
+ *		  a HFI Transport Info.
+ * @reserved2:	  Reserved.
+ */
+struct nbft_hfi {
+	__u8 structure_id;
+	__u8 index;
+	__u8 flags;
+	__u8 trtype;
+	__u8 reserved1[12];
+	struct nbft_heap_obj trinfo_obj;
+	__u8 reserved2[10];
+};
+
+/**
+ * enum nbft_hfi_flags - HFI Descriptor Flags
+ * @NBFT_HFI_VALID: Descriptor Valid: If set to 1h, then this descriptor is valid.
+ *		    If cleared to 0h, then this descriptor is reserved.
+ */
+enum nbft_hfi_flags {
+	NBFT_HFI_VALID	= 1 << 0,
+};
+
+/**
+ * struct nbft_hfi_info_tcp - HFI Transport Info Descriptor - NVMe/TCP (Figure 13)
+ * @structure_id:	Structure ID: This field shall be set to 7h (i.e.,
+ *			HFI Transport Info; #NBFT_DESC_HFI_TRINFO).
+ * @version:		Version: This field shall be set to 1h.
+ * @trtype:		HFI Transport Type, see &enum nbft_trtype: This field
+ *			shall be set to 03h (i.e., NVMe/TCP; #NBFT_TRTYPE_TCP).
+ * @trinfo_version:	Transport Info Version: Implementations compliant to this
+ *			specification shall set this field to 1h.
+ * @hfi_index:		HFI Descriptor Index: The value of the HFI Descriptor Index
+ *			field of the HFI Descriptor (see &struct nbft_hfi.index)
+ *			whose HFI Transport Info Descriptor Heap Object Reference
+ *			field indicates this HFI Transport Info Descriptor.
+ * @flags:		HFI Transport Flags, see &enum nbft_hfi_info_tcp_flags.
+ * @pci_sbdf:		PCI Express Routing ID for the HFI Transport Function:
+ *			This field indicates the PCI Express Routing ID as specified
+ *			in the PCI Express Base Specification.
+ * @mac_addr:		MAC Address: The MAC address of this HFI, in EUI-48TM format,
+ *			as defined in the IEEE Guidelines for Use of Extended Unique
+ *			Identifiers. This field shall be set to a non-zero value.
+ * @vlan:		VLAN: If this field is set to a non-zero value, then this
+ *			field contains the VLAN identifier if the VLAN associated
+ *			with this HFI, as defined in IEEE 802.1q-2018. If no VLAN
+ *			is associated with this HFI, then this field shall be cleared
+ *			to 0h.
+ * @ip_origin:		IP Origin: If this field is set to a non-zero value, then
+ *			this field indicates the source of Ethernet L3 configuration
+ *			information used by the driver for this interface. Valid
+ *			values are defined in the Win 32 API: NL_PREFIX_ORIGIN
+ *			enumeration specification. This field should be cleared
+ *			to 0h if the IP Origin field is unused by driver.
+ * @ip_address:		IP Address: This field indicates the IPv4 or IPv6 address
+ *			of this HFI. This field shall be set to a non-zero value.
+ * @subnet_mask_prefix:	Subnet Mask Prefix: This field indicates the IPv4 or IPv6
+ *			subnet mask in CIDR routing prefix notation.
+ * @ip_gateway:		IP Gateway: If this field is set to a non-zero value, this
+ *			field indicates the IPv4 or IPv6 address of the IP gateway
+ *			for this HFI. If this field is cleared to 0h, then
+ *			no IP gateway is specified.
+ * @reserved1:		Reserved.
+ * @route_metric:	Route Metric: If this field is set to a non-zero value,
+ *			this field indicates the cost value for the route indicated
+ *			by this HF. This field contains the value utilized by the
+ *			pre-OS driver when chosing among all available routes. Lower
+ *			values relate to higher priority. Refer to IETF RFC 4249.
+ *			If the pre-OS driver supports routing and did not configure
+ *			a specific route metric for this interface, then the pre-OS
+ *			driver should set this value to 500. If the pre-OS driver
+ *			does not support routing, then this field should be cleared
+ *			to 0h.
+ * @primary_dns:	Primary DNS: If this field is set to a non-zero value,
+ *			this field indicates the IPv4 or IPv6 address of the
+ *			Primary DNS server for this HFI, if any, from byte offset
+ *			0h of the NBFT Table Header. If this field is cleared to 0h,
+ *			then no Primary DNS is specified.
+ * @secondary_dns:	Secondary DNS: If this field is set to a non-zero value,
+ *			this field indicates the IPv4 or IPv6 address of
+ *			the Secondary DNS server for this HFI, if any, from byte
+ *			offset 0h of the NBFT Table Header. If this field is
+ *			cleared to 0h, then no Secondary DNS is specified.
+ * @dhcp_server:	DHCP Server: If the DHCP Override bit is set to 1h, then
+ *			this field indicates the IPv4 or IPv6 address of the DHCP
+ *			server used to assign this HFI address. If that bit is
+ *			cleared to 0h, then this field is reserved.
+ * @host_name_obj:	Host Name Heap Object Reference: If this field is set
+ *			to a non-zero value, then this field indicates the location
+ *			and size of a heap object containing a Host Name string.
+ * @reserved2:		Reserved.
+ */
+struct nbft_hfi_info_tcp {
+	__u8 structure_id;
+	__u8 version;
+	__u8 trtype;
+	__u8 trinfo_version;
+	__le16 hfi_index;
+	__u8 flags;
+	__le32 pci_sbdf;
+	__u8 mac_addr[6];
+	__le16 vlan;
+	__u8 ip_origin;
+	__u8 ip_address[16];
+	__u8 subnet_mask_prefix;
+	__u8 ip_gateway[16];
+	__u8 reserved1;
+	__le16 route_metric;
+	__u8 primary_dns[16];
+	__u8 secondary_dns[16];
+	__u8 dhcp_server[16];
+	struct nbft_heap_obj host_name_obj;
+	__u8 reserved2[18];
+} __attribute__((packed));
+
+/**
+ * enum nbft_hfi_info_tcp_flags - HFI Transport Flags
+ * @NBFT_HFI_INFO_TCP_VALID:	     Descriptor Valid: if set to 1h, then this
+ *				     descriptor is valid. If cleared to 0h, then
+ *				     this descriptor is reserved.
+ * @NBFT_HFI_INFO_TCP_GLOBAL_ROUTE:  Global Route vs. Link Local Override Flag:
+ *				     if set to 1h, then the BIOS utilized this
+ *				     interface described by HFI to be the default
+ *				     route with highest priority. If cleared to 0h,
+ *				     then routes are local to their own scope.
+ * @NBFT_HFI_INFO_TCP_DHCP_OVERRIDE: DHCP Override: if set to 1, then HFI information
+ *				     was populated by consuming the DHCP on this
+ *				     interface. If cleared to 0h, then the HFI
+ *				     information was set administratively by
+ *				     a configuration interface to the driver and
+ *				     pre-OS envrionment.
+ */
+enum nbft_hfi_info_tcp_flags {
+	NBFT_HFI_INFO_TCP_VALID		= 1 << 0,
+	NBFT_HFI_INFO_TCP_GLOBAL_ROUTE	= 1 << 1,
+	NBFT_HFI_INFO_TCP_DHCP_OVERRIDE	= 1 << 2,
+};
+
+/**
+ * struct nbft_ssns - Subsystem Namespace (SSNS) Descriptor (Figure 15)
+ * @structure_id:		  Structure ID: This field shall be set to 4h
+ *				  (i.e., SSNS; #NBFT_DESC_SSNS).
+ * @index:			  SSNS Descriptor Index: This field indicates the number
+ *				  of this Subsystem Namespace Descriptor in the
+ *				  Subsystem Namespace Descriptor List.
+ * @flags:			  SSNS Flags, see &enum nbft_ssns_flags.
+ * @trtype:			  Transport Type, see &enum nbft_trtype.
+ * @trflags:			  Transport Specific Flags, see &enum nbft_ssns_trflags.
+ * @primary_discovery_ctrl_index: Primary Discovery Controller Index: The Discovery
+ *				  Descriptor Index field of the Discovery Descriptor
+ *				  (see &struct nbft_discovery) that is associated with
+ *				  this SSNS Descriptor. If a Discovery controller was
+ *				  used to establish this record this value shall
+ *				  be set to a non-zero value. If this namespace was
+ *				  associated with multiple Discovery controllers,
+ *				  those Discovery controllers shall have records
+ *				  in the Discovery Descriptor to facilitate multi-path
+ *				  rediscovery as required. If no Discovery controller
+ *				  was utilized to inform this namespace record,
+ *				  this field shall be cleared to 0h.
+ * @reserved1:			  Reserved.
+ * @subsys_traddr_obj:		  Subsystem Transport Address Heap Object Reference:
+ *				  This field indicates the location and size of a heap
+ *				  object containing the Subsystem Transport Address.
+ *				  For IP based transports types, shall be an IP Address.
+ * @subsys_trsvcid_obj:		  Subsystem Transport Service Identifier Heap Object Reference:
+ *				  This field indicates the location and size of a heap
+ *				  object containing an array of bytes indicating
+ *				  the Subsystem Transport Service Identifier.
+ *				  See &enum nbft_trtype.
+ * @subsys_port_id:		  Subsystem Port ID: Port in the NVM subsystem
+ *				  associated with this transport address used by
+ *				  the pre-OS driver.
+ * @nsid:			  Namespace ID: This field indicates the namespace
+ *				  identifier (NSID) of the namespace indicated by
+ *				  this descriptor. This field shall be cleared to 0h
+ *				  if not specified by the user. If this value is cleared
+ *				  to 0h, then consumers of the NBFT shall rely
+ *				  on the NID.
+ * @nidt:			  Namespace Identifier Type (NIDT): This field
+ *				  contains the value of the Namespace Identifier Type (NIDT)
+ *				  field in the Namespace Identification Descriptor
+ *				  for the namespace indicated by this descriptor.
+ *				  If a namespace supports multiple NIDT entries
+ *				  for uniqueness, the order of preference is NIDT field
+ *				  value of 3h (i.e., UUID) before 2h (i.e., NSGUID),
+ *				  and 2h before 1h (i.e., EUI-64).
+ * @nid:			  Namespace Identifier (NID): This field contains
+ *				  the value of the Namespace Identifier (NID) field
+ *				  in the Namespace Identification Descriptor for
+ *				  the namespace indicated by this descriptor.
+ * @security_desc_index:	  Security Profile Descriptor Index: If the Use Security
+ *				  Flag bit in the SSNS Flags field is set to 1h, then
+ *				  this field indicates the value of the Security Profile
+ *				  Descriptor Index field of the Security Profile
+ *				  Descriptor (see &struct nbft_security) associated
+ *				  with this namespace. If the Use Security Flag bit
+ *				  is cleared to 0h, then no Security Profile Descriptor
+ *				  is associated with this namespace and this field
+ *				  is reserved.
+ * @primary_hfi_desc_index:	  Primary HFI Descriptor Index: This field indicates
+ *				  the value of the HFI Descriptor Index field of the
+ *				  HFI Descriptor (see &struct nbft_hfi) for the
+ *				  interface associated with this namespace. If multiple
+ *				  HFIs are associated with this record, subsequent
+ *				  interfaces should be populated in the Secondary
+ *				  HFI Associations field.
+ * @reserved2:			  Reserved.
+ * @secondary_hfi_assoc_obj:	  Secondary HFI Associations Heap Object Reference:
+ *				  If this field is set to a non-zero value, then
+ *				  this field indicates an array of bytes, in which
+ *				  each byte contains the value of the HFI Descriptor
+ *				  Index field of an HFI Descriptor in the HFI Descriptor
+ *				  List. If this field is cleared to 0h, then no
+ *				  secondary HFI associations are specified.
+ * @subsys_ns_nqn_obj:		  Subsystem and Namespace NQN Heap Object Reference:
+ *				  This field indicates the location and size of
+ *				  a heap object containing the Subsystem and Namespace NQN.
+ * @ssns_extended_info_desc_obj:  SSNS Extended Information Descriptor Heap Object
+ *				  Reference: If the SSNS Extended Info In-use Flag
+ *				  bit is set to 1h, then this field indicates the
+ *				  offset in bytes of a heap object containing an
+ *				  SSNS Extended Information Descriptor
+ *				  (see &struct nbft_ssns_ext_info) heap object
+ *				  from byte offset 0h of the NBFT Table Header.
+ *				  If the SSNS Extended Info In-use Flag bit is cleared
+ *				  to 0h, then this field is reserved.
+ * @reserved3:			  Reserved.
+ */
+struct nbft_ssns {
+	__u8 structure_id;
+	__le16 index;
+	__le16 flags;
+	__u8 trtype;
+	__le16 trflags;
+	__u8 primary_discovery_ctrl_index;
+	__u8 reserved1;
+	struct nbft_heap_obj subsys_traddr_obj;
+	struct nbft_heap_obj subsys_trsvcid_obj;
+	__le16 subsys_port_id;
+	__le32 nsid;
+	__u8 nidt;
+	__u8 nid[16];
+	__u8 security_desc_index;
+	__u8 primary_hfi_desc_index;
+	__u8 reserved2;
+	struct nbft_heap_obj secondary_hfi_assoc_obj;
+	struct nbft_heap_obj subsys_ns_nqn_obj;
+	struct nbft_heap_obj ssns_extended_info_desc_obj;
+	__u8 reserved3[62];
+} __attribute__((packed));
+
+/**
+ * enum nbft_ssns_flags - Subsystem and Namespace Specific Flags Field (Figure 16)
+ * @NBFT_SSNS_VALID:			 Descriptor Valid: If set to 1h, then this descriptor
+ *					 is valid. If cleared to 0h, then this descriptor
+ *					 is not valid. A host that supports NVMe-oF Boot,
+ *					 but does not currently have a remote Subsystem
+ *					 and Namespace assigned may clear this bit to 0h.
+ * @NBFT_SSNS_NON_BOOTABLE_ENTRY:	 Non-bootable Entry Flag: If set to 1h, this flag
+ *					 indicates that this SSNS Descriptor contains
+ *					 a namespace of administrative purpose to the boot
+ *					 process, but the pre-OS may not have established
+ *					 connectivity to or evaluated the contents of this
+ *					 Descriptor. Such namespaces may contain supplemental
+ *					 data deemed relevant by the Administrator as part
+ *					 of the pre-OS to OS hand off. This may include
+ *					 properties such as a UEFI device path that may
+ *					 not have been created for this namespace. This means
+ *					 an OS runtime may still require the contents
+ *					 of such a namespace to complete later stages
+ *					 of boot. If cleared to 0h, then this namespace did
+ *					 not have any special administrative intent.
+ * @NBFT_SSNS_USE_SECURITY_FIELD:	 Use Security Flag: If set to 1h, then there is
+ *					 a Security Profile Descriptor associated with this
+ *					 SSNS record and the Security Profile Descriptor Index
+ *					 field is valid. If cleared to 0h, then there is
+ *					 no Security Profile Descriptor associated with this
+ *					 SSNS record and the Security Profile Descriptor Index
+ *					 field is not valid.
+ * @NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE:	 DHCP Root-Path Override Flag: If set to 1h, then
+ *					 this SSNS descriptor was populated by consuming
+ *					 the DHCP Root-Path on this interface. If cleared
+ *					 to 0h, then the DHCP Root-Path was not used
+ *					 in populating the SSNS descriptor.
+ * @NBFT_SSNS_EXTENDED_INFO_IN_USE:	 SSNS Extended Info In-use Flag: If set to 1h,
+ *					 then the SSNS Extended Information Offset field
+ *					 and the SSNS Extended Information Length field
+ *					 are valid. This flag, if set to 1h, indicates
+ *					 that a Subsystem and Namespace Extended Information
+ *					 Descriptor corresponding to this descriptor is present.
+ * @NBFT_SSNS_SEPARATE_DISCOVERY_CTRL:	 Separate Discovery Controller Flag: If set to 1h,
+ *					 then the Discovery controller associated with
+ *					 this volume is on a different transport address
+ *					 than the specified in the Subsystem Transport
+ *					 Address Heap Object Reference. If cleared to 0h,
+ *					 then the Discovery controller is the same as the
+ *					 Subsystem Transport Address Heap Object Reference.
+ * @NBFT_SSNS_DISCOVERED_NAMESPACE:	 Discovered Namespace Flag: If set to 1h, then
+ *					 this namespace was acquired through discovery.
+ *					 If cleared to 0h, then this namespace was
+ *					 explicitly configured in the system.
+ * @NBFT_SSNS_UNAVAIL_NAMESPACE_MASK:	 Mask to get Unavailable Namespace Flag: This
+ *					 field indicates the availability of the namespace
+ *					 at a specific point in time. Such use is only
+ *					 a hint and its use does not guarantee the availability
+ *					 of that referenced namespace at any future point in time.
+ * @NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND:	 Not Indicated by Driver: No information is provided.
+ * @NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL:	 Available: A referenced namespace described by this
+ *					 flag was previously accessible by the pre-OS driver.
+ * @NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL: Unavailable: This namespace was administratively
+ *					 configured but unattempted, unavailable or
+ *					 inaccessible when establishing connectivity
+ *					 by the pre-OS driver.
+ */
+enum nbft_ssns_flags {
+	NBFT_SSNS_VALID				= 1 << 0,
+	NBFT_SSNS_NON_BOOTABLE_ENTRY		= 1 << 1,
+	NBFT_SSNS_USE_SECURITY_FIELD		= 1 << 2,
+	NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE	= 1 << 3,
+	NBFT_SSNS_EXTENDED_INFO_IN_USE		= 1 << 4,
+	NBFT_SSNS_SEPARATE_DISCOVERY_CTRL	= 1 << 5,
+	NBFT_SSNS_DISCOVERED_NAMESPACE		= 1 << 6,
+	NBFT_SSNS_UNAVAIL_NAMESPACE_MASK	= 0x0180,
+	NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND	= 0x0000,
+	NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL	= 0x0080,
+	NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL	= 0x0100,
+};
+
+/**
+ * enum nbft_ssns_trflags - SSNS Transport Specific Flags Field (Figure 17)
+ * @NBFT_SSNS_TRFLAG_VALID:	 Transport Specific Flags in Use: If set to 1h, then
+ *				 this descriptor is valid. If cleared to 0h, then
+ *				 this descriptor is not valid.
+ * @NBFT_SSNS_PDU_HEADER_DIGEST: PDU Header Digest (HDGST) Flag: If set to 1h, then
+ *				 the host or administrator required the connection
+ *				 described by this Subsystem and Namespace Descriptor
+ *				 to use the NVM Header Digest Enabled. A consumer
+ *				 of this information should attempt to use NVM Header
+ *				 Digest when recreating this connection if enabled.
+ *				 If cleared to 0h, then the host or administrator
+ *				 did not require the connection described by this
+ *				 Subsystem and Namespace Descriptor to use the
+ *				 NVM Header Digest Enabled.
+ * @NBFT_SSNS_DATA_DIGEST:	 Data Digest (DDGST) Flag: If set to 1h, then
+ *				 the host or administrator required the connection
+ *				 described by this Subsystem and Namespace Descriptor
+ *				 to use the NVM Data Digest Enabled. If cleared
+ *				 to 0h, then the host or administrator did not
+ *				 require the connection described by this Subsystem
+ *				 and Namespace Descriptor to use the NVM Data Digest
+ *				 Enabled. A consumer of this field should attempt
+ *				 to use NVM Data Digest when recreating this
+ *				 connection if enabled.
+ */
+enum nbft_ssns_trflags {
+	NBFT_SSNS_TRFLAG_VALID		= 1 << 0,
+	NBFT_SSNS_PDU_HEADER_DIGEST	= 1 << 1,
+	NBFT_SSNS_DATA_DIGEST		= 1 << 2,
+};
+
+/**
+ * struct nbft_ssns_ext_info - Subsystem and Namespace Extended Information
+ *			       Descriptor (Figure 19)
+ * @structure_id:	    Structure ID: This field shall be set to 9h
+ *			    (i.e., SSNS Extended Info; #NBFT_DESC_SSNS_EXT_INFO).
+ * @version:		    Version: This field shall be set to 1h.
+ * @ssns_index:		    SSNS Descriptor Index: This field indicates the value
+ *			    of the SSNS Descriptor Index field of the Subsystem
+ *			    and Namespace Descriptor (see &struct nbft_ssns) whose
+ *			    SSNS Extended Information Descriptor Heap Object
+ *			    Reference field indicates this descriptor.
+ * @flags:		    Flags, see &enum nbft_ssns_ext_info_flags.
+ * @cntlid:		    Controller ID: The controller identifier of the first
+ *			    controller associated with the Admin Queue by the driver.
+ *			    If a controller identifier is not administratively
+ *			    specified or direct configuration is not supported
+ *			    by the driver, then this field shall be cleared to 0h.
+ * @asqsz:		    Admin Submission Queue Size (ASQSZ): The Admin Submission
+ *			    Queue Size utilized for the respective SSNS by the driver.
+ * @dhcp_root_path_str_obj: DHCP Root Path String Heap Object Reference: If the
+ *			    SSNS DHCP Root Path Override (#NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE)
+ *			    flag bit is set to 1h, then this field indicates
+ *			    the offset in bytes of a heap object containing
+ *			    an DHCP Root Path String used by the driver. If the
+ *			    SNSS DHCP Root Path Override flag bit is cleared to 0h,
+ *			    then this field is reserved.
+ */
+struct nbft_ssns_ext_info {
+	__u8 structure_id;
+	__u8 version;
+	__le16 ssns_index;
+	__le32 flags;
+	__le16 cntlid;
+	__le16 asqsz;
+	struct nbft_heap_obj dhcp_root_path_str_obj;
+} __attribute__((packed));
+
+/**
+ * enum nbft_ssns_ext_info_flags - Subsystem and Namespace Extended Information
+ *				   Descriptor Flags
+ * @NBFT_SSNS_EXT_INFO_VALID:	    Descriptor Valid: If set to 1h, then this descriptor
+ *				    is valid. If cleared to 0h, then this descriptor
+ *				    is reserved.
+ * @NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ: Administrative ASQSZ: If set to 1h, then the value
+ *				    of the ASQSZ field was provided by administrative
+ *				    configuration for this SSNS record. If cleared
+ *				    to 0h, then the value of the ASQSZ field was
+ *				    either obtained by discovery or assumed
+ *				    by the driver.
+ */
+enum nbft_ssns_ext_info_flags {
+	NBFT_SSNS_EXT_INFO_VALID	= 1 << 0,
+	NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ	= 1 << 1,
+};
+
+/**
+ * struct nbft_security - Security Profile Descriptor (Figure 21)
+ * @structure_id:      Structure ID: This field shall be set to 5h
+ *		       (i.e., Security; #NBFT_DESC_SECURITY).
+ * @index:	       Security Profile Descriptor Index: This field indicates
+ *		       the number of this Security Profile Descriptor in the
+ *		       Security Profile Descriptor List.
+ * @flags:	       Security Profile Descriptor Flags, see &enum nbft_security_flags.
+ * @secret_type:       Secret Type, see &enum nbft_security_secret_type.
+ * @reserved1:	       Reserved.
+ * @sec_chan_alg_obj:  Secure Channel Algorithm Heap Object Reference: If the
+ *		       Security Policy List field is set to 1h, then this field
+ *		       indicates the location and size of a heap object containing
+ *		       a list of secure channel algorithms. The list is an array
+ *		       of bytes and the values are defined in the Security Type
+ *		       (SECTYPE) field in the Transport Specific Address Subtype
+ *		       Definition in the NVMe TCP Transport Specification.
+ *		       If the Security Policy List field is cleared to 0h, then
+ *		       this field is reserved.
+ * @auth_proto_obj:    Authentication Protocols Heap Object Reference: If the
+ *		       Authentication Policy List field is set to 1h, then this
+ *		       field indicates the location and size of a heap object
+ *		       containing a list of authentication protocol identifiers.
+ *		       If the Authentication Policy List field is cleared to 0h,
+ *		       then this field is reserved.
+ * @cipher_suite_obj:  Cipher Suite Offset Heap Object Reference: If the Cipher
+ *		       Suites Restricted by Policy bit is set to 1h, then this
+ *		       field indicates the location and size of a heap object
+ *		       containing a list of cipher suite identifiers. The list,
+ *		       if any, is an array of bytes and the values are defined
+ *		       in the IANA TLS Parameters Registry. If the Cipher Suites
+ *		       Restricted by Policy bit is cleared to 0h, then this field
+ *		       is reserved.
+ * @dh_grp_obj:	       DH Groups Heap Object Reference: If the Authentication DH Groups
+ *		       Restricted by Policy List bit is set to 1h, then this field
+ *		       indicates the location and size of a heap object containing
+ *		       a list of DH-HMAC-CHAP Diffie-Hellman (DH) group identifiers.
+ *		       If the Authentication DH Groups Restricted by Policy List
+ *		       bit is cleared to 0h, then this field is reserved.
+ * @sec_hash_func_obj: Secure Hash Functions Offset Heap Object Reference: If the
+ *		       Secure Hash Functions Policy List bit is set to 1h, then
+ *		       this field indicates the offset in bytes of a heap object
+ *		       containing a list of DH-HMAC-CHAP hash function identifiers.
+ *		       The list is an array of bytes and the values are defined
+ *		       in the NVM Express Base Specification. If the Secure Hash
+ *		       Functions Policy List bit is cleared to 0h, then this
+ *		       field is reserved.
+ * @sec_keypath_obj:   Secret Keypath Offset Heap Object Reference: if this field
+ *		       is set to a non-zero value, then this field indicates
+ *		       the location and size of a heap object containing a URI.
+ *		       The type of the URI is specified in the Secret Type field.
+ *		       If this field is cleared to 0h, then this field is reserved.
+ * @reserved2:	       Reserved.
+ */
+struct nbft_security {
+	__u8 structure_id;
+	__u8 index;
+	__le16 flags;
+	__u8 secret_type;
+	__u8 reserved1;
+	struct nbft_heap_obj sec_chan_alg_obj;
+	struct nbft_heap_obj auth_proto_obj;
+	struct nbft_heap_obj cipher_suite_obj;
+	struct nbft_heap_obj dh_grp_obj;
+	struct nbft_heap_obj sec_hash_func_obj;
+	struct nbft_heap_obj sec_keypath_obj;
+	__u8 reserved2[22];
+};
+
+/**
+ * enum nbft_security_flags - Security Profile Descriptor Flags (Figure 22)
+ * @NBFT_SECURITY_VALID:			  Descriptor Valid: If set to 1h, then
+ *						  this descriptor is valid. If cleared
+ *						  to 0h, then this descriptor is not valid.
+ * @NBFT_SECURITY_IN_BAND_AUTH_MASK:		  Mask to get the In-Band Authentication
+ *						  Required field.
+ * @NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED:	  In-band authentication is not supported
+ *						  by the NVM subsystem.
+ * @NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED:	  In-band authentication is supported by
+ *						  the NVM subsystem and is not required.
+ * @NBFT_SECURITY_IN_BAND_AUTH_REQUIRED:	  In-band authentication is supported by
+ *						  the NVM subsystem and is required.
+ * @NBFT_SECURITY_AUTH_POLICY_LIST_MASK:	  Mask to get the Authentication Policy List
+ *						  flag: This field indicates whether
+ *						  authentication protocols were indicated
+ *						  by policy from driver defaults or
+ *						  administrative configuration.
+ * @NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED: Authentication Protocols Heap Object Reference
+ *						  field Offset and Length are reserved.
+ * @NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER:	  Authentication Protocols Offset field and
+ *						  the Authentication Protocols Length field
+ *						  indicate a list of authentication protocols
+ *						  used by the driver.
+ * @NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN:	  Authentication Protocols Offset field and
+ *						  the Authentication Protocols Length field
+ *						  indicate a list of authentication protocols
+ *						  that were administratively set and used
+ *						  by the driver.
+ * @NBFT_SECURITY_SEC_CHAN_NEG_MASK:		  Mask to get the Secure Channel Negotiation
+ *						  Required flag: This field indicates whether
+ *						  secure channel negotiation (e.g. TLS)
+ *						  is required.
+ * @NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED:	  Secure channel negotiation is not supported
+ *						  by the NVM subsystem.
+ * @NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED:	  Secure channel negotiation is supported
+ *						  by the NVM subsystem and is not required.
+ * @NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED:	  Secure channel negotiation is supported
+ *						  by the NVM subsystem and is required.
+ * @NBFT_SECURITY_SEC_POLICY_LIST_MASK:		  Mask to get the Security Policy List flag:
+ *						  This field indicates whether secure channel
+ *						  protocols were indicated by policy from driver
+ *						  defaults or administrative configuration.
+ * @NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED:  The Offset field and Length field in the
+ *						  Secure Channel Algorithm Heap Object Reference
+ *						  field are reserved.
+ * @NBFT_SECURITY_SEC_POLICY_LIST_DRIVER:	  The Heap Object specified by the Secure Channel
+ *						  Algorithm Heap Object Reference field indicates
+ *						  a list of authentication protocols used
+ *						  by the driver.
+ * @NBFT_SECURITY_SEC_POLICY_LIST_ADMIN:	  The Heap Object specified by the Secure Channel
+ *						  Algorithm Heap Object Reference field indicates
+ *						  a list of authentication protocols that were
+ *						  administratively set and used by the driver.
+ * @NBFT_SECURITY_CIPHER_RESTRICTED:		  Cipher Suites Restricted by Policy: If set to 1h,
+ *						  then the Cipher Suite Offset field and the
+ *						  Ciper Suite Length field indicate a list
+ *						  of supported cipher suites by the driver.
+ *						  If cleared to 0h, then the Cipher Suite Offset
+ *						  field and the Cipher Suite Length field
+ *						  are reserved.
+ * @NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED:	  Authentication DH Groups Restricted
+ *						  by Policy List: If set to 1h, then connections
+ *						  shall use one of the authentication DH groups
+ *						  in the Authentication DH Groups List is required.
+ *						  If cleared to 0h, then no Authentication DH Groups
+ *						  List is indicated and use of an authentication
+ *						  DH Group is not required.
+ * @NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST:	  Secure Hash Functions Policy List: If set to 1h,
+ *						  then connections shall use one of the secure
+ *						  hash functions in the Secure Hash Functions
+ *						  Policy List is required. If cleared to 0h,
+ *						  then no Secure Hash Functions Policy
+ *						  List is indicated and use of a secure
+ *						  hash function is not required.
+ */
+enum nbft_security_flags {
+	NBFT_SECURITY_VALID				= 1 << 0,
+	NBFT_SECURITY_IN_BAND_AUTH_MASK			= 0x0006,
+	NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED	= 0x0000,
+	NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED		= 0x0002,
+	NBFT_SECURITY_IN_BAND_AUTH_REQUIRED		= 0x0004,
+	NBFT_SECURITY_AUTH_POLICY_LIST_MASK		= 0x0018,
+	NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED	= 0x0000,
+	NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER		= 0x0008,
+	NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN		= 0x0010,
+	NBFT_SECURITY_SEC_CHAN_NEG_MASK			= 0x0060,
+	NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED	= 0x0000,
+	NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED		= 0x0020,
+	NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED		= 0x0040,
+	NBFT_SECURITY_SEC_POLICY_LIST_MASK		= 0x0180,
+	NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED	= 0x0000,
+	NBFT_SECURITY_SEC_POLICY_LIST_DRIVER		= 0x0080,
+	NBFT_SECURITY_SEC_POLICY_LIST_ADMIN		= 0x0100,
+	NBFT_SECURITY_CIPHER_RESTRICTED			= 1 << 9,
+	NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED		= 1 << 10,
+	NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST		= 1 << 11,
+};
+
+/**
+ * enum nbft_security_secret_type - Security Profile Descriptor Secret Type
+ * @NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI: Redfish Host Interface URI:
+ *						 If set to 1h, then the Secret Keypath
+ *						 Object Reference is a URI pointing
+ *						 to a Redfish Key Collection Object
+ *						 that contains the PSK.
+ */
+enum nbft_security_secret_type {
+	NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI	= 1 << 1,
+};
+
+/**
+ * struct nbft_discovery - Discovery Descriptor (Figure 24)
+ * @structure_id:	     Structure ID: This field shall be set to 6h
+ *			     (i.e., Discovery Descriptor; #NBFT_DESC_DISCOVERY).
+ * @flags:		     Discovery Descriptor Flags, see &enum nbft_discovery_flags.
+ * @index:		     Discovery Descriptor Index: This field indicates
+ *			     the number of this Discovery Descriptor in
+ *			     the Discovery Descriptor List.
+ * @hfi_index:		     HFI Descriptor Index: This field indicates the value
+ *			     of the HFI Descriptor Index field of the HFI Descriptor
+ *			     associated with this Discovery Descriptor. If multiple
+ *			     HFIs share a common Discovery controller, there shall
+ *			     be multiple Discovery Descriptor entries with one per HFI.
+ * @sec_index:		     Security Profile Descriptor Index: This field indicates
+ *			     the value of the Security Profile Descriptor Index
+ *			     field of the Security Descriptor associated with
+ *			     this Discovery Descriptor.
+ * @reserved1:		     Reserved.
+ * @discovery_ctrl_addr_obj: Discovery Controller Address Heap Object Reference:
+ *			     This field indicates the location and size of a heap
+ *			     object containing a URI which indicates an NVMe Discovery
+ *			     controller associated with this Discovery Descriptor.
+ *			     If this field is cleared to 0h, then no URI is specified.
+ * @discovery_ctrl_nqn_obj:  Discovery Controller NQN Heap Object Reference:
+ *			     If set to a non-zero value, this field indicates
+ *			     the location and size of a heap object containing
+ *			     an NVMe Discovery controller NQN. If the NVMe Discovery
+ *			     controller referenced by this record requires secure
+ *			     authentication with a well known Subsystem NQN, this
+ *			     field indicates the unique NQN for that NVMe Discovery
+ *			     controller. This record is involved formatted as an NQN
+ *			     string. If this field is cleared to 0h, then this
+ *			     field is reserved and the OS shall use the well
+ *			     known discovery NQN for this record.
+ * @reserved2:		     Reserved.
+ */
+struct nbft_discovery {
+	__u8 structure_id;
+	__u8 flags;
+	__u8 index;
+	__u8 hfi_index;
+	__u8 sec_index;
+	__u8 reserved1;
+	struct nbft_heap_obj discovery_ctrl_addr_obj;
+	struct nbft_heap_obj discovery_ctrl_nqn_obj;
+	__u8 reserved2[14];
+};
+
+/**
+ * enum nbft_discovery_flags - Discovery Descriptor Flags
+ * @NBFT_DISCOVERY_VALID: Descriptor Valid: if set to 1h, then this descriptor
+ *			  is valid. If cleared to 0h, then this descriptor
+ *			  is reserved.
+ */
+enum nbft_discovery_flags {
+	NBFT_DISCOVERY_VALID	= 1 << 0,
+};
+
+/*
+ *  End of NBFT ACPI table definitions
+ */
+
+
+/*
+ *  Convenient NBFT table parser ('nbft_info' prefix)
+ */
+
+/**
+ * enum nbft_info_primary_admin_host_flag - Primary Administrative Host Descriptor Flags
+ * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED: Not Indicated by Driver: The driver
+ * 						     that created this NBFT provided no
+ * 						     administrative priority hint for
+ * 						     this NBFT.
+ * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED:    Unselected: The driver that created
+ * 						     this NBFT explicitly indicated that
+ * 						     this NBFT should not be prioritized
+ * 						     over any other NBFT.
+ * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED:	     Selected: The driver that created
+ * 						     this NBFT explicitly indicated that
+ * 						     this NBFT should be prioritized over
+ * 						     any other NBFT.
+ * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED:	     Reserved.
+ */
+enum nbft_info_primary_admin_host_flag {
+	NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED,
+	NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED,
+	NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED,
+	NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED,
+};
+
+/**
+ * struct nbft_info_host - Host Descriptor
+ * @id:			 Host ID (raw UUID, length = 16 bytes).
+ * @nqn:		 Host NQN.
+ * @host_id_configured:	 HostID Configured Flag: value of True indicates that @id
+ * 			 contains administratively-configured value, or driver
+ * 			 default value if False.
+ * @host_nqn_configured: Host NQN Configured Flag: value of True indicates that
+ * 			 @nqn contains administratively-configured value,
+ * 			 or driver default value if False.
+ * @primary:		 Primary Administrative Host Descriptor, see
+ * 			 &enum nbft_info_primary_admin_host_flag.
+ */
+struct nbft_info_host {
+	unsigned char *id;
+	char *nqn;
+	bool host_id_configured;
+	bool host_nqn_configured;
+	enum nbft_info_primary_admin_host_flag primary;
+};
+
+/**
+ * struct nbft_info_hfi_info_tcp - HFI Transport Info Descriptor - NVMe/TCP
+ * @pci_sbdf:		       PCI Express Routing ID for the HFI Transport Function.
+ * @mac_addr:		       MAC Address: The MAC address of this HFI,
+ * 			       in EUI-48TM format.
+ * @vlan:		       The VLAN identifier if the VLAN is associated with
+ * 			       this HFI, as defined in IEEE 802.1q-2018 or zeroes
+ * 			       if no VLAN is associated with this HFI.
+ * @ip_origin:		       The source of Ethernet L3 configuration information
+ * 			       used by the driver or 0 if not used.
+ * @ipaddr:		       The IPv4 or IPv6 address of this HFI.
+ * @subnet_mask_prefix:	       The IPv4 or IPv6 subnet mask in CIDR routing prefix
+ * 			       notation.
+ * @gateway_ipaddr:	       The IPv4 or IPv6 address of the IP gateway for this
+ * 			       HFI or zeroes if no IP gateway is specified.
+ * @route_metric:	       The cost value for the route indicated by this HFI.
+ * @primary_dns_ipaddr:	       The IPv4 or IPv6 address of the Primary DNS server
+ * 			       for this HFI.
+ * @secondary_dns_ipaddr:      The IPv4 or IPv6 address of the Secondary DNS server
+ * 			       for this HFI.
+ * @dhcp_server_ipaddr:	       The IPv4 or IPv6 address of the DHCP server used
+ * 			       to assign this HFI address.
+ * @host_name:		       The Host Name string.
+ * @this_hfi_is_default_route: If True, then the BIOS utilized this interface
+ * 			       described by HFI to be the default route with highest
+ * 			       priority. If False, then routes are local to their
+ * 			       own scope.
+ * @dhcp_override:	       If True, then HFI information was populated
+ * 			       by consuming the DHCP on this interface. If False,
+ * 			       then the HFI information was set administratively
+ * 			       by a configuration interface to the driver and
+ * 			       pre-OS envrionment.
+ */
+struct nbft_info_hfi_info_tcp {
+	__u32 pci_sbdf;
+	__u8 mac_addr[6];
+	__u16 vlan;
+	__u8 ip_origin;
+	char ipaddr[40];
+	__u8 subnet_mask_prefix;
+	char gateway_ipaddr[40];
+	__u16 route_metric;
+	char primary_dns_ipaddr[40];
+	char secondary_dns_ipaddr[40];
+	char dhcp_server_ipaddr[40];
+	char *host_name;
+	bool this_hfi_is_default_route;
+	bool dhcp_override;
+};
+
+/**
+ * struct nbft_info_hfi - Host Fabric Interface (HFI) Descriptor
+ * @index:     HFI Descriptor Index: indicates the number of this HFI Descriptor
+ * 	       in the Host Fabric Interface Descriptor List.
+ * @transport: Transport Type string (e.g. 'tcp').
+ * @tcp_info:  The HFI Transport Info Descriptor, see &struct nbft_info_hfi_info_tcp.
+ */
+struct nbft_info_hfi {
+	int index;
+	char transport[8];
+	struct nbft_info_hfi_info_tcp tcp_info;
+};
+
+/**
+ * struct nbft_info_discovery - Discovery Descriptor
+ * @index:    The number of this Discovery Descriptor in the Discovery
+ * 	      Descriptor List.
+ * @security: The Security Profile Descriptor, see &struct nbft_info_security.
+ * @hfi:      The HFI Descriptor associated with this Discovery Descriptor.
+ * 	      See &struct nbft_info_hfi.
+ * @uri:      A URI which indicates an NVMe Discovery controller associated
+ * 	      with this Discovery Descriptor.
+ * @nqn:      An NVMe Discovery controller NQN.
+ */
+struct nbft_info_discovery {
+	int index;
+	struct nbft_info_security *security;
+	struct nbft_info_hfi *hfi;
+	char *uri;
+	char *nqn;
+};
+
+/**
+ * struct nbft_info_security - Security Profile Descriptor
+ * @index: The number of this Security Profile Descriptor in the Security
+ * 	   Profile Descriptor List.
+ */
+struct nbft_info_security {
+	int index;
+	/* TODO add fields */
+};
+
+/**
+ * enum nbft_info_nid_type - Namespace Identifier Type (NIDT)
+ * @NBFT_INFO_NID_TYPE_NONE:	No identifier available.
+ * @NBFT_INFO_NID_TYPE_EUI64:	The EUI-64 identifier.
+ * @NBFT_INFO_NID_TYPE_NGUID:	The NSGUID identifier.
+ * @NBFT_INFO_NID_TYPE_NS_UUID:	The UUID identifier.
+ */
+enum nbft_info_nid_type {
+	NBFT_INFO_NID_TYPE_NONE		= 0,
+	NBFT_INFO_NID_TYPE_EUI64	= 1,
+	NBFT_INFO_NID_TYPE_NGUID	= 2,
+	NBFT_INFO_NID_TYPE_NS_UUID	= 3,
+};
+
+/**
+ * struct nbft_info_subsystem_ns - Subsystem Namespace (SSNS) info
+ * @index: 			SSNS Descriptor Index in the descriptor list.
+ * @discovery:			Primary Discovery Controller associated with
+ * 				this SSNS Descriptor.
+ * @security:			Security Profile Descriptor associated with
+ * 				this namespace.
+ * @num_hfis:			Number of HFIs.
+ * @hfis:			List of HFIs associated with this namespace.
+ * 				Includes the primary HFI at the first position
+ * 				and all secondary HFIs. This array is null-terminated.
+ * @transport:			Transport Type string (e.g. 'tcp').
+ * @traddr:			Subsystem Transport Address.
+ * @trsvcid:			Subsystem Transport Service Identifier.
+ * @subsys_port_id:		The Subsystem Port ID.
+ * @nsid:			The Namespace ID of this descriptor or when @nid
+ * 				should be used instead.
+ * @nid_type:			Namespace Identifier Type, see &enum nbft_info_nid_type.
+ * @nid:			The Namespace Identifier value.
+ * @subsys_nqn:			Subsystem and Namespace NQN.
+ * @pdu_header_digest_required:	PDU Header Digest (HDGST) Flag: the use of NVM Header
+ * 				Digest Enabled is required.
+ * @data_digest_required: 	Data Digest (DDGST) Flag: the use of NVM Data Digest
+ * 				Enabled is required.
+ * @controller_id:		Controller ID (SSNS Extended Information Descriptor):
+ * 				The controller ID associated with the Admin Queue
+ * 				or 0 if not supported.
+ * @asqsz:			Admin Submission Queue Size (SSNS Extended Information
+ * 				Descriptor) or 0 if not supported.
+ * @dhcp_root_path_string:	DHCP Root Path Override string (SSNS Extended
+ * 				Information Descriptor).
+ */
+struct nbft_info_subsystem_ns {
+	int index;
+	struct nbft_info_discovery *discovery;
+	struct nbft_info_security *security;
+	int num_hfis;
+	struct nbft_info_hfi **hfis;
+	char transport[8];
+	char traddr[40];
+	char *trsvcid;
+	__u16 subsys_port_id;
+	__u32 nsid;
+	enum nbft_info_nid_type nid_type;
+	__u8 *nid;
+	char *subsys_nqn;
+	bool pdu_header_digest_required;
+	bool data_digest_required;
+	int controller_id;
+	int asqsz;
+	char *dhcp_root_path_string;
+};
+
+/**
+ * struct nbft_info - The parsed NBFT table data.
+ * @filename:	       Path to the NBFT table.
+ * @raw_nbft:	       The original NBFT table contents.
+ * @raw_nbft_size:     Size of @raw_nbft.
+ * @host:	       The Host Descriptor (should match other NBFTs).
+ * @hfi_list:	       The HFI Descriptor List (null-terminated array).
+ * @security_list:     The Security Profile Descriptor List (null-terminated array).
+ * @discovery_list:    The Discovery Descriptor List (null-terminated array).
+ * @subsystem_ns_list: The SSNS Descriptor List (null-terminated array).
+ */
+struct nbft_info {
+	char *filename;
+	__u8 *raw_nbft;
+	ssize_t raw_nbft_size;
+	struct nbft_info_host host;
+	struct nbft_info_hfi **hfi_list;
+	struct nbft_info_security **security_list;
+	struct nbft_info_discovery **discovery_list;
+	struct nbft_info_subsystem_ns **subsystem_ns_list;
+};
+
+/**
+ * nvme_nbft_read() - Read and parse contents of an ACPI NBFT table
+ *
+ * @nbft:     Parsed NBFT table data.
+ * @filename: Filename of the raw NBFT table to read.
+ *
+ * Read and parse the specified NBFT file into a struct nbft_info.
+ * Free with 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.2