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