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