xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_store.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
177cb4d3eSLandon J. Fuller /*-
277cb4d3eSLandon J. Fuller  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
377cb4d3eSLandon J. Fuller  * All rights reserved.
477cb4d3eSLandon J. Fuller  *
577cb4d3eSLandon J. Fuller  * Redistribution and use in source and binary forms, with or without
677cb4d3eSLandon J. Fuller  * modification, are permitted provided that the following conditions
777cb4d3eSLandon J. Fuller  * are met:
877cb4d3eSLandon J. Fuller  * 1. Redistributions of source code must retain the above copyright
977cb4d3eSLandon J. Fuller  *    notice, this list of conditions and the following disclaimer,
1077cb4d3eSLandon J. Fuller  *    without modification.
1177cb4d3eSLandon J. Fuller  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1277cb4d3eSLandon J. Fuller  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
1377cb4d3eSLandon J. Fuller  *    redistribution must be conditioned upon including a substantially
1477cb4d3eSLandon J. Fuller  *    similar Disclaimer requirement for further binary redistribution.
1577cb4d3eSLandon J. Fuller  *
1677cb4d3eSLandon J. Fuller  * NO WARRANTY
1777cb4d3eSLandon J. Fuller  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1877cb4d3eSLandon J. Fuller  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1977cb4d3eSLandon J. Fuller  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
2077cb4d3eSLandon J. Fuller  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2177cb4d3eSLandon J. Fuller  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2277cb4d3eSLandon J. Fuller  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2377cb4d3eSLandon J. Fuller  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2477cb4d3eSLandon J. Fuller  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2577cb4d3eSLandon J. Fuller  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2677cb4d3eSLandon J. Fuller  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
2777cb4d3eSLandon J. Fuller  * THE POSSIBILITY OF SUCH DAMAGES.
2877cb4d3eSLandon J. Fuller  */
2977cb4d3eSLandon J. Fuller 
3019be09f3SLandon J. Fuller #include <sys/param.h>
3119be09f3SLandon J. Fuller #include <sys/hash.h>
3291898857SMark Johnston #include <sys/limits.h>
3377cb4d3eSLandon J. Fuller #include <sys/queue.h>
3477cb4d3eSLandon J. Fuller 
3577cb4d3eSLandon J. Fuller #ifdef _KERNEL
3677cb4d3eSLandon J. Fuller 
3719be09f3SLandon J. Fuller #include <sys/ctype.h>
3877cb4d3eSLandon J. Fuller #include <sys/systm.h>
3977cb4d3eSLandon J. Fuller 
4019be09f3SLandon J. Fuller #include <machine/_inttypes.h>
4119be09f3SLandon J. Fuller 
4277cb4d3eSLandon J. Fuller #else /* !_KERNEL */
4377cb4d3eSLandon J. Fuller 
4419be09f3SLandon J. Fuller #include <ctype.h>
4577cb4d3eSLandon J. Fuller #include <errno.h>
4619be09f3SLandon J. Fuller #include <inttypes.h>
4777cb4d3eSLandon J. Fuller #include <stdbool.h>
4877cb4d3eSLandon J. Fuller #include <stdio.h>
4977cb4d3eSLandon J. Fuller #include <stdint.h>
5077cb4d3eSLandon J. Fuller #include <stdlib.h>
5177cb4d3eSLandon J. Fuller #include <string.h>
5277cb4d3eSLandon J. Fuller 
5377cb4d3eSLandon J. Fuller #endif /* _KERNEL */
5477cb4d3eSLandon J. Fuller 
5577cb4d3eSLandon J. Fuller #include "bhnd_nvram_private.h"
5677cb4d3eSLandon J. Fuller #include "bhnd_nvram_datavar.h"
5777cb4d3eSLandon J. Fuller 
5877cb4d3eSLandon J. Fuller #include "bhnd_nvram_storevar.h"
5977cb4d3eSLandon J. Fuller 
6077cb4d3eSLandon J. Fuller /*
6177cb4d3eSLandon J. Fuller  * BHND NVRAM Store
6277cb4d3eSLandon J. Fuller  *
6377cb4d3eSLandon J. Fuller  * Manages in-memory and persistent representations of NVRAM data.
6477cb4d3eSLandon J. Fuller  */
6577cb4d3eSLandon J. Fuller 
6619be09f3SLandon J. Fuller static int			 bhnd_nvstore_parse_data(
6719be09f3SLandon J. Fuller 				     struct bhnd_nvram_store *sc);
6819be09f3SLandon J. Fuller 
6919be09f3SLandon J. Fuller static int			 bhnd_nvstore_parse_path_entries(
7019be09f3SLandon J. Fuller 				     struct bhnd_nvram_store *sc);
7177cb4d3eSLandon J. Fuller 
72a7c43ebdSLandon J. Fuller static int			 bhnd_nvram_store_export_child(
73a7c43ebdSLandon J. Fuller 				     struct bhnd_nvram_store *sc,
74a7c43ebdSLandon J. Fuller 				     bhnd_nvstore_path *top,
75a7c43ebdSLandon J. Fuller 				     bhnd_nvstore_path *child,
76a7c43ebdSLandon J. Fuller 				     bhnd_nvram_plist *plist,
77a7c43ebdSLandon J. Fuller 				     uint32_t flags);
78a7c43ebdSLandon J. Fuller 
79a7c43ebdSLandon J. Fuller static int			 bhnd_nvstore_export_merge(
80a7c43ebdSLandon J. Fuller 				     struct bhnd_nvram_store *sc,
81a7c43ebdSLandon J. Fuller 				     bhnd_nvstore_path *path,
82a7c43ebdSLandon J. Fuller 				     bhnd_nvram_plist *merged,
83a7c43ebdSLandon J. Fuller 				     uint32_t flags);
84a7c43ebdSLandon J. Fuller 
85a7c43ebdSLandon J. Fuller static int			 bhnd_nvstore_export_devpath_alias(
86a7c43ebdSLandon J. Fuller 				     struct bhnd_nvram_store *sc,
87a7c43ebdSLandon J. Fuller 				     bhnd_nvstore_path *path,
88a7c43ebdSLandon J. Fuller 				     const char *devpath,
89a7c43ebdSLandon J. Fuller 				     bhnd_nvram_plist *plist,
90a7c43ebdSLandon J. Fuller 				     u_long *alias_val);
91a7c43ebdSLandon J. Fuller 
9277cb4d3eSLandon J. Fuller /**
9377cb4d3eSLandon J. Fuller  * Allocate and initialize a new NVRAM data store instance.
9477cb4d3eSLandon J. Fuller  *
9577cb4d3eSLandon J. Fuller  * The caller is responsible for deallocating the instance via
9677cb4d3eSLandon J. Fuller  * bhnd_nvram_store_free().
9777cb4d3eSLandon J. Fuller  *
9877cb4d3eSLandon J. Fuller  * @param[out] store On success, a pointer to the newly allocated NVRAM data
9977cb4d3eSLandon J. Fuller  * instance.
10077cb4d3eSLandon J. Fuller  * @param data The NVRAM data to be managed by the returned NVRAM data store
10177cb4d3eSLandon J. Fuller  * instance.
10277cb4d3eSLandon J. Fuller  *
10377cb4d3eSLandon J. Fuller  * @retval 0 success
10477cb4d3eSLandon J. Fuller  * @retval non-zero if an error occurs during allocation or initialization, a
10577cb4d3eSLandon J. Fuller  * regular unix error code will be returned.
10677cb4d3eSLandon J. Fuller  */
10777cb4d3eSLandon J. Fuller int
bhnd_nvram_store_new(struct bhnd_nvram_store ** store,struct bhnd_nvram_data * data)10877cb4d3eSLandon J. Fuller bhnd_nvram_store_new(struct bhnd_nvram_store **store,
10977cb4d3eSLandon J. Fuller     struct bhnd_nvram_data *data)
11077cb4d3eSLandon J. Fuller {
11177cb4d3eSLandon J. Fuller 	struct bhnd_nvram_store *sc;
11277cb4d3eSLandon J. Fuller 	int			 error;
11377cb4d3eSLandon J. Fuller 
11477cb4d3eSLandon J. Fuller 	/* Allocate new instance */
11577cb4d3eSLandon J. Fuller 	sc = bhnd_nv_calloc(1, sizeof(*sc));
11677cb4d3eSLandon J. Fuller 	if (sc == NULL)
11777cb4d3eSLandon J. Fuller 		return (ENOMEM);
11877cb4d3eSLandon J. Fuller 
11919be09f3SLandon J. Fuller 	BHND_NVSTORE_LOCK_INIT(sc);
12019be09f3SLandon J. Fuller 	BHND_NVSTORE_LOCK(sc);
12119be09f3SLandon J. Fuller 
12219be09f3SLandon J. Fuller 	/* Initialize path hash table */
12319be09f3SLandon J. Fuller 	sc->num_paths = 0;
12419be09f3SLandon J. Fuller 	for (size_t i = 0; i < nitems(sc->paths); i++)
12519be09f3SLandon J. Fuller 		LIST_INIT(&sc->paths[i]);
12619be09f3SLandon J. Fuller 
12719be09f3SLandon J. Fuller 	/* Initialize alias hash table */
12819be09f3SLandon J. Fuller 	sc->num_aliases = 0;
12919be09f3SLandon J. Fuller 	for (size_t i = 0; i < nitems(sc->aliases); i++)
13019be09f3SLandon J. Fuller 		LIST_INIT(&sc->aliases[i]);
13177cb4d3eSLandon J. Fuller 
13277cb4d3eSLandon J. Fuller 	/* Retain the NVRAM data */
13319be09f3SLandon J. Fuller 	sc->data = bhnd_nvram_data_retain(data);
13419be09f3SLandon J. Fuller 	sc->data_caps = bhnd_nvram_data_caps(data);
135a7c43ebdSLandon J. Fuller 	sc->data_opts = bhnd_nvram_data_options(data);
136a7c43ebdSLandon J. Fuller 	if (sc->data_opts != NULL) {
137a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_retain(sc->data_opts);
138a7c43ebdSLandon J. Fuller 	} else {
139a7c43ebdSLandon J. Fuller 		sc->data_opts = bhnd_nvram_plist_new();
140a7c43ebdSLandon J. Fuller 		if (sc->data_opts == NULL) {
141a7c43ebdSLandon J. Fuller 			error = ENOMEM;
142a7c43ebdSLandon J. Fuller 			goto cleanup;
143a7c43ebdSLandon J. Fuller 		}
144a7c43ebdSLandon J. Fuller 	}
14577cb4d3eSLandon J. Fuller 
14619be09f3SLandon J. Fuller 	/* Register required root path */
14719be09f3SLandon J. Fuller 	error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH,
14819be09f3SLandon J. Fuller 	    BHND_NVSTORE_ROOT_PATH_LEN);
14919be09f3SLandon J. Fuller 	if (error)
15077cb4d3eSLandon J. Fuller 		goto cleanup;
15177cb4d3eSLandon J. Fuller 
15219be09f3SLandon J. Fuller 	sc->root_path = bhnd_nvstore_get_path(sc, BHND_NVSTORE_ROOT_PATH,
15319be09f3SLandon J. Fuller 	    BHND_NVSTORE_ROOT_PATH_LEN);
15419be09f3SLandon J. Fuller 	BHND_NV_ASSERT(sc->root_path, ("missing root path"));
15519be09f3SLandon J. Fuller 
15619be09f3SLandon J. Fuller 	/* Parse all variables vended by our backing NVRAM data instance,
15719be09f3SLandon J. Fuller 	 * generating all path entries, alias entries, and variable indexes */
15819be09f3SLandon J. Fuller 	if ((error = bhnd_nvstore_parse_data(sc)))
15919be09f3SLandon J. Fuller 		goto cleanup;
16077cb4d3eSLandon J. Fuller 
16177cb4d3eSLandon J. Fuller 	*store = sc;
16219be09f3SLandon J. Fuller 
16319be09f3SLandon J. Fuller 	BHND_NVSTORE_UNLOCK(sc);
16477cb4d3eSLandon J. Fuller 	return (0);
16577cb4d3eSLandon J. Fuller 
16677cb4d3eSLandon J. Fuller cleanup:
16719be09f3SLandon J. Fuller 	BHND_NVSTORE_UNLOCK(sc);
16877cb4d3eSLandon J. Fuller 	bhnd_nvram_store_free(sc);
16977cb4d3eSLandon J. Fuller 	return (error);
17077cb4d3eSLandon J. Fuller }
17177cb4d3eSLandon J. Fuller 
17277cb4d3eSLandon J. Fuller /**
17377cb4d3eSLandon J. Fuller  * Allocate and initialize a new NVRAM data store instance, parsing the
17477cb4d3eSLandon J. Fuller  * NVRAM data from @p io.
17577cb4d3eSLandon J. Fuller  *
17677cb4d3eSLandon J. Fuller  * The caller is responsible for deallocating the instance via
17777cb4d3eSLandon J. Fuller  * bhnd_nvram_store_free().
17877cb4d3eSLandon J. Fuller  *
17977cb4d3eSLandon J. Fuller  * The NVRAM data mapped by @p io will be copied, and @p io may be safely
18077cb4d3eSLandon J. Fuller  * deallocated after bhnd_nvram_store_new() returns.
18177cb4d3eSLandon J. Fuller  *
18277cb4d3eSLandon J. Fuller  * @param[out] store On success, a pointer to the newly allocated NVRAM data
18377cb4d3eSLandon J. Fuller  * instance.
18477cb4d3eSLandon J. Fuller  * @param io An I/O context mapping the NVRAM data to be copied and parsed.
18577cb4d3eSLandon J. Fuller  * @param cls The NVRAM data class to be used when parsing @p io, or NULL
18677cb4d3eSLandon J. Fuller  * to perform runtime identification of the appropriate data class.
18777cb4d3eSLandon J. Fuller  *
18877cb4d3eSLandon J. Fuller  * @retval 0 success
18977cb4d3eSLandon J. Fuller  * @retval non-zero if an error occurs during allocation or initialization, a
19077cb4d3eSLandon J. Fuller  * regular unix error code will be returned.
19177cb4d3eSLandon J. Fuller  */
19277cb4d3eSLandon J. Fuller int
bhnd_nvram_store_parse_new(struct bhnd_nvram_store ** store,struct bhnd_nvram_io * io,bhnd_nvram_data_class * cls)19377cb4d3eSLandon J. Fuller bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
19458efe686SLandon J. Fuller     struct bhnd_nvram_io *io, bhnd_nvram_data_class *cls)
19577cb4d3eSLandon J. Fuller {
19677cb4d3eSLandon J. Fuller 	struct bhnd_nvram_data	*data;
19777cb4d3eSLandon J. Fuller 	int			 error;
19877cb4d3eSLandon J. Fuller 
19977cb4d3eSLandon J. Fuller 	/* Try to parse the data */
20077cb4d3eSLandon J. Fuller 	if ((error = bhnd_nvram_data_new(cls, &data, io)))
20177cb4d3eSLandon J. Fuller 		return (error);
20277cb4d3eSLandon J. Fuller 
20377cb4d3eSLandon J. Fuller 	/* Try to create our new store instance */
20477cb4d3eSLandon J. Fuller 	error = bhnd_nvram_store_new(store, data);
20577cb4d3eSLandon J. Fuller 	bhnd_nvram_data_release(data);
20677cb4d3eSLandon J. Fuller 
20777cb4d3eSLandon J. Fuller 	return (error);
20877cb4d3eSLandon J. Fuller }
20977cb4d3eSLandon J. Fuller 
21077cb4d3eSLandon J. Fuller /**
21177cb4d3eSLandon J. Fuller  * Free an NVRAM store instance, releasing all associated resources.
21277cb4d3eSLandon J. Fuller  *
21377cb4d3eSLandon J. Fuller  * @param sc A store instance previously allocated via
21477cb4d3eSLandon J. Fuller  * bhnd_nvram_store_new().
21577cb4d3eSLandon J. Fuller  */
21677cb4d3eSLandon J. Fuller void
bhnd_nvram_store_free(struct bhnd_nvram_store * sc)21777cb4d3eSLandon J. Fuller bhnd_nvram_store_free(struct bhnd_nvram_store *sc)
21877cb4d3eSLandon J. Fuller {
21977cb4d3eSLandon J. Fuller 
22019be09f3SLandon J. Fuller 	/* Clean up alias hash table */
22119be09f3SLandon J. Fuller 	for (size_t i = 0; i < nitems(sc->aliases); i++) {
22219be09f3SLandon J. Fuller 		bhnd_nvstore_alias *alias, *anext;
22319be09f3SLandon J. Fuller 		LIST_FOREACH_SAFE(alias, &sc->aliases[i], na_link, anext)
22419be09f3SLandon J. Fuller 			bhnd_nv_free(alias);
22577cb4d3eSLandon J. Fuller 	}
22677cb4d3eSLandon J. Fuller 
22719be09f3SLandon J. Fuller 	/* Clean up path hash table */
22819be09f3SLandon J. Fuller 	for (size_t i = 0; i < nitems(sc->paths); i++) {
22919be09f3SLandon J. Fuller 		bhnd_nvstore_path *path, *pnext;
23019be09f3SLandon J. Fuller 		LIST_FOREACH_SAFE(path, &sc->paths[i], np_link, pnext)
23119be09f3SLandon J. Fuller 			bhnd_nvstore_path_free(path);
23219be09f3SLandon J. Fuller 	}
23377cb4d3eSLandon J. Fuller 
23419be09f3SLandon J. Fuller 	if (sc->data != NULL)
23519be09f3SLandon J. Fuller 		bhnd_nvram_data_release(sc->data);
23677cb4d3eSLandon J. Fuller 
237a7c43ebdSLandon J. Fuller 	if (sc->data_opts != NULL)
238a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_release(sc->data_opts);
23977cb4d3eSLandon J. Fuller 
24077cb4d3eSLandon J. Fuller 	BHND_NVSTORE_LOCK_DESTROY(sc);
24177cb4d3eSLandon J. Fuller 	bhnd_nv_free(sc);
24277cb4d3eSLandon J. Fuller }
24377cb4d3eSLandon J. Fuller 
24477cb4d3eSLandon J. Fuller /**
24519be09f3SLandon J. Fuller  * Parse all variables vended by our backing NVRAM data instance,
24619be09f3SLandon J. Fuller  * generating all path entries, alias entries, and variable indexes.
24719be09f3SLandon J. Fuller  *
24819be09f3SLandon J. Fuller  * @param	sc	The NVRAM store instance to be initialized with
24919be09f3SLandon J. Fuller  *			paths, aliases, and data parsed from its backing
25019be09f3SLandon J. Fuller  *			data.
25119be09f3SLandon J. Fuller  *
25219be09f3SLandon J. Fuller  * @retval 0		success
25319be09f3SLandon J. Fuller  * @retval non-zero	if an error occurs during parsing, a regular unix error
25419be09f3SLandon J. Fuller  *			code will be returned.
25519be09f3SLandon J. Fuller  */
25619be09f3SLandon J. Fuller static int
bhnd_nvstore_parse_data(struct bhnd_nvram_store * sc)25719be09f3SLandon J. Fuller bhnd_nvstore_parse_data(struct bhnd_nvram_store *sc)
25819be09f3SLandon J. Fuller {
25919be09f3SLandon J. Fuller 	const char	*name;
26019be09f3SLandon J. Fuller 	void		*cookiep;
26119be09f3SLandon J. Fuller 	int		 error;
26219be09f3SLandon J. Fuller 
26319be09f3SLandon J. Fuller 	/* Parse and register all device paths and path aliases. This enables
26419be09f3SLandon J. Fuller 	 * resolution of _forward_ references to device paths aliases when
26519be09f3SLandon J. Fuller 	 * scanning variable entries below */
26619be09f3SLandon J. Fuller 	if ((error = bhnd_nvstore_parse_path_entries(sc)))
26719be09f3SLandon J. Fuller 		return (error);
26819be09f3SLandon J. Fuller 
26919be09f3SLandon J. Fuller 	/* Calculate the per-path variable counts, and report dangling alias
27019be09f3SLandon J. Fuller 	 * references as an error. */
27119be09f3SLandon J. Fuller 	cookiep = NULL;
27219be09f3SLandon J. Fuller 	while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
27319be09f3SLandon J. Fuller 		bhnd_nvstore_path	*path;
27419be09f3SLandon J. Fuller 		bhnd_nvstore_name_info	 info;
27519be09f3SLandon J. Fuller 
27619be09f3SLandon J. Fuller 		/* Parse the name info */
27719be09f3SLandon J. Fuller 		error = bhnd_nvstore_parse_name_info(name,
27819be09f3SLandon J. Fuller 		    BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
27919be09f3SLandon J. Fuller 		if (error)
28019be09f3SLandon J. Fuller 			return (error);
28119be09f3SLandon J. Fuller 
28219be09f3SLandon J. Fuller 		switch (info.type) {
28319be09f3SLandon J. Fuller 		case BHND_NVSTORE_VAR:
28419be09f3SLandon J. Fuller 			/* Fetch referenced path */
28519be09f3SLandon J. Fuller 			path = bhnd_nvstore_var_get_path(sc, &info);
28619be09f3SLandon J. Fuller 			if (path == NULL) {
28719be09f3SLandon J. Fuller 				BHND_NV_LOG("variable '%s' has dangling "
28819be09f3SLandon J. Fuller 					    "path reference\n", name);
28919be09f3SLandon J. Fuller 				return (EFTYPE);
29019be09f3SLandon J. Fuller 			}
29119be09f3SLandon J. Fuller 
29219be09f3SLandon J. Fuller 			/* Increment path variable count */
29319be09f3SLandon J. Fuller 			if (path->num_vars == SIZE_MAX) {
29419be09f3SLandon J. Fuller 				BHND_NV_LOG("more than SIZE_MAX variables in "
29519be09f3SLandon J. Fuller 				    "path %s\n", path->path_str);
29619be09f3SLandon J. Fuller 				return (EFTYPE);
29719be09f3SLandon J. Fuller 			}
29819be09f3SLandon J. Fuller 			path->num_vars++;
29919be09f3SLandon J. Fuller 			break;
30019be09f3SLandon J. Fuller 
30119be09f3SLandon J. Fuller 		case BHND_NVSTORE_ALIAS_DECL:
30219be09f3SLandon J. Fuller 			/* Skip -- path alias already parsed and recorded */
30319be09f3SLandon J. Fuller 			break;
30419be09f3SLandon J. Fuller 		}
30519be09f3SLandon J. Fuller 	}
30619be09f3SLandon J. Fuller 
30719be09f3SLandon J. Fuller 	/* If the backing NVRAM data instance vends only a single root ("/")
30819be09f3SLandon J. Fuller 	 * path, we may be able to skip generating an index for the root
30919be09f3SLandon J. Fuller 	 * path */
31019be09f3SLandon J. Fuller 	if (sc->num_paths == 1) {
31119be09f3SLandon J. Fuller 		bhnd_nvstore_path *path;
31219be09f3SLandon J. Fuller 
31319be09f3SLandon J. Fuller 		/* If the backing instance provides its own name-based lookup
31419be09f3SLandon J. Fuller 		 * indexing, we can skip generating a duplicate here */
31519be09f3SLandon J. Fuller 		if (sc->data_caps & BHND_NVRAM_DATA_CAP_INDEXED)
31619be09f3SLandon J. Fuller 			return (0);
31719be09f3SLandon J. Fuller 
31819be09f3SLandon J. Fuller 		/* If the sole root path contains fewer variables than the
31919be09f3SLandon J. Fuller 		 * minimum indexing threshhold, we do not need to generate an
32019be09f3SLandon J. Fuller 		 * index */
32119be09f3SLandon J. Fuller 		path = bhnd_nvstore_get_root_path(sc);
32219be09f3SLandon J. Fuller 		if (path->num_vars < BHND_NV_IDX_VAR_THRESHOLD)
32319be09f3SLandon J. Fuller 			return (0);
32419be09f3SLandon J. Fuller 	}
32519be09f3SLandon J. Fuller 
32619be09f3SLandon J. Fuller 	/* Allocate per-path index instances */
32719be09f3SLandon J. Fuller 	for (size_t i = 0; i < nitems(sc->paths); i++) {
32819be09f3SLandon J. Fuller 		bhnd_nvstore_path	*path;
32919be09f3SLandon J. Fuller 
33019be09f3SLandon J. Fuller 		LIST_FOREACH(path, &sc->paths[i], np_link) {
33119be09f3SLandon J. Fuller 			path->index = bhnd_nvstore_index_new(path->num_vars);
33219be09f3SLandon J. Fuller 			if (path->index == NULL)
33319be09f3SLandon J. Fuller 				return (ENOMEM);
33419be09f3SLandon J. Fuller 		}
33519be09f3SLandon J. Fuller 	}
33619be09f3SLandon J. Fuller 
33719be09f3SLandon J. Fuller 	/* Populate per-path indexes */
33819be09f3SLandon J. Fuller 	cookiep = NULL;
33919be09f3SLandon J. Fuller 	while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
34019be09f3SLandon J. Fuller 		bhnd_nvstore_name_info	 info;
34119be09f3SLandon J. Fuller 		bhnd_nvstore_path	*path;
34219be09f3SLandon J. Fuller 
34319be09f3SLandon J. Fuller 		/* Parse the name info */
34419be09f3SLandon J. Fuller 		error = bhnd_nvstore_parse_name_info(name,
34519be09f3SLandon J. Fuller 		    BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
34619be09f3SLandon J. Fuller 		if (error)
34719be09f3SLandon J. Fuller 			return (error);
34819be09f3SLandon J. Fuller 
34919be09f3SLandon J. Fuller 		switch (info.type) {
35019be09f3SLandon J. Fuller 		case BHND_NVSTORE_VAR:
35119be09f3SLandon J. Fuller 			/* Fetch referenced path */
35219be09f3SLandon J. Fuller 			path = bhnd_nvstore_var_get_path(sc, &info);
35319be09f3SLandon J. Fuller 			BHND_NV_ASSERT(path != NULL,
35419be09f3SLandon J. Fuller 			    ("dangling path reference"));
35519be09f3SLandon J. Fuller 
35619be09f3SLandon J. Fuller 			/* Append to index */
35719be09f3SLandon J. Fuller 			error = bhnd_nvstore_index_append(sc, path->index,
35819be09f3SLandon J. Fuller 			    cookiep);
35919be09f3SLandon J. Fuller 			if (error)
36019be09f3SLandon J. Fuller 				return (error);
36119be09f3SLandon J. Fuller 			break;
36219be09f3SLandon J. Fuller 
36319be09f3SLandon J. Fuller 		case BHND_NVSTORE_ALIAS_DECL:
36419be09f3SLandon J. Fuller 			/* Skip */
36519be09f3SLandon J. Fuller 			break;
36619be09f3SLandon J. Fuller 		}
36719be09f3SLandon J. Fuller 	}
36819be09f3SLandon J. Fuller 
36919be09f3SLandon J. Fuller 	/* Prepare indexes for querying */
37019be09f3SLandon J. Fuller 	for (size_t i = 0; i < nitems(sc->paths); i++) {
37119be09f3SLandon J. Fuller 		bhnd_nvstore_path	*path;
37219be09f3SLandon J. Fuller 
37319be09f3SLandon J. Fuller 		LIST_FOREACH(path, &sc->paths[i], np_link) {
37419be09f3SLandon J. Fuller 			error = bhnd_nvstore_index_prepare(sc, path->index);
37519be09f3SLandon J. Fuller 			if (error)
37619be09f3SLandon J. Fuller 				return (error);
37719be09f3SLandon J. Fuller 		}
37819be09f3SLandon J. Fuller 	}
37919be09f3SLandon J. Fuller 
38019be09f3SLandon J. Fuller 	return (0);
38119be09f3SLandon J. Fuller }
38219be09f3SLandon J. Fuller 
38319be09f3SLandon J. Fuller /**
38419be09f3SLandon J. Fuller  * Parse and register path and path alias entries for all declarations found in
38519be09f3SLandon J. Fuller  * the NVRAM data backing @p nvram.
38619be09f3SLandon J. Fuller  *
38719be09f3SLandon J. Fuller  * @param sc		The NVRAM store instance.
38819be09f3SLandon J. Fuller  *
38919be09f3SLandon J. Fuller  * @retval 0		success
39019be09f3SLandon J. Fuller  * @retval non-zero	If parsing fails, a regular unix error code will be
39119be09f3SLandon J. Fuller  *			returned.
39219be09f3SLandon J. Fuller  */
39319be09f3SLandon J. Fuller static int
bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store * sc)39419be09f3SLandon J. Fuller bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc)
39519be09f3SLandon J. Fuller {
39619be09f3SLandon J. Fuller 	const char	*name;
39719be09f3SLandon J. Fuller 	void		*cookiep;
39819be09f3SLandon J. Fuller 	int		 error;
39919be09f3SLandon J. Fuller 
40019be09f3SLandon J. Fuller 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
40119be09f3SLandon J. Fuller 
40219be09f3SLandon J. Fuller 	/* Skip path registration if the data source does not support device
40319be09f3SLandon J. Fuller 	 * paths. */
40419be09f3SLandon J. Fuller 	if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) {
40519be09f3SLandon J. Fuller 		BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path"));
40619be09f3SLandon J. Fuller 		return (0);
40719be09f3SLandon J. Fuller 	}
40819be09f3SLandon J. Fuller 
40919be09f3SLandon J. Fuller 	/* Otherwise, parse and register all paths and path aliases */
41019be09f3SLandon J. Fuller 	cookiep = NULL;
41119be09f3SLandon J. Fuller 	while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
41219be09f3SLandon J. Fuller 		bhnd_nvstore_name_info info;
41319be09f3SLandon J. Fuller 
41419be09f3SLandon J. Fuller 		/* Parse the name info */
41519be09f3SLandon J. Fuller 		error = bhnd_nvstore_parse_name_info(name,
41619be09f3SLandon J. Fuller 		    BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
41719be09f3SLandon J. Fuller 		if (error)
41819be09f3SLandon J. Fuller 			return (error);
41919be09f3SLandon J. Fuller 
42019be09f3SLandon J. Fuller 		/* Register the path */
42119be09f3SLandon J. Fuller 		error = bhnd_nvstore_var_register_path(sc, &info, cookiep);
42219be09f3SLandon J. Fuller 		if (error) {
42319be09f3SLandon J. Fuller 			BHND_NV_LOG("failed to register path for %s: %d\n",
42419be09f3SLandon J. Fuller 			    name, error);
42519be09f3SLandon J. Fuller 			return (error);
42619be09f3SLandon J. Fuller 		}
42719be09f3SLandon J. Fuller 	}
42819be09f3SLandon J. Fuller 
42919be09f3SLandon J. Fuller 	return (0);
43019be09f3SLandon J. Fuller }
431a7c43ebdSLandon J. Fuller 
432a7c43ebdSLandon J. Fuller /**
433a7c43ebdSLandon J. Fuller  * Merge exported per-path variables (uncommitted, committed, or both) into
434a7c43ebdSLandon J. Fuller  * the empty @p merged property list.
435a7c43ebdSLandon J. Fuller  *
436a7c43ebdSLandon J. Fuller  * @param	sc	The NVRAM store instance.
437a7c43ebdSLandon J. Fuller  * @param	path	The NVRAM path to be exported.
438a7c43ebdSLandon J. Fuller  * @param	merged	The property list to populate with the merged results.
439a7c43ebdSLandon J. Fuller  * @param	flags	Export flags. See BHND_NVSTORE_EXPORT_*.
440a7c43ebdSLandon J. Fuller  *
441a7c43ebdSLandon J. Fuller  * @retval 0		success
442a7c43ebdSLandon J. Fuller  * @retval ENOMEM	If allocation fails.
443a7c43ebdSLandon J. Fuller  * @retval non-zero	If merging the variables defined in @p path otherwise
444a7c43ebdSLandon J. Fuller  *			fails, a regular unix error code will be returned.
445a7c43ebdSLandon J. Fuller  */
446a7c43ebdSLandon J. Fuller static int
bhnd_nvstore_export_merge(struct bhnd_nvram_store * sc,bhnd_nvstore_path * path,bhnd_nvram_plist * merged,uint32_t flags)447a7c43ebdSLandon J. Fuller bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc,
448a7c43ebdSLandon J. Fuller     bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags)
449a7c43ebdSLandon J. Fuller {
450a7c43ebdSLandon J. Fuller 	void	*cookiep, *idxp;
451a7c43ebdSLandon J. Fuller 	int	 error;
452a7c43ebdSLandon J. Fuller 
453a7c43ebdSLandon J. Fuller 	/* Populate merged list with all pending variables */
454a7c43ebdSLandon J. Fuller 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
455a7c43ebdSLandon J. Fuller 		bhnd_nvram_prop *prop;
456a7c43ebdSLandon J. Fuller 
457a7c43ebdSLandon J. Fuller 		prop = NULL;
458a7c43ebdSLandon J. Fuller 		while ((prop = bhnd_nvram_plist_next(path->pending, prop))) {
459a7c43ebdSLandon J. Fuller 			/* Skip variables marked for deletion */
460a7c43ebdSLandon J. Fuller 			if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) {
461a7c43ebdSLandon J. Fuller 				if (bhnd_nvram_prop_is_null(prop))
462a7c43ebdSLandon J. Fuller 					continue;
463a7c43ebdSLandon J. Fuller 			}
464a7c43ebdSLandon J. Fuller 
465a7c43ebdSLandon J. Fuller 			/* Append to merged list */
466a7c43ebdSLandon J. Fuller 			error = bhnd_nvram_plist_append(merged, prop);
467a7c43ebdSLandon J. Fuller 			if (error)
468a7c43ebdSLandon J. Fuller 				return (error);
469a7c43ebdSLandon J. Fuller 		}
470a7c43ebdSLandon J. Fuller 	}
471a7c43ebdSLandon J. Fuller 
472a7c43ebdSLandon J. Fuller 	/* Skip merging committed variables? */
473a7c43ebdSLandon J. Fuller 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED))
474a7c43ebdSLandon J. Fuller 		return (0);
475a7c43ebdSLandon J. Fuller 
476a7c43ebdSLandon J. Fuller 	/* Merge in the committed NVRAM variables */
477a7c43ebdSLandon J. Fuller 	idxp = NULL;
478a7c43ebdSLandon J. Fuller 	while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) {
479a7c43ebdSLandon J. Fuller 		const char	*name;
480a7c43ebdSLandon J. Fuller 		bhnd_nvram_val	*val;
481a7c43ebdSLandon J. Fuller 
482a7c43ebdSLandon J. Fuller 		/* Fetch the variable name */
483a7c43ebdSLandon J. Fuller 		name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
484a7c43ebdSLandon J. Fuller 
485a7c43ebdSLandon J. Fuller 		/* Trim device path prefix */
486a7c43ebdSLandon J. Fuller 		if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)
487a7c43ebdSLandon J. Fuller 			name = bhnd_nvram_trim_path_name(name);
488a7c43ebdSLandon J. Fuller 
489a7c43ebdSLandon J. Fuller 		/* Skip if already defined in pending updates */
490a7c43ebdSLandon J. Fuller 		if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
491a7c43ebdSLandon J. Fuller 			if (bhnd_nvram_plist_contains(path->pending, name))
492a7c43ebdSLandon J. Fuller 				continue;
493a7c43ebdSLandon J. Fuller 		}
494a7c43ebdSLandon J. Fuller 
495a7c43ebdSLandon J. Fuller 		/* Skip if higher precedence value was already defined. This
496a7c43ebdSLandon J. Fuller 		 * may occur if the underlying data store contains duplicate
497a7c43ebdSLandon J. Fuller 		 * keys; iteration will always return the definition with
498a7c43ebdSLandon J. Fuller 		 * the highest precedence first */
499a7c43ebdSLandon J. Fuller 		if (bhnd_nvram_plist_contains(merged, name))
500a7c43ebdSLandon J. Fuller 			continue;
501a7c43ebdSLandon J. Fuller 
502a7c43ebdSLandon J. Fuller 		/* Fetch the variable's value representation */
503a7c43ebdSLandon J. Fuller 		if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val)))
504a7c43ebdSLandon J. Fuller 			return (error);
505a7c43ebdSLandon J. Fuller 
506a7c43ebdSLandon J. Fuller 		/* Add to path variable list */
507a7c43ebdSLandon J. Fuller 		error = bhnd_nvram_plist_append_val(merged, name, val);
508a7c43ebdSLandon J. Fuller 		bhnd_nvram_val_release(val);
509a7c43ebdSLandon J. Fuller 		if (error)
510a7c43ebdSLandon J. Fuller 			return (error);
511a7c43ebdSLandon J. Fuller 	}
512a7c43ebdSLandon J. Fuller 
513a7c43ebdSLandon J. Fuller 	return (0);
514a7c43ebdSLandon J. Fuller }
515a7c43ebdSLandon J. Fuller 
516a7c43ebdSLandon J. Fuller /**
517a7c43ebdSLandon J. Fuller  * Find a free alias value for @p path, and append the devpathXX alias
518a7c43ebdSLandon J. Fuller  * declaration to @p plist.
519a7c43ebdSLandon J. Fuller  *
520a7c43ebdSLandon J. Fuller  * @param	sc		The NVRAM store instance.
521a7c43ebdSLandon J. Fuller  * @param	path		The NVRAM path for which a devpath alias
522a7c43ebdSLandon J. Fuller  *				variable should be produced.
523a7c43ebdSLandon J. Fuller  * @param	devpath		The devpathXX path value for @p path.
524a7c43ebdSLandon J. Fuller  * @param	plist		The property list to which @p path's devpath
525a7c43ebdSLandon J. Fuller  *				variable will be appended.
526a7c43ebdSLandon J. Fuller  * @param[out]	alias_val	On success, will be set to the alias value
527a7c43ebdSLandon J. Fuller  *				allocated for @p path.
528a7c43ebdSLandon J. Fuller  *
529a7c43ebdSLandon J. Fuller  * @retval 0		success
530a7c43ebdSLandon J. Fuller  * @retval ENOMEM	If allocation fails.
531a7c43ebdSLandon J. Fuller  * @retval non-zero	If merging the variables defined in @p path otherwise
532a7c43ebdSLandon J. Fuller  *			fails, a regular unix error code will be returned.
533a7c43ebdSLandon J. Fuller  */
534a7c43ebdSLandon J. Fuller 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)535a7c43ebdSLandon J. Fuller bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc,
536a7c43ebdSLandon J. Fuller     bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist,
537a7c43ebdSLandon J. Fuller     u_long *alias_val)
538a7c43ebdSLandon J. Fuller {
539a7c43ebdSLandon J. Fuller 	bhnd_nvstore_alias	*alias;
540a7c43ebdSLandon J. Fuller 	char			*pathvar;
541a7c43ebdSLandon J. Fuller 	int			 error;
542a7c43ebdSLandon J. Fuller 
543a7c43ebdSLandon J. Fuller 	*alias_val = 0;
544a7c43ebdSLandon J. Fuller 
545a7c43ebdSLandon J. Fuller 	/* Prefer alias value already reserved for this path. */
546a7c43ebdSLandon J. Fuller 	alias = bhnd_nvstore_find_alias(sc, path->path_str);
547a7c43ebdSLandon J. Fuller 	if (alias != NULL) {
548a7c43ebdSLandon J. Fuller 		*alias_val = alias->alias;
549a7c43ebdSLandon J. Fuller 
550a7c43ebdSLandon J. Fuller 		/* Allocate devpathXX variable name */
551a7c43ebdSLandon J. Fuller 		bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
552a7c43ebdSLandon J. Fuller 		if (pathvar == NULL)
553a7c43ebdSLandon J. Fuller 			return (ENOMEM);
554a7c43ebdSLandon J. Fuller 
555a7c43ebdSLandon J. Fuller 		/* Append alias variable to property list */
556a7c43ebdSLandon J. Fuller 		error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
557a7c43ebdSLandon J. Fuller 
558a7c43ebdSLandon J. Fuller 		BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use",
559a7c43ebdSLandon J. Fuller 		   * alias_val, path->path_str));
560a7c43ebdSLandon J. Fuller 
561a7c43ebdSLandon J. Fuller 		bhnd_nv_free(pathvar);
562a7c43ebdSLandon J. Fuller 		return (error);
563a7c43ebdSLandon J. Fuller 	}
564a7c43ebdSLandon J. Fuller 
565a7c43ebdSLandon J. Fuller 	/* Find the next free devpathXX alias entry */
566a7c43ebdSLandon J. Fuller 	while (1) {
567a7c43ebdSLandon J. Fuller 		/* Skip existing reserved alias values */
568a7c43ebdSLandon J. Fuller 		while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) {
569a7c43ebdSLandon J. Fuller 			if (*alias_val == ULONG_MAX)
570a7c43ebdSLandon J. Fuller 				return (ENOMEM);
571a7c43ebdSLandon J. Fuller 
572a7c43ebdSLandon J. Fuller 			(*alias_val)++;
573a7c43ebdSLandon J. Fuller 		}
574a7c43ebdSLandon J. Fuller 
575a7c43ebdSLandon J. Fuller 		/* Allocate devpathXX variable name */
576a7c43ebdSLandon J. Fuller 		bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
577a7c43ebdSLandon J. Fuller 		if (pathvar == NULL)
578a7c43ebdSLandon J. Fuller 			return (ENOMEM);
579a7c43ebdSLandon J. Fuller 
580a7c43ebdSLandon J. Fuller 		/* If not in-use, we can terminate the search */
581a7c43ebdSLandon J. Fuller 		if (!bhnd_nvram_plist_contains(plist, pathvar))
582a7c43ebdSLandon J. Fuller 			break;
583a7c43ebdSLandon J. Fuller 
584a7c43ebdSLandon J. Fuller 		/* Keep searching */
585a7c43ebdSLandon J. Fuller 		bhnd_nv_free(pathvar);
586a7c43ebdSLandon J. Fuller 
587a7c43ebdSLandon J. Fuller 		if (*alias_val == ULONG_MAX)
588a7c43ebdSLandon J. Fuller 			return (ENOMEM);
589a7c43ebdSLandon J. Fuller 
590a7c43ebdSLandon J. Fuller 		(*alias_val)++;
591a7c43ebdSLandon J. Fuller 	}
592a7c43ebdSLandon J. Fuller 
593a7c43ebdSLandon J. Fuller 	/* Append alias variable to property list */
594a7c43ebdSLandon J. Fuller 	error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
595a7c43ebdSLandon J. Fuller 
596a7c43ebdSLandon J. Fuller 	bhnd_nv_free(pathvar);
597a7c43ebdSLandon J. Fuller 	return (error);
598a7c43ebdSLandon J. Fuller }
599a7c43ebdSLandon J. Fuller 
600a7c43ebdSLandon J. Fuller /**
601a7c43ebdSLandon J. Fuller  * Export a single @p child path's properties, appending the result to @p plist.
602a7c43ebdSLandon J. Fuller  *
603a7c43ebdSLandon J. Fuller  * @param	sc		The NVRAM store instance.
604a7c43ebdSLandon J. Fuller  * @param	top		The root NVRAM path being exported.
605a7c43ebdSLandon J. Fuller  * @param	child		The NVRAM path to be exported.
606a7c43ebdSLandon J. Fuller  * @param	plist		The property list to which @p child's exported
607a7c43ebdSLandon J. Fuller  *				properties should be appended.
608a7c43ebdSLandon J. Fuller  * @param	flags		Export flags. See BHND_NVSTORE_EXPORT_*.
609a7c43ebdSLandon J. Fuller  *
610a7c43ebdSLandon J. Fuller  * @retval 0		success
611a7c43ebdSLandon J. Fuller  * @retval ENOMEM	If allocation fails.
612a7c43ebdSLandon J. Fuller  * @retval non-zero	If merging the variables defined in @p path otherwise
613a7c43ebdSLandon J. Fuller  *			fails, a regular unix error code will be returned.
614a7c43ebdSLandon J. Fuller  */
615a7c43ebdSLandon J. Fuller 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)616a7c43ebdSLandon J. Fuller bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc,
617a7c43ebdSLandon J. Fuller     bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist,
618a7c43ebdSLandon J. Fuller     uint32_t flags)
619a7c43ebdSLandon J. Fuller {
620a7c43ebdSLandon J. Fuller 	bhnd_nvram_plist	*path_vars;
621a7c43ebdSLandon J. Fuller 	bhnd_nvram_prop		*prop;
622a7c43ebdSLandon J. Fuller 	const char		*relpath;
623a7c43ebdSLandon J. Fuller 	char			*prefix, *namebuf;
624a7c43ebdSLandon J. Fuller 	size_t			 prefix_len, relpath_len;
625*87a43286SWarner Losh 	size_t			 namebuf_size;
626a7c43ebdSLandon J. Fuller 	bool			 emit_compact_devpath;
627a7c43ebdSLandon J. Fuller 	int			 error;
628a7c43ebdSLandon J. Fuller 
629a7c43ebdSLandon J. Fuller 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
630a7c43ebdSLandon J. Fuller 
631a7c43ebdSLandon J. Fuller 	prefix = NULL;
632a7c43ebdSLandon J. Fuller 	path_vars = NULL;
633a7c43ebdSLandon J. Fuller 	namebuf = NULL;
634a7c43ebdSLandon J. Fuller 
635a7c43ebdSLandon J. Fuller 	/* Determine the path relative to the top-level path */
636a7c43ebdSLandon J. Fuller 	relpath = bhnd_nvstore_parse_relpath(top->path_str, child->path_str);
637a7c43ebdSLandon J. Fuller 	if (relpath == NULL) {
638a7c43ebdSLandon J. Fuller 		/* Skip -- not a child of the root path */
639a7c43ebdSLandon J. Fuller 		return (0);
640a7c43ebdSLandon J. Fuller 	}
641a7c43ebdSLandon J. Fuller 	relpath_len = strlen(relpath);
642a7c43ebdSLandon J. Fuller 
643a7c43ebdSLandon J. Fuller 	/* Skip sub-path if export of children was not requested,  */
644a7c43ebdSLandon J. Fuller 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0)
645a7c43ebdSLandon J. Fuller 		return (0);
646a7c43ebdSLandon J. Fuller 
647a7c43ebdSLandon J. Fuller 	/* Collect all variables to be included in the export */
648a7c43ebdSLandon J. Fuller 	if ((path_vars = bhnd_nvram_plist_new()) == NULL)
649a7c43ebdSLandon J. Fuller 		return (ENOMEM);
650a7c43ebdSLandon J. Fuller 
651a7c43ebdSLandon J. Fuller 	if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) {
652a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_release(path_vars);
653a7c43ebdSLandon J. Fuller 		return (error);
654a7c43ebdSLandon J. Fuller 	}
655a7c43ebdSLandon J. Fuller 
656a7c43ebdSLandon J. Fuller 	/* Skip if no children are to be exported */
657a7c43ebdSLandon J. Fuller 	if (bhnd_nvram_plist_count(path_vars) == 0) {
658a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_release(path_vars);
659a7c43ebdSLandon J. Fuller 		return (0);
660a7c43ebdSLandon J. Fuller 	}
661a7c43ebdSLandon J. Fuller 
662a7c43ebdSLandon J. Fuller 	/* Determine appropriate device path encoding */
663a7c43ebdSLandon J. Fuller 	emit_compact_devpath = false;
664a7c43ebdSLandon J. Fuller 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) {
665a7c43ebdSLandon J. Fuller 		/* Re-encode as compact (if non-empty path) */
666a7c43ebdSLandon J. Fuller 		if (relpath_len > 0)
667a7c43ebdSLandon J. Fuller 			emit_compact_devpath = true;
668a7c43ebdSLandon J. Fuller 	} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) {
669a7c43ebdSLandon J. Fuller 		/* Re-encode with fully expanded device path */
670a7c43ebdSLandon J. Fuller 		emit_compact_devpath = false;
671a7c43ebdSLandon J. Fuller 	} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
672a7c43ebdSLandon J. Fuller 		/* Preserve existing encoding of this path */
673a7c43ebdSLandon J. Fuller 		if (bhnd_nvstore_find_alias(sc, child->path_str) != NULL)
674a7c43ebdSLandon J. Fuller 			emit_compact_devpath = true;
675a7c43ebdSLandon J. Fuller 	} else {
676a7c43ebdSLandon J. Fuller 		BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags);
677a7c43ebdSLandon J. Fuller 		error = EINVAL;
678a7c43ebdSLandon J. Fuller 		goto finished;
679a7c43ebdSLandon J. Fuller 	}
680a7c43ebdSLandon J. Fuller 
681a7c43ebdSLandon J. Fuller 	/* Allocate variable device path prefix to use for all property names,
682a7c43ebdSLandon J. Fuller 	 * and if using compact encoding, emit the devpathXX= variable */
683a7c43ebdSLandon J. Fuller 	prefix = NULL;
684a7c43ebdSLandon J. Fuller 	prefix_len = 0;
685a7c43ebdSLandon J. Fuller 	if (emit_compact_devpath) {
686a7c43ebdSLandon J. Fuller 		u_long	alias_val;
687a7c43ebdSLandon J. Fuller 		int	len;
688a7c43ebdSLandon J. Fuller 
689a7c43ebdSLandon J. Fuller 		/* Reserve an alias value and append the devpathXX= variable to
690a7c43ebdSLandon J. Fuller 		 * the property list */
691a7c43ebdSLandon J. Fuller 		error = bhnd_nvstore_export_devpath_alias(sc, child, relpath,
692a7c43ebdSLandon J. Fuller 		    plist, &alias_val);
693a7c43ebdSLandon J. Fuller 		if (error)
694a7c43ebdSLandon J. Fuller 			goto finished;
695a7c43ebdSLandon J. Fuller 
696a7c43ebdSLandon J. Fuller 		/* Allocate variable name prefix */
697a7c43ebdSLandon J. Fuller 		len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val);
698a7c43ebdSLandon J. Fuller 		if (prefix == NULL) {
699a7c43ebdSLandon J. Fuller 			error = ENOMEM;
700a7c43ebdSLandon J. Fuller 			goto finished;
701a7c43ebdSLandon J. Fuller 		}
702a7c43ebdSLandon J. Fuller 
703a7c43ebdSLandon J. Fuller 		prefix_len = len;
704a7c43ebdSLandon J. Fuller 	} else if (relpath_len > 0) {
705a7c43ebdSLandon J. Fuller 		int len;
706a7c43ebdSLandon J. Fuller 
707a7c43ebdSLandon J. Fuller 		/* Allocate the variable name prefix, appending '/' to the
708a7c43ebdSLandon J. Fuller 		 * relative path */
709a7c43ebdSLandon J. Fuller 		len = bhnd_nv_asprintf(&prefix, "%s/", relpath);
710a7c43ebdSLandon J. Fuller 		if (prefix == NULL) {
711a7c43ebdSLandon J. Fuller 			error = ENOMEM;
712a7c43ebdSLandon J. Fuller 			goto finished;
713a7c43ebdSLandon J. Fuller 		}
714a7c43ebdSLandon J. Fuller 
715a7c43ebdSLandon J. Fuller 		prefix_len = len;
716a7c43ebdSLandon J. Fuller 	}
717a7c43ebdSLandon J. Fuller 
718a7c43ebdSLandon J. Fuller 	/* If prefixing of variable names is required, allocate a name
719a7c43ebdSLandon J. Fuller 	 * formatting buffer */
720a7c43ebdSLandon J. Fuller 	namebuf_size = 0;
721a7c43ebdSLandon J. Fuller 	if (prefix != NULL) {
722a7c43ebdSLandon J. Fuller 		size_t	maxlen;
723a7c43ebdSLandon J. Fuller 
724a7c43ebdSLandon J. Fuller 		/* Find the maximum name length */
725a7c43ebdSLandon J. Fuller 		maxlen = 0;
726a7c43ebdSLandon J. Fuller 		prop = NULL;
727a7c43ebdSLandon J. Fuller 		while ((prop = bhnd_nvram_plist_next(path_vars, prop))) {
728a7c43ebdSLandon J. Fuller 			const char *name;
729a7c43ebdSLandon J. Fuller 
730a7c43ebdSLandon J. Fuller 			name = bhnd_nvram_prop_name(prop);
731a7c43ebdSLandon J. Fuller 			maxlen = bhnd_nv_ummax(strlen(name), maxlen);
732a7c43ebdSLandon J. Fuller 		}
733a7c43ebdSLandon J. Fuller 
734a7c43ebdSLandon J. Fuller 		/* Allocate name buffer (path-prefix + name + '\0') */
735a7c43ebdSLandon J. Fuller 		namebuf_size = prefix_len + maxlen + 1;
736a7c43ebdSLandon J. Fuller 		namebuf = bhnd_nv_malloc(namebuf_size);
737a7c43ebdSLandon J. Fuller 		if (namebuf == NULL) {
738a7c43ebdSLandon J. Fuller 			error = ENOMEM;
739a7c43ebdSLandon J. Fuller 			goto finished;
740a7c43ebdSLandon J. Fuller 		}
741a7c43ebdSLandon J. Fuller 	}
742a7c43ebdSLandon J. Fuller 
743a7c43ebdSLandon J. Fuller 	/* Append all path variables to the export plist, prepending the
744a7c43ebdSLandon J. Fuller 	 * device-path prefix to the variable names, if required */
745a7c43ebdSLandon J. Fuller 	prop = NULL;
746a7c43ebdSLandon J. Fuller 	while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) {
747a7c43ebdSLandon J. Fuller 		const char *name;
748a7c43ebdSLandon J. Fuller 
749a7c43ebdSLandon J. Fuller 		/* Prepend device prefix to the variable name */
750a7c43ebdSLandon J. Fuller 		name = bhnd_nvram_prop_name(prop);
751a7c43ebdSLandon J. Fuller 		if (prefix != NULL) {
752a7c43ebdSLandon J. Fuller 			int len;
753a7c43ebdSLandon J. Fuller 
754a7c43ebdSLandon J. Fuller 			/*
755a7c43ebdSLandon J. Fuller 			 * Write prefixed variable name to our name buffer.
756a7c43ebdSLandon J. Fuller 			 *
757a7c43ebdSLandon J. Fuller 			 * We precalcuate the size when scanning all names
758a7c43ebdSLandon J. Fuller 			 * above, so this should always succeed.
759a7c43ebdSLandon J. Fuller 			 */
760a7c43ebdSLandon J. Fuller 			len = snprintf(namebuf, namebuf_size, "%s%s", prefix,
761a7c43ebdSLandon J. Fuller 			    name);
762a7c43ebdSLandon J. Fuller 			if (len < 0 || (size_t)len >= namebuf_size)
763a7c43ebdSLandon J. Fuller 				BHND_NV_PANIC("invalid max_name_len");
764a7c43ebdSLandon J. Fuller 
765a7c43ebdSLandon J. Fuller 			name = namebuf;
766a7c43ebdSLandon J. Fuller 		}
767a7c43ebdSLandon J. Fuller 
768a7c43ebdSLandon J. Fuller 		/* Add property to export plist */
769a7c43ebdSLandon J. Fuller 		error = bhnd_nvram_plist_append_val(plist, name,
770a7c43ebdSLandon J. Fuller 		    bhnd_nvram_prop_val(prop));
771a7c43ebdSLandon J. Fuller 		if (error)
772a7c43ebdSLandon J. Fuller 			goto finished;
773a7c43ebdSLandon J. Fuller 	}
774a7c43ebdSLandon J. Fuller 
775a7c43ebdSLandon J. Fuller 	/* Success */
776a7c43ebdSLandon J. Fuller 	error = 0;
777a7c43ebdSLandon J. Fuller 
778a7c43ebdSLandon J. Fuller finished:
779a7c43ebdSLandon J. Fuller 	if (prefix != NULL)
780a7c43ebdSLandon J. Fuller 		bhnd_nv_free(prefix);
781a7c43ebdSLandon J. Fuller 
782a7c43ebdSLandon J. Fuller 	if (namebuf != NULL)
783a7c43ebdSLandon J. Fuller 		bhnd_nv_free(namebuf);
784a7c43ebdSLandon J. Fuller 
785a7c43ebdSLandon J. Fuller 	if (path_vars != NULL)
786a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_release(path_vars);
787a7c43ebdSLandon J. Fuller 
788a7c43ebdSLandon J. Fuller 	return (error);
789a7c43ebdSLandon J. Fuller }
790a7c43ebdSLandon J. Fuller 
791a7c43ebdSLandon J. Fuller /**
792a7c43ebdSLandon J. Fuller  * Export a flat, ordered NVRAM property list representation of all NVRAM
793a7c43ebdSLandon J. Fuller  * properties at @p path.
794a7c43ebdSLandon J. Fuller  *
795a7c43ebdSLandon J. Fuller  * @param	sc	The NVRAM store instance.
796a7c43ebdSLandon J. Fuller  * @param	path	The NVRAM path to export, or NULL to select the root
797a7c43ebdSLandon J. Fuller  *			path.
798a7c43ebdSLandon J. Fuller  * @param[out]	cls	On success, will be set to the backing data class
799a7c43ebdSLandon J. Fuller  *			of @p sc. If the data class is are not desired,
800a7c43ebdSLandon J. Fuller  *			a NULL pointer may be provided.
801a7c43ebdSLandon J. Fuller  * @param[out]	props	On success, will be set to a caller-owned property
802a7c43ebdSLandon J. Fuller  *			list containing the exported properties. The caller is
803a7c43ebdSLandon J. Fuller  *			responsible for releasing this value via
804a7c43ebdSLandon J. Fuller  *			bhnd_nvram_plist_release().
805a7c43ebdSLandon J. Fuller  * @param[out]	options	On success, will be set to a caller-owned property
806a7c43ebdSLandon J. Fuller  *			list containing the current NVRAM serialization options
807a7c43ebdSLandon J. Fuller  *			for @p sc. The caller is responsible for releasing this
808a7c43ebdSLandon J. Fuller  *			value via bhnd_nvram_plist_release().
809a7c43ebdSLandon J. Fuller  * @param	flags	Export flags. See BHND_NVSTORE_EXPORT_*.
810a7c43ebdSLandon J. Fuller  *
811a7c43ebdSLandon J. Fuller  * @retval 0		success
812a7c43ebdSLandon J. Fuller  * @retval EINVAL	If @p flags is invalid.
813a7c43ebdSLandon J. Fuller  * @retval ENOENT	The requested path was not found.
814a7c43ebdSLandon J. Fuller  * @retval ENOMEM	If allocation fails.
815a7c43ebdSLandon J. Fuller  * @retval non-zero	If export of  @p path otherwise fails, a regular unix
816a7c43ebdSLandon J. Fuller  *			error code will be returned.
817a7c43ebdSLandon J. Fuller  */
818a7c43ebdSLandon J. Fuller 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)819a7c43ebdSLandon J. Fuller bhnd_nvram_store_export(struct bhnd_nvram_store *sc, const char *path,
820a7c43ebdSLandon J. Fuller     bhnd_nvram_data_class **cls, bhnd_nvram_plist **props,
821a7c43ebdSLandon J. Fuller     bhnd_nvram_plist **options, uint32_t flags)
822a7c43ebdSLandon J. Fuller {
823a7c43ebdSLandon J. Fuller 	bhnd_nvram_plist	*unordered;
824a7c43ebdSLandon J. Fuller 	bhnd_nvstore_path	*top;
825a7c43ebdSLandon J. Fuller 	bhnd_nvram_prop		*prop;
826a7c43ebdSLandon J. Fuller 	const char		*name;
827a7c43ebdSLandon J. Fuller 	void			*cookiep;
828a7c43ebdSLandon J. Fuller 	size_t			 num_dpath_flags;
829a7c43ebdSLandon J. Fuller 	int			 error;
830a7c43ebdSLandon J. Fuller 
831a7c43ebdSLandon J. Fuller 	*props = NULL;
832a7c43ebdSLandon J. Fuller 	unordered = NULL;
833a7c43ebdSLandon J. Fuller 	num_dpath_flags = 0;
834a7c43ebdSLandon J. Fuller 	if (options != NULL)
835a7c43ebdSLandon J. Fuller 		*options = NULL;
836a7c43ebdSLandon J. Fuller 
837a7c43ebdSLandon J. Fuller 	/* Default to exporting root path */
838a7c43ebdSLandon J. Fuller 	if (path == NULL)
839a7c43ebdSLandon J. Fuller 		path = BHND_NVSTORE_ROOT_PATH;
840a7c43ebdSLandon J. Fuller 
841a7c43ebdSLandon J. Fuller 	/* Default to exporting all properties */
842a7c43ebdSLandon J. Fuller 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) &&
843a7c43ebdSLandon J. Fuller 	    !BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED))
844a7c43ebdSLandon J. Fuller 	{
845a7c43ebdSLandon J. Fuller 		flags |= BHND_NVSTORE_EXPORT_ALL_VARS;
846a7c43ebdSLandon J. Fuller 	}
847a7c43ebdSLandon J. Fuller 
848a7c43ebdSLandon J. Fuller 	/* Default to preserving the current device path encoding */
849a7c43ebdSLandon J. Fuller 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS) &&
850a7c43ebdSLandon J. Fuller 	    !BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
851a7c43ebdSLandon J. Fuller 	{
852a7c43ebdSLandon J. Fuller 		flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS;
853a7c43ebdSLandon J. Fuller 	}
854a7c43ebdSLandon J. Fuller 
855a7c43ebdSLandon J. Fuller 	/* Exactly one device path encoding flag must be set */
856a7c43ebdSLandon J. Fuller 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS))
857a7c43ebdSLandon J. Fuller 		num_dpath_flags++;
858a7c43ebdSLandon J. Fuller 
859a7c43ebdSLandon J. Fuller 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
860a7c43ebdSLandon J. Fuller 		num_dpath_flags++;
861a7c43ebdSLandon J. Fuller 
862a7c43ebdSLandon J. Fuller 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS))
863a7c43ebdSLandon J. Fuller 		num_dpath_flags++;
864a7c43ebdSLandon J. Fuller 
865a7c43ebdSLandon J. Fuller 	if (num_dpath_flags != 1)
866a7c43ebdSLandon J. Fuller 		return (EINVAL);
867a7c43ebdSLandon J. Fuller 
868a7c43ebdSLandon J. Fuller 	/* If EXPORT_DELETED is set, EXPORT_UNCOMMITTED must be set too */
869a7c43ebdSLandon J. Fuller 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED) &&
870a7c43ebdSLandon J. Fuller 	    !BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED))
871a7c43ebdSLandon J. Fuller 	{
872a7c43ebdSLandon J. Fuller 		return (EINVAL);
873a7c43ebdSLandon J. Fuller 	}
874a7c43ebdSLandon J. Fuller 
875a7c43ebdSLandon J. Fuller 	/* Lock internal state before querying paths/properties */
876a7c43ebdSLandon J. Fuller 	BHND_NVSTORE_LOCK(sc);
877a7c43ebdSLandon J. Fuller 
878a7c43ebdSLandon J. Fuller 	/* Fetch referenced path */
879a7c43ebdSLandon J. Fuller 	top = bhnd_nvstore_get_path(sc, path, strlen(path));
880a7c43ebdSLandon J. Fuller 	if (top == NULL) {
881a7c43ebdSLandon J. Fuller 		error = ENOENT;
882a7c43ebdSLandon J. Fuller 		goto failed;
883a7c43ebdSLandon J. Fuller 	}
884a7c43ebdSLandon J. Fuller 
885a7c43ebdSLandon J. Fuller 	/* Allocate new, empty property list */
886a7c43ebdSLandon J. Fuller 	if ((unordered = bhnd_nvram_plist_new()) == NULL) {
887a7c43ebdSLandon J. Fuller 		error = ENOMEM;
888a7c43ebdSLandon J. Fuller 		goto failed;
889a7c43ebdSLandon J. Fuller 	}
890a7c43ebdSLandon J. Fuller 
891a7c43ebdSLandon J. Fuller 	/* Export the top-level path first */
892a7c43ebdSLandon J. Fuller 	error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags);
893a7c43ebdSLandon J. Fuller 	if (error)
894a7c43ebdSLandon J. Fuller 		goto failed;
895a7c43ebdSLandon J. Fuller 
896a7c43ebdSLandon J. Fuller 	/* Attempt to export any children of the root path */
897a7c43ebdSLandon J. Fuller 	for (size_t i = 0; i < nitems(sc->paths); i++) {
898a7c43ebdSLandon J. Fuller 		bhnd_nvstore_path *child;
899a7c43ebdSLandon J. Fuller 
900a7c43ebdSLandon J. Fuller 		LIST_FOREACH(child, &sc->paths[i], np_link) {
901a7c43ebdSLandon J. Fuller 			/* Top-level path was already exported */
902a7c43ebdSLandon J. Fuller 			if (child == top)
903a7c43ebdSLandon J. Fuller 				continue;
904a7c43ebdSLandon J. Fuller 
905a7c43ebdSLandon J. Fuller 			error = bhnd_nvram_store_export_child(sc, top,
906a7c43ebdSLandon J. Fuller 			    child, unordered, flags);
907a7c43ebdSLandon J. Fuller 			if (error)
908a7c43ebdSLandon J. Fuller 				goto failed;
909a7c43ebdSLandon J. Fuller 		}
910a7c43ebdSLandon J. Fuller 	}
911a7c43ebdSLandon J. Fuller 
912a7c43ebdSLandon J. Fuller 	/* If requested, provide the current class and serialization options */
913a7c43ebdSLandon J. Fuller 	if (cls != NULL)
914a7c43ebdSLandon J. Fuller 		*cls = bhnd_nvram_data_get_class(sc->data);
915a7c43ebdSLandon J. Fuller 
916a7c43ebdSLandon J. Fuller 	if (options != NULL)
917a7c43ebdSLandon J. Fuller 		*options = bhnd_nvram_plist_retain(sc->data_opts);
918a7c43ebdSLandon J. Fuller 
919a7c43ebdSLandon J. Fuller 	/*
920a7c43ebdSLandon J. Fuller 	 * If we're re-encoding device paths, don't bother preserving the
921a7c43ebdSLandon J. Fuller 	 * existing NVRAM variable order; our variable names will not match
922a7c43ebdSLandon J. Fuller 	 * the existing backing NVRAM data.
923a7c43ebdSLandon J. Fuller 	 */
924a7c43ebdSLandon J. Fuller 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
925a7c43ebdSLandon J. Fuller 		*props = unordered;
926a7c43ebdSLandon J. Fuller 		unordered = NULL;
927a7c43ebdSLandon J. Fuller 
928a7c43ebdSLandon J. Fuller 		goto finished;
929a7c43ebdSLandon J. Fuller 	}
930a7c43ebdSLandon J. Fuller 
931a7c43ebdSLandon J. Fuller 	/*
932a7c43ebdSLandon J. Fuller 	 * Re-order the flattened output to match the existing NVRAM variable
933a7c43ebdSLandon J. Fuller 	 * ordering.
934a7c43ebdSLandon J. Fuller 	 *
935a7c43ebdSLandon J. Fuller 	 * We append all new variables at the end of the input; this should
936a7c43ebdSLandon J. Fuller 	 * reduce the delta that needs to be written (e.g. to flash) when
937a7c43ebdSLandon J. Fuller 	 * committing NVRAM updates, and should result in a serialization
938a7c43ebdSLandon J. Fuller 	 * identical to the input serialization if uncommitted updates are
939a7c43ebdSLandon J. Fuller 	 * excluded from the export.
940a7c43ebdSLandon J. Fuller 	 */
941a7c43ebdSLandon J. Fuller 	if ((*props = bhnd_nvram_plist_new()) == NULL) {
942a7c43ebdSLandon J. Fuller 		error = ENOMEM;
943a7c43ebdSLandon J. Fuller 		goto failed;
944a7c43ebdSLandon J. Fuller 	}
945a7c43ebdSLandon J. Fuller 
946a7c43ebdSLandon J. Fuller 	/* Using the backing NVRAM data ordering to order all variables
947a7c43ebdSLandon J. Fuller 	 * currently defined in the backing store */
948a7c43ebdSLandon J. Fuller 	cookiep = NULL;
949a7c43ebdSLandon J. Fuller 	while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
950a7c43ebdSLandon J. Fuller 		prop = bhnd_nvram_plist_get_prop(unordered, name);
951a7c43ebdSLandon J. Fuller 		if (prop == NULL)
952a7c43ebdSLandon J. Fuller 			continue;
953a7c43ebdSLandon J. Fuller 
954a7c43ebdSLandon J. Fuller 		/* Append to ordered result */
955a7c43ebdSLandon J. Fuller 		if ((error = bhnd_nvram_plist_append(*props, prop)))
956a7c43ebdSLandon J. Fuller 			goto failed;
957a7c43ebdSLandon J. Fuller 
958a7c43ebdSLandon J. Fuller 		/* Remove from unordered list */
959a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_remove(unordered, name);
960a7c43ebdSLandon J. Fuller 	}
961a7c43ebdSLandon J. Fuller 
962a7c43ebdSLandon J. Fuller 	/* Any remaining variables are new, and should be appended to the
963a7c43ebdSLandon J. Fuller 	 * end of the export list */
964a7c43ebdSLandon J. Fuller 	prop = NULL;
965a7c43ebdSLandon J. Fuller 	while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) {
966a7c43ebdSLandon J. Fuller 		if ((error = bhnd_nvram_plist_append(*props, prop)))
967a7c43ebdSLandon J. Fuller 			goto failed;
968a7c43ebdSLandon J. Fuller 	}
969a7c43ebdSLandon J. Fuller 
970a7c43ebdSLandon J. Fuller 	/* Export complete */
971a7c43ebdSLandon J. Fuller finished:
972a7c43ebdSLandon J. Fuller 	BHND_NVSTORE_UNLOCK(sc);
973a7c43ebdSLandon J. Fuller 
974a7c43ebdSLandon J. Fuller 	if (unordered != NULL)
975a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_release(unordered);
976a7c43ebdSLandon J. Fuller 
977a7c43ebdSLandon J. Fuller 	return (0);
978a7c43ebdSLandon J. Fuller 
979a7c43ebdSLandon J. Fuller failed:
980a7c43ebdSLandon J. Fuller 	BHND_NVSTORE_UNLOCK(sc);
981a7c43ebdSLandon J. Fuller 
982a7c43ebdSLandon J. Fuller 	if (unordered != NULL)
983a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_release(unordered);
984a7c43ebdSLandon J. Fuller 
985a7c43ebdSLandon J. Fuller 	if (options != NULL && *options != NULL)
986a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_release(*options);
987a7c43ebdSLandon J. Fuller 
988a7c43ebdSLandon J. Fuller 	if (*props != NULL)
989a7c43ebdSLandon J. Fuller 		bhnd_nvram_plist_release(*props);
990a7c43ebdSLandon J. Fuller 
991a7c43ebdSLandon J. Fuller 	return (error);
992a7c43ebdSLandon J. Fuller }
993a7c43ebdSLandon J. Fuller 
99419be09f3SLandon J. Fuller /**
995c283839dSLandon J. Fuller  * Encode all NVRAM properties at @p path, using the @p store's current NVRAM
996c283839dSLandon J. Fuller  * data format.
997c283839dSLandon J. Fuller  *
998c283839dSLandon J. Fuller  * @param	sc	The NVRAM store instance.
999c283839dSLandon J. Fuller  * @param	path	The NVRAM path to export, or NULL to select the root
1000c283839dSLandon J. Fuller  *			path.
1001c283839dSLandon J. Fuller  * @param[out]	data	On success, will be set to the newly serialized value.
1002c283839dSLandon J. Fuller  *			The caller is responsible for freeing this value
1003c283839dSLandon J. Fuller  *			via bhnd_nvram_io_free().
1004c283839dSLandon J. Fuller  * @param	flags	Export flags. See BHND_NVSTORE_EXPORT_*.
1005c283839dSLandon J. Fuller  *
1006c283839dSLandon J. Fuller  * @retval 0		success
1007c283839dSLandon J. Fuller  * @retval EINVAL	If @p flags is invalid.
1008c283839dSLandon J. Fuller  * @retval ENOENT	The requested path was not found.
1009c283839dSLandon J. Fuller  * @retval ENOMEM	If allocation fails.
1010c283839dSLandon J. Fuller  * @retval non-zero	If serialization of  @p path otherwise fails, a regular
1011c283839dSLandon J. Fuller  *			unix error code will be returned.
1012c283839dSLandon J. Fuller  */
1013c283839dSLandon J. Fuller int
bhnd_nvram_store_serialize(struct bhnd_nvram_store * sc,const char * path,struct bhnd_nvram_io ** data,uint32_t flags)1014c283839dSLandon J. Fuller bhnd_nvram_store_serialize(struct bhnd_nvram_store *sc, const char *path,
1015c283839dSLandon J. Fuller    struct bhnd_nvram_io **data,  uint32_t flags)
1016c283839dSLandon J. Fuller {
1017c283839dSLandon J. Fuller 	bhnd_nvram_plist	*props;
1018c283839dSLandon J. Fuller 	bhnd_nvram_plist	*options;
1019c283839dSLandon J. Fuller 	bhnd_nvram_data_class	*cls;
1020c283839dSLandon J. Fuller 	struct bhnd_nvram_io	*io;
1021c283839dSLandon J. Fuller 	void			*outp;
1022c283839dSLandon J. Fuller 	size_t			 olen;
1023c283839dSLandon J. Fuller 	int			 error;
1024c283839dSLandon J. Fuller 
1025c283839dSLandon J. Fuller 	props = NULL;
1026c283839dSLandon J. Fuller 	options = NULL;
1027c283839dSLandon J. Fuller 	io = NULL;
1028c283839dSLandon J. Fuller 
1029c283839dSLandon J. Fuller 	/* Perform requested export */
1030c283839dSLandon J. Fuller 	error = bhnd_nvram_store_export(sc, path, &cls, &props, &options,
1031c283839dSLandon J. Fuller 	    flags);
1032c283839dSLandon J. Fuller 	if (error)
1033c283839dSLandon J. Fuller 		return (error);
1034c283839dSLandon J. Fuller 
1035c283839dSLandon J. Fuller 	/* Determine serialized size */
1036c283839dSLandon J. Fuller 	error = bhnd_nvram_data_serialize(cls, props, options, NULL, &olen);
1037c283839dSLandon J. Fuller 	if (error)
1038c283839dSLandon J. Fuller 		goto failed;
1039c283839dSLandon J. Fuller 
1040c283839dSLandon J. Fuller 	/* Allocate output buffer */
1041c283839dSLandon J. Fuller 	if ((io = bhnd_nvram_iobuf_empty(olen, olen)) == NULL) {
1042c283839dSLandon J. Fuller 		error = ENOMEM;
1043c283839dSLandon J. Fuller 		goto failed;
1044c283839dSLandon J. Fuller 	}
1045c283839dSLandon J. Fuller 
1046c283839dSLandon J. Fuller 	/* Fetch write pointer */
1047c283839dSLandon J. Fuller 	if ((error = bhnd_nvram_io_write_ptr(io, 0, &outp, olen, NULL)))
1048c283839dSLandon J. Fuller 		goto failed;
1049c283839dSLandon J. Fuller 
1050c283839dSLandon J. Fuller 	/* Perform serialization */
1051c283839dSLandon J. Fuller 	error = bhnd_nvram_data_serialize(cls, props, options, outp, &olen);
1052c283839dSLandon J. Fuller 	if (error)
1053c283839dSLandon J. Fuller 		goto failed;
1054c283839dSLandon J. Fuller 
1055c283839dSLandon J. Fuller 	if ((error = bhnd_nvram_io_setsize(io, olen)))
1056c283839dSLandon J. Fuller 		goto failed;
1057c283839dSLandon J. Fuller 
1058c283839dSLandon J. Fuller 	/* Success */
1059c283839dSLandon J. Fuller 	bhnd_nvram_plist_release(props);
1060c283839dSLandon J. Fuller 	bhnd_nvram_plist_release(options);
1061c283839dSLandon J. Fuller 
1062c283839dSLandon J. Fuller 	*data = io;
1063c283839dSLandon J. Fuller 	return (0);
1064c283839dSLandon J. Fuller 
1065c283839dSLandon J. Fuller failed:
1066c283839dSLandon J. Fuller 	if (props != NULL)
1067c283839dSLandon J. Fuller 		bhnd_nvram_plist_release(props);
1068c283839dSLandon J. Fuller 
1069c283839dSLandon J. Fuller 	if (options != NULL)
1070c283839dSLandon J. Fuller 		bhnd_nvram_plist_release(options);
1071c283839dSLandon J. Fuller 
1072c283839dSLandon J. Fuller 	if (io != NULL)
1073c283839dSLandon J. Fuller 		bhnd_nvram_io_free(io);
1074c283839dSLandon J. Fuller 
1075c283839dSLandon J. Fuller 	return (error);
1076c283839dSLandon J. Fuller }
1077c283839dSLandon J. Fuller 
1078c283839dSLandon J. Fuller /**
107977cb4d3eSLandon J. Fuller  * Read an NVRAM variable.
108077cb4d3eSLandon J. Fuller  *
108177cb4d3eSLandon J. Fuller  * @param		sc	The NVRAM parser state.
108277cb4d3eSLandon J. Fuller  * @param		name	The NVRAM variable name.
108319be09f3SLandon J. Fuller  * @param[out]		outp	On success, the requested value will be written
108477cb4d3eSLandon J. Fuller  *				to this buffer. This argment may be NULL if
108577cb4d3eSLandon J. Fuller  *				the value is not desired.
108619be09f3SLandon J. Fuller  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
108777cb4d3eSLandon J. Fuller  *				to the actual size of the requested value.
108819be09f3SLandon J. Fuller  * @param		otype	The requested data type to be written to
108919be09f3SLandon J. Fuller  *				@p outp.
109077cb4d3eSLandon J. Fuller  *
109177cb4d3eSLandon J. Fuller  * @retval 0		success
109277cb4d3eSLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
109319be09f3SLandon J. Fuller  * @retval ENOMEM	If @p outp is non-NULL and a buffer of @p olen is too
109477cb4d3eSLandon J. Fuller  *			small to hold the requested value.
109577cb4d3eSLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
109677cb4d3eSLandon J. Fuller  *			error code will be returned.
109777cb4d3eSLandon J. Fuller   */
109877cb4d3eSLandon J. Fuller int
bhnd_nvram_store_getvar(struct bhnd_nvram_store * sc,const char * name,void * outp,size_t * olen,bhnd_nvram_type otype)109977cb4d3eSLandon J. Fuller bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
110019be09f3SLandon J. Fuller     void *outp, size_t *olen, bhnd_nvram_type otype)
110177cb4d3eSLandon J. Fuller {
110219be09f3SLandon J. Fuller 	bhnd_nvstore_name_info	 info;
110319be09f3SLandon J. Fuller 	bhnd_nvstore_path	*path;
110419be09f3SLandon J. Fuller 	bhnd_nvram_prop		*prop;
110577cb4d3eSLandon J. Fuller 	void			*cookiep;
110677cb4d3eSLandon J. Fuller 	int			 error;
110777cb4d3eSLandon J. Fuller 
110877cb4d3eSLandon J. Fuller 	BHND_NVSTORE_LOCK(sc);
110977cb4d3eSLandon J. Fuller 
111019be09f3SLandon J. Fuller 	/* Parse the variable name */
111119be09f3SLandon J. Fuller 	error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
111219be09f3SLandon J. Fuller 	    sc->data_caps, &info);
111319be09f3SLandon J. Fuller 	if (error)
111419be09f3SLandon J. Fuller 		goto finished;
111519be09f3SLandon J. Fuller 
111619be09f3SLandon J. Fuller 	/* Fetch the variable's enclosing path entry */
111719be09f3SLandon J. Fuller 	if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) {
111819be09f3SLandon J. Fuller 		error = ENOENT;
111919be09f3SLandon J. Fuller 		goto finished;
112077cb4d3eSLandon J. Fuller 	}
112177cb4d3eSLandon J. Fuller 
112219be09f3SLandon J. Fuller 	/* Search uncommitted updates first */
112319be09f3SLandon J. Fuller 	prop = bhnd_nvstore_path_get_update(sc, path, info.name);
112419be09f3SLandon J. Fuller 	if (prop != NULL) {
112519be09f3SLandon J. Fuller 		if (bhnd_nvram_prop_is_null(prop)) {
112619be09f3SLandon J. Fuller 			/* NULL denotes a pending deletion */
112719be09f3SLandon J. Fuller 			error = ENOENT;
112819be09f3SLandon J. Fuller 		} else {
112919be09f3SLandon J. Fuller 			error = bhnd_nvram_prop_encode(prop, outp, olen, otype);
113019be09f3SLandon J. Fuller 		}
113119be09f3SLandon J. Fuller 		goto finished;
113219be09f3SLandon J. Fuller 	}
113377cb4d3eSLandon J. Fuller 
113419be09f3SLandon J. Fuller 	/* Search the backing NVRAM data */
113519be09f3SLandon J. Fuller 	cookiep = bhnd_nvstore_path_data_lookup(sc, path, info.name);
113619be09f3SLandon J. Fuller 	if (cookiep != NULL) {
113719be09f3SLandon J. Fuller 		/* Found in backing store */
113819be09f3SLandon J. Fuller 		error = bhnd_nvram_data_getvar(sc->data, cookiep, outp, olen,
113919be09f3SLandon J. Fuller 		     otype);
114019be09f3SLandon J. Fuller 		goto finished;
114119be09f3SLandon J. Fuller 	}
114277cb4d3eSLandon J. Fuller 
114319be09f3SLandon J. Fuller 	/* Not found */
114419be09f3SLandon J. Fuller 	error = ENOENT;
114519be09f3SLandon J. Fuller 
114619be09f3SLandon J. Fuller finished:
114777cb4d3eSLandon J. Fuller 	BHND_NVSTORE_UNLOCK(sc);
114877cb4d3eSLandon J. Fuller 	return (error);
114977cb4d3eSLandon J. Fuller }
115077cb4d3eSLandon J. Fuller 
115119be09f3SLandon J. Fuller /**
115219be09f3SLandon J. Fuller  * Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar()
115319be09f3SLandon J. Fuller  * implementation.
115419be09f3SLandon J. Fuller  *
115519be09f3SLandon J. Fuller  * If @p value is NULL, the variable will be marked for deletion.
115619be09f3SLandon J. Fuller  */
115719be09f3SLandon J. Fuller static int
bhnd_nvram_store_setval_common(struct bhnd_nvram_store * sc,const char * name,bhnd_nvram_val * value)115819be09f3SLandon J. Fuller bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name,
115919be09f3SLandon J. Fuller     bhnd_nvram_val *value)
116019be09f3SLandon J. Fuller {
116119be09f3SLandon J. Fuller 	bhnd_nvstore_path	*path;
116219be09f3SLandon J. Fuller 	bhnd_nvstore_name_info	 info;
116319be09f3SLandon J. Fuller 	int			 error;
116419be09f3SLandon J. Fuller 
116519be09f3SLandon J. Fuller 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
116619be09f3SLandon J. Fuller 
116719be09f3SLandon J. Fuller 	/* Parse the variable name */
116819be09f3SLandon J. Fuller 	error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
116919be09f3SLandon J. Fuller 	    sc->data_caps, &info);
117019be09f3SLandon J. Fuller 	if (error)
117119be09f3SLandon J. Fuller 		return (error);
117219be09f3SLandon J. Fuller 
117319be09f3SLandon J. Fuller 	/* Fetch the variable's enclosing path entry */
117419be09f3SLandon J. Fuller 	if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL)
117519be09f3SLandon J. Fuller 		return (error);
117619be09f3SLandon J. Fuller 
117719be09f3SLandon J. Fuller 	/* Register the update entry */
117819be09f3SLandon J. Fuller 	return (bhnd_nvstore_path_register_update(sc, path, info.name, value));
117977cb4d3eSLandon J. Fuller }
118077cb4d3eSLandon J. Fuller 
118119be09f3SLandon J. Fuller /**
118219be09f3SLandon J. Fuller  * Set an NVRAM variable.
118319be09f3SLandon J. Fuller  *
118419be09f3SLandon J. Fuller  * @param	sc	The NVRAM parser state.
118519be09f3SLandon J. Fuller  * @param	name	The NVRAM variable name.
118619be09f3SLandon J. Fuller  * @param	value	The new value.
118719be09f3SLandon J. Fuller  *
118819be09f3SLandon J. Fuller  * @retval 0		success
118919be09f3SLandon J. Fuller  * @retval ENOENT	The requested variable @p name was not found.
119019be09f3SLandon J. Fuller  * @retval EINVAL	If @p value is invalid.
119119be09f3SLandon J. Fuller  */
119219be09f3SLandon J. Fuller int
bhnd_nvram_store_setval(struct bhnd_nvram_store * sc,const char * name,bhnd_nvram_val * value)119319be09f3SLandon J. Fuller bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name,
119419be09f3SLandon J. Fuller     bhnd_nvram_val *value)
119519be09f3SLandon J. Fuller {
119619be09f3SLandon J. Fuller 	int error;
119719be09f3SLandon J. Fuller 
119819be09f3SLandon J. Fuller 	BHND_NVSTORE_LOCK(sc);
119919be09f3SLandon J. Fuller 	error = bhnd_nvram_store_setval_common(sc, name, value);
120077cb4d3eSLandon J. Fuller 	BHND_NVSTORE_UNLOCK(sc);
120177cb4d3eSLandon J. Fuller 
120277cb4d3eSLandon J. Fuller 	return (error);
120377cb4d3eSLandon J. Fuller }
120477cb4d3eSLandon J. Fuller 
120577cb4d3eSLandon J. Fuller /**
120677cb4d3eSLandon J. Fuller  * Set an NVRAM variable.
120777cb4d3eSLandon J. Fuller  *
120877cb4d3eSLandon J. Fuller  * @param		sc	The NVRAM parser state.
120977cb4d3eSLandon J. Fuller  * @param		name	The NVRAM variable name.
121019be09f3SLandon J. Fuller  * @param[out]		inp	The new value.
121119be09f3SLandon J. Fuller  * @param[in,out]	ilen	The size of @p inp.
121219be09f3SLandon J. Fuller  * @param		itype	The data type of @p inp.
121377cb4d3eSLandon J. Fuller  *
121477cb4d3eSLandon J. Fuller  * @retval 0		success
121519be09f3SLandon J. Fuller  * @retval ENOENT	The requested variable @p name was not found.
121619be09f3SLandon J. Fuller  * @retval EINVAL	If the new value is invalid.
121719be09f3SLandon J. Fuller  * @retval EINVAL	If @p name is read-only.
121877cb4d3eSLandon J. Fuller  */
121977cb4d3eSLandon J. Fuller int
bhnd_nvram_store_setvar(struct bhnd_nvram_store * sc,const char * name,const void * inp,size_t ilen,bhnd_nvram_type itype)122077cb4d3eSLandon J. Fuller bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
122119be09f3SLandon J. Fuller     const void *inp, size_t ilen, bhnd_nvram_type itype)
122277cb4d3eSLandon J. Fuller {
122319be09f3SLandon J. Fuller 	bhnd_nvram_val	val;
122477cb4d3eSLandon J. Fuller 	int		error;
122577cb4d3eSLandon J. Fuller 
122619be09f3SLandon J. Fuller 	error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
122719be09f3SLandon J. Fuller 	    BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA);
122877cb4d3eSLandon J. Fuller 	if (error) {
122919be09f3SLandon J. Fuller 		BHND_NV_LOG("error initializing value: %d\n", error);
123019be09f3SLandon J. Fuller 		return (EINVAL);
123119be09f3SLandon J. Fuller 	}
123219be09f3SLandon J. Fuller 
123319be09f3SLandon J. Fuller 	BHND_NVSTORE_LOCK(sc);
123419be09f3SLandon J. Fuller 	error = bhnd_nvram_store_setval_common(sc, name, &val);
123519be09f3SLandon J. Fuller 	BHND_NVSTORE_UNLOCK(sc);
123619be09f3SLandon J. Fuller 
123719be09f3SLandon J. Fuller 	bhnd_nvram_val_release(&val);
123819be09f3SLandon J. Fuller 
123977cb4d3eSLandon J. Fuller 	return (error);
124077cb4d3eSLandon J. Fuller }
124177cb4d3eSLandon J. Fuller 
124277cb4d3eSLandon J. Fuller /**
124319be09f3SLandon J. Fuller  * Unset an NVRAM variable.
124477cb4d3eSLandon J. Fuller  *
124577cb4d3eSLandon J. Fuller  * @param		sc	The NVRAM parser state.
124619be09f3SLandon J. Fuller  * @param		name	The NVRAM variable name.
124777cb4d3eSLandon J. Fuller  *
124877cb4d3eSLandon J. Fuller  * @retval 0		success
124919be09f3SLandon J. Fuller  * @retval ENOENT	The requested variable @p name was not found.
125019be09f3SLandon J. Fuller  * @retval EINVAL	If @p name is read-only.
125177cb4d3eSLandon J. Fuller  */
125219be09f3SLandon J. Fuller int
bhnd_nvram_store_unsetvar(struct bhnd_nvram_store * sc,const char * name)125319be09f3SLandon J. Fuller bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name)
125477cb4d3eSLandon J. Fuller {
125577cb4d3eSLandon J. Fuller 	int error;
125677cb4d3eSLandon J. Fuller 
125719be09f3SLandon J. Fuller 	BHND_NVSTORE_LOCK(sc);
125819be09f3SLandon J. Fuller 	error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL);
125919be09f3SLandon J. Fuller 	BHND_NVSTORE_UNLOCK(sc);
126019be09f3SLandon J. Fuller 
126177cb4d3eSLandon J. Fuller 	return (error);
126277cb4d3eSLandon J. Fuller }
1263