xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_store.c (revision 6be3386466ab79a84b48429ae66244f21526d3df)
1 /*-
2  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/hash.h>
35 #include <sys/limits.h>
36 #include <sys/queue.h>
37 
38 #ifdef _KERNEL
39 
40 #include <sys/ctype.h>
41 #include <sys/systm.h>
42 
43 #include <machine/_inttypes.h>
44 
45 #else /* !_KERNEL */
46 
47 #include <ctype.h>
48 #include <errno.h>
49 #include <inttypes.h>
50 #include <stdbool.h>
51 #include <stdio.h>
52 #include <stdint.h>
53 #include <stdlib.h>
54 #include <string.h>
55 
56 #endif /* _KERNEL */
57 
58 #include "bhnd_nvram_private.h"
59 #include "bhnd_nvram_datavar.h"
60 
61 #include "bhnd_nvram_storevar.h"
62 
63 /*
64  * BHND NVRAM Store
65  *
66  * Manages in-memory and persistent representations of NVRAM data.
67  */
68 
69 static int			 bhnd_nvstore_parse_data(
70 				     struct bhnd_nvram_store *sc);
71 
72 static int			 bhnd_nvstore_parse_path_entries(
73 				     struct bhnd_nvram_store *sc);
74 
75 static int			 bhnd_nvram_store_export_child(
76 				     struct bhnd_nvram_store *sc,
77 				     bhnd_nvstore_path *top,
78 				     bhnd_nvstore_path *child,
79 				     bhnd_nvram_plist *plist,
80 				     uint32_t flags);
81 
82 static int			 bhnd_nvstore_export_merge(
83 				     struct bhnd_nvram_store *sc,
84 				     bhnd_nvstore_path *path,
85 				     bhnd_nvram_plist *merged,
86 				     uint32_t flags);
87 
88 static int			 bhnd_nvstore_export_devpath_alias(
89 				     struct bhnd_nvram_store *sc,
90 				     bhnd_nvstore_path *path,
91 				     const char *devpath,
92 				     bhnd_nvram_plist *plist,
93 				     u_long *alias_val);
94 
95 /**
96  * Allocate and initialize a new NVRAM data store instance.
97  *
98  * The caller is responsible for deallocating the instance via
99  * bhnd_nvram_store_free().
100  *
101  * @param[out] store On success, a pointer to the newly allocated NVRAM data
102  * instance.
103  * @param data The NVRAM data to be managed by the returned NVRAM data store
104  * instance.
105  *
106  * @retval 0 success
107  * @retval non-zero if an error occurs during allocation or initialization, a
108  * regular unix error code will be returned.
109  */
110 int
111 bhnd_nvram_store_new(struct bhnd_nvram_store **store,
112     struct bhnd_nvram_data *data)
113 {
114 	struct bhnd_nvram_store *sc;
115 	int			 error;
116 
117 	/* Allocate new instance */
118 	sc = bhnd_nv_calloc(1, sizeof(*sc));
119 	if (sc == NULL)
120 		return (ENOMEM);
121 
122 	BHND_NVSTORE_LOCK_INIT(sc);
123 	BHND_NVSTORE_LOCK(sc);
124 
125 	/* Initialize path hash table */
126 	sc->num_paths = 0;
127 	for (size_t i = 0; i < nitems(sc->paths); i++)
128 		LIST_INIT(&sc->paths[i]);
129 
130 	/* Initialize alias hash table */
131 	sc->num_aliases = 0;
132 	for (size_t i = 0; i < nitems(sc->aliases); i++)
133 		LIST_INIT(&sc->aliases[i]);
134 
135 	/* Retain the NVRAM data */
136 	sc->data = bhnd_nvram_data_retain(data);
137 	sc->data_caps = bhnd_nvram_data_caps(data);
138 	sc->data_opts = bhnd_nvram_data_options(data);
139 	if (sc->data_opts != NULL) {
140 		bhnd_nvram_plist_retain(sc->data_opts);
141 	} else {
142 		sc->data_opts = bhnd_nvram_plist_new();
143 		if (sc->data_opts == NULL) {
144 			error = ENOMEM;
145 			goto cleanup;
146 		}
147 	}
148 
149 	/* Register required root path */
150 	error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH,
151 	    BHND_NVSTORE_ROOT_PATH_LEN);
152 	if (error)
153 		goto cleanup;
154 
155 	sc->root_path = bhnd_nvstore_get_path(sc, BHND_NVSTORE_ROOT_PATH,
156 	    BHND_NVSTORE_ROOT_PATH_LEN);
157 	BHND_NV_ASSERT(sc->root_path, ("missing root path"));
158 
159 	/* Parse all variables vended by our backing NVRAM data instance,
160 	 * generating all path entries, alias entries, and variable indexes */
161 	if ((error = bhnd_nvstore_parse_data(sc)))
162 		goto cleanup;
163 
164 	*store = sc;
165 
166 	BHND_NVSTORE_UNLOCK(sc);
167 	return (0);
168 
169 cleanup:
170 	BHND_NVSTORE_UNLOCK(sc);
171 	bhnd_nvram_store_free(sc);
172 	return (error);
173 }
174 
175 /**
176  * Allocate and initialize a new NVRAM data store instance, parsing the
177  * NVRAM data from @p io.
178  *
179  * The caller is responsible for deallocating the instance via
180  * bhnd_nvram_store_free().
181  *
182  * The NVRAM data mapped by @p io will be copied, and @p io may be safely
183  * deallocated after bhnd_nvram_store_new() returns.
184  *
185  * @param[out] store On success, a pointer to the newly allocated NVRAM data
186  * instance.
187  * @param io An I/O context mapping the NVRAM data to be copied and parsed.
188  * @param cls The NVRAM data class to be used when parsing @p io, or NULL
189  * to perform runtime identification of the appropriate data class.
190  *
191  * @retval 0 success
192  * @retval non-zero if an error occurs during allocation or initialization, a
193  * regular unix error code will be returned.
194  */
195 int
196 bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
197     struct bhnd_nvram_io *io, bhnd_nvram_data_class *cls)
198 {
199 	struct bhnd_nvram_data	*data;
200 	int			 error;
201 
202 	/* Try to parse the data */
203 	if ((error = bhnd_nvram_data_new(cls, &data, io)))
204 		return (error);
205 
206 	/* Try to create our new store instance */
207 	error = bhnd_nvram_store_new(store, data);
208 	bhnd_nvram_data_release(data);
209 
210 	return (error);
211 }
212 
213 /**
214  * Free an NVRAM store instance, releasing all associated resources.
215  *
216  * @param sc A store instance previously allocated via
217  * bhnd_nvram_store_new().
218  */
219 void
220 bhnd_nvram_store_free(struct bhnd_nvram_store *sc)
221 {
222 
223 	/* Clean up alias hash table */
224 	for (size_t i = 0; i < nitems(sc->aliases); i++) {
225 		bhnd_nvstore_alias *alias, *anext;
226 		LIST_FOREACH_SAFE(alias, &sc->aliases[i], na_link, anext)
227 			bhnd_nv_free(alias);
228 	}
229 
230 	/* Clean up path hash table */
231 	for (size_t i = 0; i < nitems(sc->paths); i++) {
232 		bhnd_nvstore_path *path, *pnext;
233 		LIST_FOREACH_SAFE(path, &sc->paths[i], np_link, pnext)
234 			bhnd_nvstore_path_free(path);
235 	}
236 
237 	if (sc->data != NULL)
238 		bhnd_nvram_data_release(sc->data);
239 
240 	if (sc->data_opts != NULL)
241 		bhnd_nvram_plist_release(sc->data_opts);
242 
243 	BHND_NVSTORE_LOCK_DESTROY(sc);
244 	bhnd_nv_free(sc);
245 }
246 
247 /**
248  * Parse all variables vended by our backing NVRAM data instance,
249  * generating all path entries, alias entries, and variable indexes.
250  *
251  * @param	sc	The NVRAM store instance to be initialized with
252  *			paths, aliases, and data parsed from its backing
253  *			data.
254  *
255  * @retval 0		success
256  * @retval non-zero	if an error occurs during parsing, a regular unix error
257  *			code will be returned.
258  */
259 static int
260 bhnd_nvstore_parse_data(struct bhnd_nvram_store *sc)
261 {
262 	const char	*name;
263 	void		*cookiep;
264 	int		 error;
265 
266 	/* Parse and register all device paths and path aliases. This enables
267 	 * resolution of _forward_ references to device paths aliases when
268 	 * scanning variable entries below */
269 	if ((error = bhnd_nvstore_parse_path_entries(sc)))
270 		return (error);
271 
272 	/* Calculate the per-path variable counts, and report dangling alias
273 	 * references as an error. */
274 	cookiep = NULL;
275 	while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
276 		bhnd_nvstore_path	*path;
277 		bhnd_nvstore_name_info	 info;
278 
279 		/* Parse the name info */
280 		error = bhnd_nvstore_parse_name_info(name,
281 		    BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
282 		if (error)
283 			return (error);
284 
285 		switch (info.type) {
286 		case BHND_NVSTORE_VAR:
287 			/* Fetch referenced path */
288 			path = bhnd_nvstore_var_get_path(sc, &info);
289 			if (path == NULL) {
290 				BHND_NV_LOG("variable '%s' has dangling "
291 					    "path reference\n", name);
292 				return (EFTYPE);
293 			}
294 
295 			/* Increment path variable count */
296 			if (path->num_vars == SIZE_MAX) {
297 				BHND_NV_LOG("more than SIZE_MAX variables in "
298 				    "path %s\n", path->path_str);
299 				return (EFTYPE);
300 			}
301 			path->num_vars++;
302 			break;
303 
304 		case BHND_NVSTORE_ALIAS_DECL:
305 			/* Skip -- path alias already parsed and recorded */
306 			break;
307 		}
308 	}
309 
310 	/* If the backing NVRAM data instance vends only a single root ("/")
311 	 * path, we may be able to skip generating an index for the root
312 	 * path */
313 	if (sc->num_paths == 1) {
314 		bhnd_nvstore_path *path;
315 
316 		/* If the backing instance provides its own name-based lookup
317 		 * indexing, we can skip generating a duplicate here */
318 		if (sc->data_caps & BHND_NVRAM_DATA_CAP_INDEXED)
319 			return (0);
320 
321 		/* If the sole root path contains fewer variables than the
322 		 * minimum indexing threshhold, we do not need to generate an
323 		 * index */
324 		path = bhnd_nvstore_get_root_path(sc);
325 		if (path->num_vars < BHND_NV_IDX_VAR_THRESHOLD)
326 			return (0);
327 	}
328 
329 	/* Allocate per-path index instances */
330 	for (size_t i = 0; i < nitems(sc->paths); i++) {
331 		bhnd_nvstore_path	*path;
332 
333 		LIST_FOREACH(path, &sc->paths[i], np_link) {
334 			path->index = bhnd_nvstore_index_new(path->num_vars);
335 			if (path->index == NULL)
336 				return (ENOMEM);
337 		}
338 	}
339 
340 	/* Populate per-path indexes */
341 	cookiep = NULL;
342 	while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
343 		bhnd_nvstore_name_info	 info;
344 		bhnd_nvstore_path	*path;
345 
346 		/* Parse the name info */
347 		error = bhnd_nvstore_parse_name_info(name,
348 		    BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
349 		if (error)
350 			return (error);
351 
352 		switch (info.type) {
353 		case BHND_NVSTORE_VAR:
354 			/* Fetch referenced path */
355 			path = bhnd_nvstore_var_get_path(sc, &info);
356 			BHND_NV_ASSERT(path != NULL,
357 			    ("dangling path reference"));
358 
359 			/* Append to index */
360 			error = bhnd_nvstore_index_append(sc, path->index,
361 			    cookiep);
362 			if (error)
363 				return (error);
364 			break;
365 
366 		case BHND_NVSTORE_ALIAS_DECL:
367 			/* Skip */
368 			break;
369 		}
370 	}
371 
372 	/* Prepare indexes for querying */
373 	for (size_t i = 0; i < nitems(sc->paths); i++) {
374 		bhnd_nvstore_path	*path;
375 
376 		LIST_FOREACH(path, &sc->paths[i], np_link) {
377 			error = bhnd_nvstore_index_prepare(sc, path->index);
378 			if (error)
379 				return (error);
380 		}
381 	}
382 
383 	return (0);
384 }
385 
386 /**
387  * Parse and register path and path alias entries for all declarations found in
388  * the NVRAM data backing @p nvram.
389  *
390  * @param sc		The NVRAM store instance.
391  *
392  * @retval 0		success
393  * @retval non-zero	If parsing fails, a regular unix error code will be
394  *			returned.
395  */
396 static int
397 bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc)
398 {
399 	const char	*name;
400 	void		*cookiep;
401 	int		 error;
402 
403 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
404 
405 	/* Skip path registration if the data source does not support device
406 	 * paths. */
407 	if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) {
408 		BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path"));
409 		return (0);
410 	}
411 
412 	/* Otherwise, parse and register all paths and path aliases */
413 	cookiep = NULL;
414 	while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
415 		bhnd_nvstore_name_info info;
416 
417 		/* Parse the name info */
418 		error = bhnd_nvstore_parse_name_info(name,
419 		    BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
420 		if (error)
421 			return (error);
422 
423 		/* Register the path */
424 		error = bhnd_nvstore_var_register_path(sc, &info, cookiep);
425 		if (error) {
426 			BHND_NV_LOG("failed to register path for %s: %d\n",
427 			    name, error);
428 			return (error);
429 		}
430 	}
431 
432 	return (0);
433 }
434 
435 /**
436  * Merge exported per-path variables (uncommitted, committed, or both) into
437  * the empty @p merged property list.
438  *
439  * @param	sc	The NVRAM store instance.
440  * @param	path	The NVRAM path to be exported.
441  * @param	merged	The property list to populate with the merged results.
442  * @param	flags	Export flags. See BHND_NVSTORE_EXPORT_*.
443  *
444  * @retval 0		success
445  * @retval ENOMEM	If allocation fails.
446  * @retval non-zero	If merging the variables defined in @p path otherwise
447  *			fails, a regular unix error code will be returned.
448  */
449 static int
450 bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc,
451     bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags)
452 {
453 	void	*cookiep, *idxp;
454 	int	 error;
455 
456 	/* Populate merged list with all pending variables */
457 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
458 		bhnd_nvram_prop *prop;
459 
460 		prop = NULL;
461 		while ((prop = bhnd_nvram_plist_next(path->pending, prop))) {
462 			/* Skip variables marked for deletion */
463 			if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) {
464 				if (bhnd_nvram_prop_is_null(prop))
465 					continue;
466 			}
467 
468 			/* Append to merged list */
469 			error = bhnd_nvram_plist_append(merged, prop);
470 			if (error)
471 				return (error);
472 		}
473 	}
474 
475 	/* Skip merging committed variables? */
476 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED))
477 		return (0);
478 
479 	/* Merge in the committed NVRAM variables */
480 	idxp = NULL;
481 	while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) {
482 		const char	*name;
483 		bhnd_nvram_val	*val;
484 
485 		/* Fetch the variable name */
486 		name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
487 
488 		/* Trim device path prefix */
489 		if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)
490 			name = bhnd_nvram_trim_path_name(name);
491 
492 		/* Skip if already defined in pending updates */
493 		if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
494 			if (bhnd_nvram_plist_contains(path->pending, name))
495 				continue;
496 		}
497 
498 		/* Skip if higher precedence value was already defined. This
499 		 * may occur if the underlying data store contains duplicate
500 		 * keys; iteration will always return the definition with
501 		 * the highest precedence first */
502 		if (bhnd_nvram_plist_contains(merged, name))
503 			continue;
504 
505 		/* Fetch the variable's value representation */
506 		if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val)))
507 			return (error);
508 
509 		/* Add to path variable list */
510 		error = bhnd_nvram_plist_append_val(merged, name, val);
511 		bhnd_nvram_val_release(val);
512 		if (error)
513 			return (error);
514 	}
515 
516 	return (0);
517 }
518 
519 /**
520  * Find a free alias value for @p path, and append the devpathXX alias
521  * declaration to @p plist.
522  *
523  * @param	sc		The NVRAM store instance.
524  * @param	path		The NVRAM path for which a devpath alias
525  *				variable should be produced.
526  * @param	devpath		The devpathXX path value for @p path.
527  * @param	plist		The property list to which @p path's devpath
528  *				variable will be appended.
529  * @param[out]	alias_val	On success, will be set to the alias value
530  *				allocated for @p path.
531  *
532  * @retval 0		success
533  * @retval ENOMEM	If allocation fails.
534  * @retval non-zero	If merging the variables defined in @p path otherwise
535  *			fails, a regular unix error code will be returned.
536  */
537 static int
538 bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc,
539     bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist,
540     u_long *alias_val)
541 {
542 	bhnd_nvstore_alias	*alias;
543 	char			*pathvar;
544 	int			 error;
545 
546 	*alias_val = 0;
547 
548 	/* Prefer alias value already reserved for this path. */
549 	alias = bhnd_nvstore_find_alias(sc, path->path_str);
550 	if (alias != NULL) {
551 		*alias_val = alias->alias;
552 
553 		/* Allocate devpathXX variable name */
554 		bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
555 		if (pathvar == NULL)
556 			return (ENOMEM);
557 
558 		/* Append alias variable to property list */
559 		error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
560 
561 		BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use",
562 		   * alias_val, path->path_str));
563 
564 		bhnd_nv_free(pathvar);
565 		return (error);
566 	}
567 
568 	/* Find the next free devpathXX alias entry */
569 	while (1) {
570 		/* Skip existing reserved alias values */
571 		while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) {
572 			if (*alias_val == ULONG_MAX)
573 				return (ENOMEM);
574 
575 			(*alias_val)++;
576 		}
577 
578 		/* Allocate devpathXX variable name */
579 		bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
580 		if (pathvar == NULL)
581 			return (ENOMEM);
582 
583 		/* If not in-use, we can terminate the search */
584 		if (!bhnd_nvram_plist_contains(plist, pathvar))
585 			break;
586 
587 		/* Keep searching */
588 		bhnd_nv_free(pathvar);
589 
590 		if (*alias_val == ULONG_MAX)
591 			return (ENOMEM);
592 
593 		(*alias_val)++;
594 	}
595 
596 	/* Append alias variable to property list */
597 	error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
598 
599 	bhnd_nv_free(pathvar);
600 	return (error);
601 }
602 
603 /**
604  * Export a single @p child path's properties, appending the result to @p plist.
605  *
606  * @param	sc		The NVRAM store instance.
607  * @param	top		The root NVRAM path being exported.
608  * @param	child		The NVRAM path to be exported.
609  * @param	plist		The property list to which @p child's exported
610  *				properties should be appended.
611  * @param	flags		Export flags. See BHND_NVSTORE_EXPORT_*.
612  *
613  * @retval 0		success
614  * @retval ENOMEM	If allocation fails.
615  * @retval non-zero	If merging the variables defined in @p path otherwise
616  *			fails, a regular unix error code will be returned.
617  */
618 static int
619 bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc,
620     bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist,
621     uint32_t flags)
622 {
623 	bhnd_nvram_plist	*path_vars;
624 	bhnd_nvram_prop		*prop;
625 	const char		*relpath;
626 	char			*prefix, *namebuf;
627 	size_t			 prefix_len, relpath_len;
628 	size_t			 namebuf_size, num_props;
629 	bool			 emit_compact_devpath;
630 	int			 error;
631 
632 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
633 
634 	prefix = NULL;
635 	num_props = 0;
636 	path_vars = NULL;
637 	namebuf = NULL;
638 
639 	/* Determine the path relative to the top-level path */
640 	relpath = bhnd_nvstore_parse_relpath(top->path_str, child->path_str);
641 	if (relpath == NULL) {
642 		/* Skip -- not a child of the root path */
643 		return (0);
644 	}
645 	relpath_len = strlen(relpath);
646 
647 	/* Skip sub-path if export of children was not requested,  */
648 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0)
649 		return (0);
650 
651 	/* Collect all variables to be included in the export */
652 	if ((path_vars = bhnd_nvram_plist_new()) == NULL)
653 		return (ENOMEM);
654 
655 	if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) {
656 		bhnd_nvram_plist_release(path_vars);
657 		return (error);
658 	}
659 
660 	/* Skip if no children are to be exported */
661 	if (bhnd_nvram_plist_count(path_vars) == 0) {
662 		bhnd_nvram_plist_release(path_vars);
663 		return (0);
664 	}
665 
666 	/* Determine appropriate device path encoding */
667 	emit_compact_devpath = false;
668 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) {
669 		/* Re-encode as compact (if non-empty path) */
670 		if (relpath_len > 0)
671 			emit_compact_devpath = true;
672 	} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) {
673 		/* Re-encode with fully expanded device path */
674 		emit_compact_devpath = false;
675 	} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
676 		/* Preserve existing encoding of this path */
677 		if (bhnd_nvstore_find_alias(sc, child->path_str) != NULL)
678 			emit_compact_devpath = true;
679 	} else {
680 		BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags);
681 		error = EINVAL;
682 		goto finished;
683 	}
684 
685 	/* Allocate variable device path prefix to use for all property names,
686 	 * and if using compact encoding, emit the devpathXX= variable */
687 	prefix = NULL;
688 	prefix_len = 0;
689 	if (emit_compact_devpath) {
690 		u_long	alias_val;
691 		int	len;
692 
693 		/* Reserve an alias value and append the devpathXX= variable to
694 		 * the property list */
695 		error = bhnd_nvstore_export_devpath_alias(sc, child, relpath,
696 		    plist, &alias_val);
697 		if (error)
698 			goto finished;
699 
700 		/* Allocate variable name prefix */
701 		len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val);
702 		if (prefix == NULL) {
703 			error = ENOMEM;
704 			goto finished;
705 		}
706 
707 		prefix_len = len;
708 	} else if (relpath_len > 0) {
709 		int len;
710 
711 		/* Allocate the variable name prefix, appending '/' to the
712 		 * relative path */
713 		len = bhnd_nv_asprintf(&prefix, "%s/", relpath);
714 		if (prefix == NULL) {
715 			error = ENOMEM;
716 			goto finished;
717 		}
718 
719 		prefix_len = len;
720 	}
721 
722 	/* If prefixing of variable names is required, allocate a name
723 	 * formatting buffer */
724 	namebuf_size = 0;
725 	if (prefix != NULL) {
726 		size_t	maxlen;
727 
728 		/* Find the maximum name length */
729 		maxlen = 0;
730 		prop = NULL;
731 		while ((prop = bhnd_nvram_plist_next(path_vars, prop))) {
732 			const char *name;
733 
734 			name = bhnd_nvram_prop_name(prop);
735 			maxlen = bhnd_nv_ummax(strlen(name), maxlen);
736 		}
737 
738 		/* Allocate name buffer (path-prefix + name + '\0') */
739 		namebuf_size = prefix_len + maxlen + 1;
740 		namebuf = bhnd_nv_malloc(namebuf_size);
741 		if (namebuf == NULL) {
742 			error = ENOMEM;
743 			goto finished;
744 		}
745 	}
746 
747 	/* Append all path variables to the export plist, prepending the
748 	 * device-path prefix to the variable names, if required */
749 	prop = NULL;
750 	while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) {
751 		const char *name;
752 
753 		/* Prepend device prefix to the variable name */
754 		name = bhnd_nvram_prop_name(prop);
755 		if (prefix != NULL) {
756 			int len;
757 
758 			/*
759 			 * Write prefixed variable name to our name buffer.
760 			 *
761 			 * We precalcuate the size when scanning all names
762 			 * above, so this should always succeed.
763 			 */
764 			len = snprintf(namebuf, namebuf_size, "%s%s", prefix,
765 			    name);
766 			if (len < 0 || (size_t)len >= namebuf_size)
767 				BHND_NV_PANIC("invalid max_name_len");
768 
769 			name = namebuf;
770 		}
771 
772 		/* Add property to export plist */
773 		error = bhnd_nvram_plist_append_val(plist, name,
774 		    bhnd_nvram_prop_val(prop));
775 		if (error)
776 			goto finished;
777 	}
778 
779 	/* Success */
780 	error = 0;
781 
782 finished:
783 	if (prefix != NULL)
784 		bhnd_nv_free(prefix);
785 
786 	if (namebuf != NULL)
787 		bhnd_nv_free(namebuf);
788 
789 	if (path_vars != NULL)
790 		bhnd_nvram_plist_release(path_vars);
791 
792 	return (error);
793 }
794 
795 /**
796  * Export a flat, ordered NVRAM property list representation of all NVRAM
797  * properties at @p path.
798  *
799  * @param	sc	The NVRAM store instance.
800  * @param	path	The NVRAM path to export, or NULL to select the root
801  *			path.
802  * @param[out]	cls	On success, will be set to the backing data class
803  *			of @p sc. If the data class is are not desired,
804  *			a NULL pointer may be provided.
805  * @param[out]	props	On success, will be set to a caller-owned property
806  *			list containing the exported properties. The caller is
807  *			responsible for releasing this value via
808  *			bhnd_nvram_plist_release().
809  * @param[out]	options	On success, will be set to a caller-owned property
810  *			list containing the current NVRAM serialization options
811  *			for @p sc. The caller is responsible for releasing this
812  *			value via bhnd_nvram_plist_release().
813  * @param	flags	Export flags. See BHND_NVSTORE_EXPORT_*.
814  *
815  * @retval 0		success
816  * @retval EINVAL	If @p flags is invalid.
817  * @retval ENOENT	The requested path was not found.
818  * @retval ENOMEM	If allocation fails.
819  * @retval non-zero	If export of  @p path otherwise fails, a regular unix
820  *			error code will be returned.
821  */
822 int
823 bhnd_nvram_store_export(struct bhnd_nvram_store *sc, const char *path,
824     bhnd_nvram_data_class **cls, bhnd_nvram_plist **props,
825     bhnd_nvram_plist **options, uint32_t flags)
826 {
827 	bhnd_nvram_plist	*unordered;
828 	bhnd_nvstore_path	*top;
829 	bhnd_nvram_prop		*prop;
830 	const char		*name;
831 	void			*cookiep;
832 	size_t			 num_dpath_flags;
833 	int			 error;
834 
835 	*props = NULL;
836 	unordered = NULL;
837 	num_dpath_flags = 0;
838 	if (options != NULL)
839 		*options = NULL;
840 
841 	/* Default to exporting root path */
842 	if (path == NULL)
843 		path = BHND_NVSTORE_ROOT_PATH;
844 
845 	/* Default to exporting all properties */
846 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) &&
847 	    !BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED))
848 	{
849 		flags |= BHND_NVSTORE_EXPORT_ALL_VARS;
850 	}
851 
852 	/* Default to preserving the current device path encoding */
853 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS) &&
854 	    !BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
855 	{
856 		flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS;
857 	}
858 
859 	/* Exactly one device path encoding flag must be set */
860 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS))
861 		num_dpath_flags++;
862 
863 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
864 		num_dpath_flags++;
865 
866 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS))
867 		num_dpath_flags++;
868 
869 	if (num_dpath_flags != 1)
870 		return (EINVAL);
871 
872 	/* If EXPORT_DELETED is set, EXPORT_UNCOMMITTED must be set too */
873 	if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED) &&
874 	    !BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED))
875 	{
876 		return (EINVAL);
877 	}
878 
879 	/* Lock internal state before querying paths/properties */
880 	BHND_NVSTORE_LOCK(sc);
881 
882 	/* Fetch referenced path */
883 	top = bhnd_nvstore_get_path(sc, path, strlen(path));
884 	if (top == NULL) {
885 		error = ENOENT;
886 		goto failed;
887 	}
888 
889 	/* Allocate new, empty property list */
890 	if ((unordered = bhnd_nvram_plist_new()) == NULL) {
891 		error = ENOMEM;
892 		goto failed;
893 	}
894 
895 	/* Export the top-level path first */
896 	error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags);
897 	if (error)
898 		goto failed;
899 
900 	/* Attempt to export any children of the root path */
901 	for (size_t i = 0; i < nitems(sc->paths); i++) {
902 		bhnd_nvstore_path *child;
903 
904 		LIST_FOREACH(child, &sc->paths[i], np_link) {
905 			/* Top-level path was already exported */
906 			if (child == top)
907 				continue;
908 
909 			error = bhnd_nvram_store_export_child(sc, top,
910 			    child, unordered, flags);
911 			if (error)
912 				goto failed;
913 		}
914 	}
915 
916 	/* If requested, provide the current class and serialization options */
917 	if (cls != NULL)
918 		*cls = bhnd_nvram_data_get_class(sc->data);
919 
920 	if (options != NULL)
921 		*options = bhnd_nvram_plist_retain(sc->data_opts);
922 
923 	/*
924 	 * If we're re-encoding device paths, don't bother preserving the
925 	 * existing NVRAM variable order; our variable names will not match
926 	 * the existing backing NVRAM data.
927 	 */
928 	if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
929 		*props = unordered;
930 		unordered = NULL;
931 
932 		goto finished;
933 	}
934 
935 	/*
936 	 * Re-order the flattened output to match the existing NVRAM variable
937 	 * ordering.
938 	 *
939 	 * We append all new variables at the end of the input; this should
940 	 * reduce the delta that needs to be written (e.g. to flash) when
941 	 * committing NVRAM updates, and should result in a serialization
942 	 * identical to the input serialization if uncommitted updates are
943 	 * excluded from the export.
944 	 */
945 	if ((*props = bhnd_nvram_plist_new()) == NULL) {
946 		error = ENOMEM;
947 		goto failed;
948 	}
949 
950 	/* Using the backing NVRAM data ordering to order all variables
951 	 * currently defined in the backing store */
952 	cookiep = NULL;
953 	while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
954 		prop = bhnd_nvram_plist_get_prop(unordered, name);
955 		if (prop == NULL)
956 			continue;
957 
958 		/* Append to ordered result */
959 		if ((error = bhnd_nvram_plist_append(*props, prop)))
960 			goto failed;
961 
962 		/* Remove from unordered list */
963 		bhnd_nvram_plist_remove(unordered, name);
964 	}
965 
966 	/* Any remaining variables are new, and should be appended to the
967 	 * end of the export list */
968 	prop = NULL;
969 	while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) {
970 		if ((error = bhnd_nvram_plist_append(*props, prop)))
971 			goto failed;
972 	}
973 
974 	/* Export complete */
975 finished:
976 	BHND_NVSTORE_UNLOCK(sc);
977 
978 	if (unordered != NULL)
979 		bhnd_nvram_plist_release(unordered);
980 
981 	return (0);
982 
983 failed:
984 	BHND_NVSTORE_UNLOCK(sc);
985 
986 	if (unordered != NULL)
987 		bhnd_nvram_plist_release(unordered);
988 
989 	if (options != NULL && *options != NULL)
990 		bhnd_nvram_plist_release(*options);
991 
992 	if (*props != NULL)
993 		bhnd_nvram_plist_release(*props);
994 
995 	return (error);
996 }
997 
998 /**
999  * Encode all NVRAM properties at @p path, using the @p store's current NVRAM
1000  * data format.
1001  *
1002  * @param	sc	The NVRAM store instance.
1003  * @param	path	The NVRAM path to export, or NULL to select the root
1004  *			path.
1005  * @param[out]	data	On success, will be set to the newly serialized value.
1006  *			The caller is responsible for freeing this value
1007  *			via bhnd_nvram_io_free().
1008  * @param	flags	Export flags. See BHND_NVSTORE_EXPORT_*.
1009  *
1010  * @retval 0		success
1011  * @retval EINVAL	If @p flags is invalid.
1012  * @retval ENOENT	The requested path was not found.
1013  * @retval ENOMEM	If allocation fails.
1014  * @retval non-zero	If serialization of  @p path otherwise fails, a regular
1015  *			unix error code will be returned.
1016  */
1017 int
1018 bhnd_nvram_store_serialize(struct bhnd_nvram_store *sc, const char *path,
1019    struct bhnd_nvram_io **data,  uint32_t flags)
1020 {
1021 	bhnd_nvram_plist	*props;
1022 	bhnd_nvram_plist	*options;
1023 	bhnd_nvram_data_class	*cls;
1024 	struct bhnd_nvram_io	*io;
1025 	void			*outp;
1026 	size_t			 olen;
1027 	int			 error;
1028 
1029 	props = NULL;
1030 	options = NULL;
1031 	io = NULL;
1032 
1033 	/* Perform requested export */
1034 	error = bhnd_nvram_store_export(sc, path, &cls, &props, &options,
1035 	    flags);
1036 	if (error)
1037 		return (error);
1038 
1039 	/* Determine serialized size */
1040 	error = bhnd_nvram_data_serialize(cls, props, options, NULL, &olen);
1041 	if (error)
1042 		goto failed;
1043 
1044 	/* Allocate output buffer */
1045 	if ((io = bhnd_nvram_iobuf_empty(olen, olen)) == NULL) {
1046 		error = ENOMEM;
1047 		goto failed;
1048 	}
1049 
1050 	/* Fetch write pointer */
1051 	if ((error = bhnd_nvram_io_write_ptr(io, 0, &outp, olen, NULL)))
1052 		goto failed;
1053 
1054 	/* Perform serialization */
1055 	error = bhnd_nvram_data_serialize(cls, props, options, outp, &olen);
1056 	if (error)
1057 		goto failed;
1058 
1059 	if ((error = bhnd_nvram_io_setsize(io, olen)))
1060 		goto failed;
1061 
1062 	/* Success */
1063 	bhnd_nvram_plist_release(props);
1064 	bhnd_nvram_plist_release(options);
1065 
1066 	*data = io;
1067 	return (0);
1068 
1069 failed:
1070 	if (props != NULL)
1071 		bhnd_nvram_plist_release(props);
1072 
1073 	if (options != NULL)
1074 		bhnd_nvram_plist_release(options);
1075 
1076 	if (io != NULL)
1077 		bhnd_nvram_io_free(io);
1078 
1079 	return (error);
1080 }
1081 
1082 /**
1083  * Read an NVRAM variable.
1084  *
1085  * @param		sc	The NVRAM parser state.
1086  * @param		name	The NVRAM variable name.
1087  * @param[out]		outp	On success, the requested value will be written
1088  *				to this buffer. This argment may be NULL if
1089  *				the value is not desired.
1090  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
1091  *				to the actual size of the requested value.
1092  * @param		otype	The requested data type to be written to
1093  *				@p outp.
1094  *
1095  * @retval 0		success
1096  * @retval ENOENT	The requested variable was not found.
1097  * @retval ENOMEM	If @p outp is non-NULL and a buffer of @p olen is too
1098  *			small to hold the requested value.
1099  * @retval non-zero	If reading @p name otherwise fails, a regular unix
1100  *			error code will be returned.
1101   */
1102 int
1103 bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
1104     void *outp, size_t *olen, bhnd_nvram_type otype)
1105 {
1106 	bhnd_nvstore_name_info	 info;
1107 	bhnd_nvstore_path	*path;
1108 	bhnd_nvram_prop		*prop;
1109 	void			*cookiep;
1110 	int			 error;
1111 
1112 	BHND_NVSTORE_LOCK(sc);
1113 
1114 	/* Parse the variable name */
1115 	error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
1116 	    sc->data_caps, &info);
1117 	if (error)
1118 		goto finished;
1119 
1120 	/* Fetch the variable's enclosing path entry */
1121 	if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) {
1122 		error = ENOENT;
1123 		goto finished;
1124 	}
1125 
1126 	/* Search uncommitted updates first */
1127 	prop = bhnd_nvstore_path_get_update(sc, path, info.name);
1128 	if (prop != NULL) {
1129 		if (bhnd_nvram_prop_is_null(prop)) {
1130 			/* NULL denotes a pending deletion */
1131 			error = ENOENT;
1132 		} else {
1133 			error = bhnd_nvram_prop_encode(prop, outp, olen, otype);
1134 		}
1135 		goto finished;
1136 	}
1137 
1138 	/* Search the backing NVRAM data */
1139 	cookiep = bhnd_nvstore_path_data_lookup(sc, path, info.name);
1140 	if (cookiep != NULL) {
1141 		/* Found in backing store */
1142 		error = bhnd_nvram_data_getvar(sc->data, cookiep, outp, olen,
1143 		     otype);
1144 		goto finished;
1145 	}
1146 
1147 	/* Not found */
1148 	error = ENOENT;
1149 
1150 finished:
1151 	BHND_NVSTORE_UNLOCK(sc);
1152 	return (error);
1153 }
1154 
1155 /**
1156  * Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar()
1157  * implementation.
1158  *
1159  * If @p value is NULL, the variable will be marked for deletion.
1160  */
1161 static int
1162 bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name,
1163     bhnd_nvram_val *value)
1164 {
1165 	bhnd_nvstore_path	*path;
1166 	bhnd_nvstore_name_info	 info;
1167 	int			 error;
1168 
1169 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
1170 
1171 	/* Parse the variable name */
1172 	error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
1173 	    sc->data_caps, &info);
1174 	if (error)
1175 		return (error);
1176 
1177 	/* Fetch the variable's enclosing path entry */
1178 	if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL)
1179 		return (error);
1180 
1181 	/* Register the update entry */
1182 	return (bhnd_nvstore_path_register_update(sc, path, info.name, value));
1183 }
1184 
1185 /**
1186  * Set an NVRAM variable.
1187  *
1188  * @param	sc	The NVRAM parser state.
1189  * @param	name	The NVRAM variable name.
1190  * @param	value	The new value.
1191  *
1192  * @retval 0		success
1193  * @retval ENOENT	The requested variable @p name was not found.
1194  * @retval EINVAL	If @p value is invalid.
1195  */
1196 int
1197 bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name,
1198     bhnd_nvram_val *value)
1199 {
1200 	int error;
1201 
1202 	BHND_NVSTORE_LOCK(sc);
1203 	error = bhnd_nvram_store_setval_common(sc, name, value);
1204 	BHND_NVSTORE_UNLOCK(sc);
1205 
1206 	return (error);
1207 }
1208 
1209 /**
1210  * Set an NVRAM variable.
1211  *
1212  * @param		sc	The NVRAM parser state.
1213  * @param		name	The NVRAM variable name.
1214  * @param[out]		inp	The new value.
1215  * @param[in,out]	ilen	The size of @p inp.
1216  * @param		itype	The data type of @p inp.
1217  *
1218  * @retval 0		success
1219  * @retval ENOENT	The requested variable @p name was not found.
1220  * @retval EINVAL	If the new value is invalid.
1221  * @retval EINVAL	If @p name is read-only.
1222  */
1223 int
1224 bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
1225     const void *inp, size_t ilen, bhnd_nvram_type itype)
1226 {
1227 	bhnd_nvram_val	val;
1228 	int		error;
1229 
1230 	error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
1231 	    BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA);
1232 	if (error) {
1233 		BHND_NV_LOG("error initializing value: %d\n", error);
1234 		return (EINVAL);
1235 	}
1236 
1237 	BHND_NVSTORE_LOCK(sc);
1238 	error = bhnd_nvram_store_setval_common(sc, name, &val);
1239 	BHND_NVSTORE_UNLOCK(sc);
1240 
1241 	bhnd_nvram_val_release(&val);
1242 
1243 	return (error);
1244 }
1245 
1246 /**
1247  * Unset an NVRAM variable.
1248  *
1249  * @param		sc	The NVRAM parser state.
1250  * @param		name	The NVRAM variable name.
1251  *
1252  * @retval 0		success
1253  * @retval ENOENT	The requested variable @p name was not found.
1254  * @retval EINVAL	If @p name is read-only.
1255  */
1256 int
1257 bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name)
1258 {
1259 	int error;
1260 
1261 	BHND_NVSTORE_LOCK(sc);
1262 	error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL);
1263 	BHND_NVSTORE_UNLOCK(sc);
1264 
1265 	return (error);
1266 }
1267