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