svn commit: r310296 - head/sys/dev/bhnd/nvram

Landon J. Fuller landonf at FreeBSD.org
Mon Dec 19 20:31:30 UTC 2016


Author: landonf
Date: Mon Dec 19 20:31:27 2016
New Revision: 310296
URL: https://svnweb.freebsd.org/changeset/base/310296

Log:
  bhnd(4): Add support for exporting all (or a subtree) of NVRAM
  properties backed by an NVRAM store.
  
  This will be used to support:
  
  - Serializing the current NVRAM state for writing back to flash.
  - Exporting subsidiary device paths for serialization and upload to fullmac
    chipsets.
  
  Additionally, this includes an improvement to BCM-RAW format detection
  to avoid matching on BCM-TEXT NVRAM data.
  
  Approved by:	adrian (mentor)
  Differential Revision:	https://reviews.freebsd.org/D8761

Modified:
  head/sys/dev/bhnd/nvram/bhnd_nvram_data.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data.h
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h
  head/sys/dev/bhnd/nvram/bhnd_nvram_store.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_store.h
  head/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data.c	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.c	Mon Dec 19 20:31:27 2016	(r310296)
@@ -281,6 +281,18 @@ bhnd_nvram_data_count(struct bhnd_nvram_
 }
 
 /**
+ * Return a borrowed reference to the serialization options for @p nv,
+ * suitable for use with bhnd_nvram_data_serialize(), or NULL if none.
+ * 
+ * @param nv The NVRAM data to be queried.
+ */
+bhnd_nvram_plist *
+bhnd_nvram_data_options(struct bhnd_nvram_data *nv)
+{
+	return (nv->cls->op_options(nv));
+}
+
+/**
  * Compute the size of the serialized form of @p nv.
  *
  * Serialization may be performed via bhnd_nvram_data_serialize().

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data.h
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data.h	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.h	Mon Dec 19 20:31:27 2016	(r310296)
@@ -44,6 +44,7 @@
 
 #include "bhnd_nvram.h"
 #include "bhnd_nvram_io.h"
+#include "bhnd_nvram_plist.h"
 #include "bhnd_nvram_value.h"
 
 /* NVRAM data class */
@@ -115,6 +116,7 @@ int			 bhnd_nvram_data_size(struct bhnd_
 int			 bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv,
 			     void *buf, size_t *len);
 
+bhnd_nvram_plist	*bhnd_nvram_data_options(struct bhnd_nvram_data *nv);
 uint32_t		 bhnd_nvram_data_caps(struct bhnd_nvram_data *nv);
 
 const char		*bhnd_nvram_data_next(struct bhnd_nvram_data *nv,

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c	Mon Dec 19 20:31:27 2016	(r310296)
@@ -121,6 +121,7 @@ static const struct bhnd_nvram_bcm_hvar 
 struct bhnd_nvram_bcm {
 	struct bhnd_nvram_data		 nv;	/**< common instance state */
 	struct bhnd_nvram_io		*data;	/**< backing buffer */
+	bhnd_nvram_plist		*opts;	/**< serialization options */
 
 	/** BCM header values */
 	struct bhnd_nvram_bcm_hvar	 hvars[nitems(bhnd_nvram_bcm_hvars)];
@@ -157,7 +158,7 @@ bhnd_nvram_bcm_init(struct bhnd_nvram_bc
 	uint8_t				*p;
 	void				*ptr;
 	size_t				 io_offset, io_size;
-	uint8_t				 crc, valid;
+	uint8_t				 crc, valid, bcm_ver;
 	int				 error;
 
 	if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr))))
@@ -344,6 +345,14 @@ bhnd_nvram_bcm_init(struct bhnd_nvram_bc
 			bcm->count++;
 	}
 
+	/* Populate serialization options from our header */
+	bcm_ver = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER);
+	error = bhnd_nvram_plist_append_bytes(bcm->opts,
+	    BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver, sizeof(bcm_ver),
+	    BHND_NVRAM_TYPE_UINT8);
+	if (error)
+		return (error);
+
 	return (0);
 }
 
@@ -360,6 +369,12 @@ bhnd_nvram_bcm_new(struct bhnd_nvram_dat
 	    "hvar declarations must match bhnd_nvram_bcm_hvars template");
 	memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars));
 
+	/* Allocate (empty) option list, to be populated by
+	 * bhnd_nvram_bcm_init() */
+	bcm->opts = bhnd_nvram_plist_new();
+	if (bcm->opts == NULL)
+		return (ENOMEM);
+
 	/* Parse the BCM input data and initialize our backing
 	 * data representation */
 	if ((error = bhnd_nvram_bcm_init(bcm, io))) {
@@ -377,6 +392,9 @@ bhnd_nvram_bcm_free(struct bhnd_nvram_da
 
 	if (bcm->data != NULL)
 		bhnd_nvram_io_free(bcm->data);
+
+	if (bcm->opts != NULL)
+		bhnd_nvram_plist_release(bcm->opts);
 }
 
 size_t
@@ -386,6 +404,13 @@ bhnd_nvram_bcm_count(struct bhnd_nvram_d
 	return (bcm->count);
 }
 
+static bhnd_nvram_plist *
+bhnd_nvram_bcm_options(struct bhnd_nvram_data *nv)
+{
+	struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
+	return (bcm->opts);
+}
+
 static int
 bhnd_nvram_bcm_size(struct bhnd_nvram_data *nv, size_t *size)
 {

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c	Mon Dec 19 20:31:27 2016	(r310296)
@@ -79,19 +79,19 @@ bhnd_nvram_bcmraw_probe(struct bhnd_nvra
 {
 	char	 envp[16];
 	size_t	 envp_len;
+	size_t	 io_size;
 	int	 error;
 
+	io_size = bhnd_nvram_io_getsize(io);
+
 	/*
-	 * Fetch the initial bytes to try to identify BCM data.
-	 * 
-	 * We always assert a low probe priority, as we only scan the initial
-	 * bytes of the file.
+	 * Fetch initial bytes
 	 */
-	envp_len = bhnd_nv_ummin(sizeof(envp), bhnd_nvram_io_getsize(io));
+	envp_len = bhnd_nv_ummin(sizeof(envp), io_size);
 	if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len)))
 		return (error);
 
-	/* A zero-length BCM-RAW buffer should contain a single terminating
+	/* An empty BCM-RAW buffer should still contain a single terminating
 	 * NUL */
 	if (envp_len == 0)
 		return (ENXIO);
@@ -103,21 +103,33 @@ bhnd_nvram_bcmraw_probe(struct bhnd_nvra
 		return (BHND_NVRAM_DATA_PROBE_MAYBE);
 	}
 
-	/* Don't match on non-ASCII, non-printable data */
+	/* Must contain only printable ASCII characters delimited
+	 * by NUL record delimiters */
 	for (size_t i = 0; i < envp_len; i++) {
 		char c = envp[i];
-		if (envp[i] == '\0')
-			break;
 
-		if (!bhnd_nv_isprint(c))
+		/* If we hit a newline, this is probably BCM-TXT */
+		if (c == '\n')
 			return (ENXIO);
+
+		if (c == '\0' && !bhnd_nv_isprint(c))
+			continue;
 	}
 
-	/* The first character should be a valid key char */
-	if (!bhnd_nv_isalpha(envp[0]))
+	/* A valid BCM-RAW buffer should contain a terminating NUL for
+	 * the last record, followed by a final empty record terminated by
+	 * NUL */
+	envp_len = 2;
+	if (io_size < envp_len)
+		return (ENXIO);
+
+	if ((error = bhnd_nvram_io_read(io, io_size-envp_len, envp, envp_len)))
+		return (error);
+
+	if (envp[0] != '\0' || envp[1] != '\0')
 		return (ENXIO);
 
-	return (BHND_NVRAM_DATA_PROBE_MAYBE);
+	return (BHND_NVRAM_DATA_PROBE_MAYBE + 1);
 }
 
 /**
@@ -245,6 +257,12 @@ bhnd_nvram_bcmraw_count(struct bhnd_nvra
 	return (bcm->count);
 }
 
+static bhnd_nvram_plist *
+bhnd_nvram_bcmraw_options(struct bhnd_nvram_data *nv)
+{
+	return (NULL);
+}
+
 static int
 bhnd_nvram_bcmraw_size(struct bhnd_nvram_data *nv, size_t *size)
 {

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h	Mon Dec 19 20:31:27 2016	(r310296)
@@ -32,6 +32,8 @@
 #ifndef _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_
 #define _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_
 
+#define	BCM_NVRAM_ENCODE_OPT_VERSION	"bcm_version"
+
 /**
  * BCM NVRAM header value data.
  */

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c	Mon Dec 19 20:31:27 2016	(r310296)
@@ -255,6 +255,12 @@ bhnd_nvram_btxt_count(struct bhnd_nvram_
 	return (btxt->count);
 }
 
+static bhnd_nvram_plist *
+bhnd_nvram_btxt_options(struct bhnd_nvram_data *nv)
+{
+	return (NULL);
+}
+
 static int
 bhnd_nvram_btxt_size(struct bhnd_nvram_data *nv, size_t *size)
 {

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c	Mon Dec 19 20:31:27 2016	(r310296)
@@ -416,6 +416,12 @@ bhnd_nvram_sprom_free(struct bhnd_nvram_
 	bhnd_nv_free(sp->idx);
 }
 
+static bhnd_nvram_plist *
+bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
+{
+	return (NULL);
+}
+
 size_t
 bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv)
 {

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c	Mon Dec 19 20:31:27 2016	(r310296)
@@ -256,6 +256,12 @@ bhnd_nvram_tlv_count(struct bhnd_nvram_d
 	return (tlv->count);
 }
 
+static bhnd_nvram_plist *
+bhnd_nvram_tlv_options(struct bhnd_nvram_data *nv)
+{
+	return (NULL);
+}
+
 static int
 bhnd_nvram_tlv_size(struct bhnd_nvram_data *nv, size_t *size)
 {
@@ -350,17 +356,26 @@ bhnd_nvram_tlv_next(struct bhnd_nvram_da
 
 	tlv = (struct bhnd_nvram_tlv *)nv;
 
-	/* Seek past the TLV_ENV record referenced by cookiep */
-	io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep);
-	if (bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL) == NULL)
-		BHND_NV_PANIC("invalid cookiep: %p\n", cookiep);
-
-	/* Fetch the next TLV_ENV record */
-	if ((env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep)) == NULL) {
-		/* No remaining ENV records */
-		return (NULL);
+	/* Find next readable TLV record */
+	if (*cookiep == NULL) {
+		/* Start search at offset 0x0 */
+		io_offset = 0x0;
+		env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
+	} else {
+		/* Seek past the previous env record */
+		io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep);
+		env = bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL);
+		if (env == NULL)
+			BHND_NV_PANIC("invalid cookiep; record missing");
+
+		/* Advance to next env record, update the caller's cookiep */
+		env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
 	}
 
+	/* Check for EOF */
+	if (env == NULL)
+		return (NULL);
+
 	/* Return the NUL terminated name */
 	return (env->envp);
 }

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h	Mon Dec 19 20:31:27 2016	(r310296)
@@ -75,6 +75,10 @@ typedef int		 (bhnd_nvram_data_op_serial
 			     struct bhnd_nvram_data *nv, void *buf,
 			     size_t *len);
 
+/** @see bhnd_nvram_data_options() */
+typedef bhnd_nvram_plist*(bhnd_nvram_data_op_options)(
+			      struct bhnd_nvram_data *nv);
+
 /** @see bhnd_nvram_data_caps() */
 typedef uint32_t	 (bhnd_nvram_data_op_caps)(struct bhnd_nvram_data *nv);
 
@@ -133,6 +137,7 @@ struct bhnd_nvram_data_class {
 	bhnd_nvram_data_op_count		*op_count;
 	bhnd_nvram_data_op_size			*op_size;
 	bhnd_nvram_data_op_serialize		*op_serialize;
+	bhnd_nvram_data_op_options		*op_options;
 	bhnd_nvram_data_op_caps			*op_caps;
 	bhnd_nvram_data_op_next			*op_next;
 	bhnd_nvram_data_op_find			*op_find;
@@ -186,6 +191,7 @@ struct bhnd_nvram_data {
 	_macro(_cname, count)					\
 	_macro(_cname, size)					\
 	_macro(_cname, serialize)				\
+	_macro(_cname, options)					\
 	_macro(_cname, caps)					\
 	_macro(_cname, next)					\
 	_macro(_cname, find)					\

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_store.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_store.c	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_store.c	Mon Dec 19 20:31:27 2016	(r310296)
@@ -71,6 +71,26 @@ static int			 bhnd_nvstore_parse_data(
 static int			 bhnd_nvstore_parse_path_entries(
 				     struct bhnd_nvram_store *sc);
 
+static int			 bhnd_nvram_store_export_child(
+				     struct bhnd_nvram_store *sc,
+				     bhnd_nvstore_path *top,
+				     bhnd_nvstore_path *child,
+				     bhnd_nvram_plist *plist,
+				     uint32_t flags);
+
+static int			 bhnd_nvstore_export_merge(
+				     struct bhnd_nvram_store *sc,
+				     bhnd_nvstore_path *path,
+				     bhnd_nvram_plist *merged,
+				     uint32_t flags);
+
+static int			 bhnd_nvstore_export_devpath_alias(
+				     struct bhnd_nvram_store *sc,
+				     bhnd_nvstore_path *path,
+				     const char *devpath,
+				     bhnd_nvram_plist *plist,
+				     u_long *alias_val);
+
 /**
  * Allocate and initialize a new NVRAM data store instance.
  *
@@ -114,6 +134,16 @@ bhnd_nvram_store_new(struct bhnd_nvram_s
 	/* Retain the NVRAM data */
 	sc->data = bhnd_nvram_data_retain(data);
 	sc->data_caps = bhnd_nvram_data_caps(data);
+	sc->data_opts = bhnd_nvram_data_options(data);
+	if (sc->data_opts != NULL) {
+		bhnd_nvram_plist_retain(sc->data_opts);
+	} else {
+		sc->data_opts = bhnd_nvram_plist_new();
+		if (sc->data_opts == NULL) {
+			error = ENOMEM;
+			goto cleanup;
+		}
+	}
 
 	/* Register required root path */
 	error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH,
@@ -207,6 +237,8 @@ bhnd_nvram_store_free(struct bhnd_nvram_
 	if (sc->data != NULL)
 		bhnd_nvram_data_release(sc->data);
 
+	if (sc->data_opts != NULL)
+		bhnd_nvram_plist_release(sc->data_opts);
 
 	BHND_NVSTORE_LOCK_DESTROY(sc);
 	bhnd_nv_free(sc);
@@ -400,6 +432,571 @@ bhnd_nvstore_parse_path_entries(struct b
 
 	return (0);
 }
+
+
+/**
+ * Merge exported per-path variables (uncommitted, committed, or both) into 
+ * the empty @p merged property list.
+ * 
+ * @param	sc	The NVRAM store instance.
+ * @param	path	The NVRAM path to be exported.
+ * @param	merged	The property list to populate with the merged results.
+ * @param	flags	Export flags. See BHND_NVSTORE_EXPORT_*.
+ * 
+ * @retval 0		success
+ * @retval ENOMEM	If allocation fails.
+ * @retval non-zero	If merging the variables defined in @p path otherwise
+ *			fails, a regular unix error code will be returned.
+ */
+static int
+bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc,
+    bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags)
+{
+	void	*cookiep, *idxp;
+	int	 error;
+
+	/* Populate merged list with all pending variables */
+	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
+		bhnd_nvram_prop *prop;
+
+		prop = NULL;
+		while ((prop = bhnd_nvram_plist_next(path->pending, prop))) {
+			/* Skip variables marked for deletion */
+			if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) {
+				if (bhnd_nvram_prop_is_null(prop))
+					continue;
+			}
+
+			/* Append to merged list */
+			error = bhnd_nvram_plist_append(merged, prop);
+			if (error)
+				return (error);
+		}
+	}
+
+	/* Skip merging committed variables? */
+	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED))
+		return (0);
+
+	/* Merge in the committed NVRAM variables */
+	idxp = NULL;
+	while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) {
+		const char	*name;
+		bhnd_nvram_val	*val;
+
+		/* Fetch the variable name */
+		name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
+
+		/* Trim device path prefix */
+		if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)
+			name = bhnd_nvram_trim_path_name(name);
+
+		/* Skip if already defined in pending updates */
+		if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
+			if (bhnd_nvram_plist_contains(path->pending, name))
+				continue;
+		}
+
+		/* Skip if higher precedence value was already defined. This
+		 * may occur if the underlying data store contains duplicate
+		 * keys; iteration will always return the definition with
+		 * the highest precedence first */
+		if (bhnd_nvram_plist_contains(merged, name))
+			continue;
+
+		/* Fetch the variable's value representation */
+		if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val)))
+			return (error);
+
+		/* Add to path variable list */
+		error = bhnd_nvram_plist_append_val(merged, name, val);
+		bhnd_nvram_val_release(val);
+		if (error)
+			return (error);
+	}
+
+	return (0);
+}
+
+/**
+ * Find a free alias value for @p path, and append the devpathXX alias
+ * declaration to @p plist.
+ * 
+ * @param	sc		The NVRAM store instance.
+ * @param	path		The NVRAM path for which a devpath alias
+ *				variable should be produced.
+ * @param	devpath		The devpathXX path value for @p path.
+ * @param	plist		The property list to which @p path's devpath
+ *				variable will be appended.
+ * @param[out]	alias_val	On success, will be set to the alias value
+ *				allocated for @p path.
+ * 
+ * @retval 0		success
+ * @retval ENOMEM	If allocation fails.
+ * @retval non-zero	If merging the variables defined in @p path otherwise
+ *			fails, a regular unix error code will be returned.
+ */
+static int
+bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc,
+    bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist,
+    u_long *alias_val)
+{
+	bhnd_nvstore_alias	*alias;
+	char			*pathvar;
+	int			 error;
+
+	*alias_val = 0;
+
+	/* Prefer alias value already reserved for this path. */
+	alias = bhnd_nvstore_find_alias(sc, path->path_str);
+	if (alias != NULL) {
+		*alias_val = alias->alias;
+
+		/* Allocate devpathXX variable name */
+		bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
+		if (pathvar == NULL)
+			return (ENOMEM);
+
+		/* Append alias variable to property list */
+		error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
+
+		BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use",
+		   * alias_val, path->path_str));
+
+		bhnd_nv_free(pathvar);
+		return (error);
+	}
+
+	/* Find the next free devpathXX alias entry */
+	while (1) {
+		/* Skip existing reserved alias values */
+		while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) {
+			if (*alias_val == ULONG_MAX)
+				return (ENOMEM);
+
+			(*alias_val)++;
+		}
+
+		/* Allocate devpathXX variable name */
+		bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
+		if (pathvar == NULL)
+			return (ENOMEM);
+
+		/* If not in-use, we can terminate the search */
+		if (!bhnd_nvram_plist_contains(plist, pathvar))
+			break;
+
+		/* Keep searching */
+		bhnd_nv_free(pathvar);
+
+		if (*alias_val == ULONG_MAX)
+			return (ENOMEM);
+
+		(*alias_val)++;
+	}
+
+	/* Append alias variable to property list */
+	error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
+
+	bhnd_nv_free(pathvar);
+	return (error);
+}
+
+/**
+ * Export a single @p child path's properties, appending the result to @p plist.
+ * 
+ * @param	sc		The NVRAM store instance.
+ * @param	top		The root NVRAM path being exported.
+ * @param	child		The NVRAM path to be exported.
+ * @param	plist		The property list to which @p child's exported
+ *				properties should be appended.
+ * @param	flags		Export flags. See BHND_NVSTORE_EXPORT_*.
+ * 
+ * @retval 0		success
+ * @retval ENOMEM	If allocation fails.
+ * @retval non-zero	If merging the variables defined in @p path otherwise
+ *			fails, a regular unix error code will be returned.
+ */
+static int
+bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc,
+    bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist,
+    uint32_t flags)
+{
+	bhnd_nvram_plist	*path_vars;
+	bhnd_nvram_prop		*prop;
+	const char		*relpath;
+	char			*prefix, *namebuf;
+	size_t			 prefix_len, relpath_len;
+	size_t			 namebuf_size, num_props;
+	bool			 emit_compact_devpath;
+	int			 error;
+
+	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
+
+	prefix = NULL;
+	num_props = 0;
+	path_vars = NULL;
+	namebuf = NULL;
+
+	/* Determine the path relative to the top-level path */
+	relpath = bhnd_nvstore_parse_relpath(top->path_str, child->path_str);
+	if (relpath == NULL) {
+		/* Skip -- not a child of the root path */
+		return (0);
+	}
+	relpath_len = strlen(relpath);
+
+	/* Skip sub-path if export of children was not requested,  */
+	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0)
+		return (0);
+
+	/* Collect all variables to be included in the export */
+	if ((path_vars = bhnd_nvram_plist_new()) == NULL)
+		return (ENOMEM);
+
+	if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) {
+		bhnd_nvram_plist_release(path_vars);
+		return (error);
+	}
+
+	/* Skip if no children are to be exported */
+	if (bhnd_nvram_plist_count(path_vars) == 0) {
+		bhnd_nvram_plist_release(path_vars);
+		return (0);
+	}
+
+	/* Determine appropriate device path encoding */
+	emit_compact_devpath = false;
+	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) {
+		/* Re-encode as compact (if non-empty path) */
+		if (relpath_len > 0)
+			emit_compact_devpath = true;
+	} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) {
+		/* Re-encode with fully expanded device path */
+		emit_compact_devpath = false;
+	} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
+		/* Preserve existing encoding of this path */
+		if (bhnd_nvstore_find_alias(sc, child->path_str) != NULL)
+			emit_compact_devpath = true;
+	} else {
+		BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags);
+		error = EINVAL;
+		goto finished;
+	}
+
+	/* Allocate variable device path prefix to use for all property names,
+	 * and if using compact encoding, emit the devpathXX= variable */
+	prefix = NULL;
+	prefix_len = 0;
+	if (emit_compact_devpath) {
+		u_long	alias_val;
+		int	len;
+
+		/* Reserve an alias value and append the devpathXX= variable to
+		 * the property list */
+		error = bhnd_nvstore_export_devpath_alias(sc, child, relpath,
+		    plist, &alias_val);
+		if (error)
+			goto finished;
+
+		/* Allocate variable name prefix */
+		len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val);
+		if (prefix == NULL) {
+			error = ENOMEM;
+			goto finished;
+		}
+	
+		prefix_len = len;
+	} else if (relpath_len > 0) {
+		int len;
+
+		/* Allocate the variable name prefix, appending '/' to the
+		 * relative path */
+		len = bhnd_nv_asprintf(&prefix, "%s/", relpath);
+		if (prefix == NULL) {
+			error = ENOMEM;
+			goto finished;
+		}
+
+		prefix_len = len;
+	}
+
+	/* If prefixing of variable names is required, allocate a name
+	 * formatting buffer */
+	namebuf_size = 0;
+	if (prefix != NULL) {
+		size_t	maxlen;
+
+		/* Find the maximum name length */
+		maxlen = 0;
+		prop = NULL;
+		while ((prop = bhnd_nvram_plist_next(path_vars, prop))) {
+			const char *name;
+
+			name = bhnd_nvram_prop_name(prop);
+			maxlen = bhnd_nv_ummax(strlen(name), maxlen);
+		}
+
+		/* Allocate name buffer (path-prefix + name + '\0') */
+		namebuf_size = prefix_len + maxlen + 1;
+		namebuf = bhnd_nv_malloc(namebuf_size);
+		if (namebuf == NULL) {
+			error = ENOMEM;
+			goto finished;
+		}
+	}
+
+	/* Append all path variables to the export plist, prepending the
+	 * device-path prefix to the variable names, if required */
+	prop = NULL;
+	while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) {
+		const char *name;
+
+		/* Prepend device prefix to the variable name */
+		name = bhnd_nvram_prop_name(prop);
+		if (prefix != NULL) {
+			int len;
+
+			/*
+			 * Write prefixed variable name to our name buffer.
+			 * 
+			 * We precalcuate the size when scanning all names 
+			 * above, so this should always succeed.
+			 */
+			len = snprintf(namebuf, namebuf_size, "%s%s", prefix,
+			    name);
+			if (len < 0 || (size_t)len >= namebuf_size)
+				BHND_NV_PANIC("invalid max_name_len");
+
+			name = namebuf;
+		}
+
+		/* Add property to export plist */
+		error = bhnd_nvram_plist_append_val(plist, name,
+		    bhnd_nvram_prop_val(prop));
+		if (error)
+			goto finished;
+	}
+
+	/* Success */
+	error = 0;
+
+finished:
+	if (prefix != NULL)
+		bhnd_nv_free(prefix);
+
+	if (namebuf != NULL)
+		bhnd_nv_free(namebuf);
+
+	if (path_vars != NULL)
+		bhnd_nvram_plist_release(path_vars);
+
+	return (error);
+}
+
+/**
+ * Export a flat, ordered NVRAM property list representation of all NVRAM
+ * properties at @p path.
+ * 
+ * @param	sc	The NVRAM store instance.
+ * @param	path	The NVRAM path to export, or NULL to select the root
+ *			path.
+ * @param[out]	cls	On success, will be set to the backing data class
+ *			of @p sc. If the data class is are not desired,
+ *			a NULL pointer may be provided.
+ * @param[out]	props	On success, will be set to a caller-owned property
+ *			list containing the exported properties. The caller is
+ *			responsible for releasing this value via
+ *			bhnd_nvram_plist_release().
+ * @param[out]	options	On success, will be set to a caller-owned property
+ *			list containing the current NVRAM serialization options
+ *			for @p sc. The caller is responsible for releasing this
+ *			value via bhnd_nvram_plist_release().
+ * @param	flags	Export flags. See BHND_NVSTORE_EXPORT_*.
+ * 
+ * @retval 0		success
+ * @retval EINVAL	If @p flags is invalid.
+ * @retval ENOENT	The requested path was not found.
+ * @retval ENOMEM	If allocation fails.
+ * @retval non-zero	If export of  @p path otherwise fails, a regular unix
+ *			error code will be returned.
+ */
+int
+bhnd_nvram_store_export(struct bhnd_nvram_store *sc, const char *path,
+    bhnd_nvram_data_class **cls, bhnd_nvram_plist **props,
+    bhnd_nvram_plist **options, uint32_t flags)
+{
+	bhnd_nvram_plist	*unordered;
+	bhnd_nvstore_path	*top;
+	bhnd_nvram_prop		*prop;
+	const char		*name;
+	void			*cookiep;
+	size_t			 num_dpath_flags;
+	int			 error;
+	
+	*props = NULL;
+	unordered = NULL;
+	num_dpath_flags = 0;
+	if (options != NULL)
+		*options = NULL;
+
+	/* Default to exporting root path */
+	if (path == NULL)
+		path = BHND_NVSTORE_ROOT_PATH;
+
+	/* Default to exporting all properties */
+	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) &&
+	    !BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED))
+	{
+		flags |= BHND_NVSTORE_EXPORT_ALL_VARS;
+	}
+
+	/* Default to preserving the current device path encoding */
+	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS) &&
+	    !BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
+	{
+		flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS;
+	}
+
+	/* Exactly one device path encoding flag must be set */
+	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS))
+		num_dpath_flags++;
+
+	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
+		num_dpath_flags++;
+
+	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS))
+		num_dpath_flags++;
+
+	if (num_dpath_flags != 1)
+		return (EINVAL);
+
+	/* If EXPORT_DELETED is set, EXPORT_UNCOMMITTED must be set too */
+	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED) &&
+	    !BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED))
+	{
+		return (EINVAL);
+	}
+
+	/* Lock internal state before querying paths/properties */
+	BHND_NVSTORE_LOCK(sc);
+
+	/* Fetch referenced path */
+	top = bhnd_nvstore_get_path(sc, path, strlen(path));
+	if (top == NULL) {
+		error = ENOENT;
+		goto failed;
+	}
+
+	/* Allocate new, empty property list */
+	if ((unordered = bhnd_nvram_plist_new()) == NULL) {
+		error = ENOMEM;
+		goto failed;
+	}
+
+	/* Export the top-level path first */
+	error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags);
+	if (error)
+		goto failed;
+
+	/* Attempt to export any children of the root path */
+	for (size_t i = 0; i < nitems(sc->paths); i++) {
+		bhnd_nvstore_path *child;
+
+		LIST_FOREACH(child, &sc->paths[i], np_link) {
+			/* Top-level path was already exported */
+			if (child == top)
+				continue;
+
+			error = bhnd_nvram_store_export_child(sc, top,
+			    child, unordered, flags);
+			if (error)
+				goto failed;
+		}
+	}
+
+	/* If requested, provide the current class and serialization options */
+	if (cls != NULL)
+		*cls = bhnd_nvram_data_get_class(sc->data);
+
+	if (options != NULL)
+		*options = bhnd_nvram_plist_retain(sc->data_opts);
+
+	/*
+	 * If we're re-encoding device paths, don't bother preserving the
+	 * existing NVRAM variable order; our variable names will not match
+	 * the existing backing NVRAM data.
+	 */
+	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
+		*props = unordered;
+		unordered = NULL;
+
+		goto finished;
+	}
+
+	/* 
+	 * Re-order the flattened output to match the existing NVRAM variable
+	 * ordering.
+	 * 
+	 * We append all new variables at the end of the input; this should
+	 * reduce the delta that needs to be written (e.g. to flash) when
+	 * committing NVRAM updates, and should result in a serialization
+	 * identical to the input serialization if uncommitted updates are
+	 * excluded from the export.
+	 */
+	if ((*props = bhnd_nvram_plist_new()) == NULL) {
+		error = ENOMEM;
+		goto failed;
+	}
+
+	/* Using the backing NVRAM data ordering to order all variables
+	 * currently defined in the backing store */ 
+	cookiep = NULL;
+	while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
+		prop = bhnd_nvram_plist_get_prop(unordered, name);
+		if (prop == NULL)
+			continue;
+
+		/* Append to ordered result */
+		if ((error = bhnd_nvram_plist_append(*props, prop)))
+			goto failed;
+	
+		/* Remove from unordered list */
+		bhnd_nvram_plist_remove(unordered, name);
+	}
+
+	/* Any remaining variables are new, and should be appended to the
+	 * end of the export list */
+	prop = NULL;
+	while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) {
+		if ((error = bhnd_nvram_plist_append(*props, prop)))
+			goto failed;
+	}
+
+	/* Export complete */
+finished:
+	BHND_NVSTORE_UNLOCK(sc);
+
+	if (unordered != NULL)
+		bhnd_nvram_plist_release(unordered);
+
+	return (0);
+
+failed:
+	BHND_NVSTORE_UNLOCK(sc);
+
+	if (unordered != NULL)
+		bhnd_nvram_plist_release(unordered);
+
+	if (options != NULL && *options != NULL)
+		bhnd_nvram_plist_release(*options);
+
+	if (*props != NULL)
+		bhnd_nvram_plist_release(*props);
+
+	return (error);
+}
+
 /**
  * Read an NVRAM variable.
  *

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_store.h
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_store.h	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_store.h	Mon Dec 19 20:31:27 2016	(r310296)
@@ -45,10 +45,24 @@
 
 #include "bhnd_nvram_data.h"
 #include "bhnd_nvram_io.h"
-#include "bhnd_nvram_value.h"
 
 struct bhnd_nvram_store;
 
+/**
+ * NVRAM export flags.
+ */
+enum {
+	BHND_NVSTORE_EXPORT_CHILDREN		= (1<<0),	/**< Include all subpaths */
+	BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS	= (1<<1),	/**< Preserve existing device path definitions (default) */
+	BHND_NVSTORE_EXPORT_COMPACT_DEVPATHS	= (1<<2),	/**< Re-encode all device paths using compact syntax */
+	BHND_NVSTORE_EXPORT_EXPAND_DEVPATHS	= (1<<3),	/**< Re-encode all device paths using non-compact syntax */
+	BHND_NVSTORE_EXPORT_ALL_VARS		= (1<<6|1<<7),	/**< Include all variables (default) */
+	BHND_NVSTORE_EXPORT_COMMITTED		= (1<<6),	/**< Include all committed changes */
+	BHND_NVSTORE_EXPORT_UNCOMMITTED		= (1<<7),	/**< Include all uncommitted changes (not including deletions) */
+	BHND_NVSTORE_EXPORT_DELETED		= (1<<8),	/**< Include all uncommitted deltions (as
+								     properties of type BHND_NVRAM_TYPE_NULL) */
+};
+
 int	bhnd_nvram_store_new(struct bhnd_nvram_store **store,
 	    struct bhnd_nvram_data *data);
 
@@ -57,6 +71,10 @@ int	bhnd_nvram_store_parse_new(struct bh
 
 void	bhnd_nvram_store_free(struct bhnd_nvram_store *store);
 
+int	bhnd_nvram_store_export(struct bhnd_nvram_store *store,
+	    const char *path, bhnd_nvram_data_class **cls,
+	    bhnd_nvram_plist **props, bhnd_nvram_plist **options,
+	    uint32_t flags);
 
 int	bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
 	    void *outp, size_t *olen, bhnd_nvram_type otype);

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c	Mon Dec 19 20:31:27 2016	(r310296)
@@ -342,7 +342,7 @@ bhnd_nvstore_path_register_update(struct
 	/* Determine whether the variable is currently defined in the
 	 * backing NVRAM data, and derive its full path-prefixed name */
 	nvram_committed = false;
-	cookiep = bhnd_nvstore_index_lookup(sc, path->index, name);
+	cookiep = bhnd_nvstore_path_data_lookup(sc, path, name);
 	if (cookiep != NULL) {
 		/* Variable is defined in the backing data */
 		nvram_committed = true;

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h	Mon Dec 19 20:28:27 2016	(r310295)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_storevar.h	Mon Dec 19 20:31:27 2016	(r310296)
@@ -271,6 +271,7 @@ struct bhnd_nvram_store {
 #endif
 	struct bhnd_nvram_data	*data;		/**< backing data */
 	uint32_t		 data_caps;	/**< data capability flags */
+	bhnd_nvram_plist	*data_opts;	/**< data serialization options */
 
 	bhnd_nvstore_alias_list	 aliases[4];	/**< path alias hash table */
 	size_t			 num_aliases;	/**< alias count */

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list