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