2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
35 #include <sys/limits.h>
36 #include <sys/queue.h>
40 #include <sys/ctype.h>
41 #include <sys/systm.h>
43 #include <machine/_inttypes.h>
58 #include "bhnd_nvram_private.h"
59 #include "bhnd_nvram_datavar.h"
61 #include "bhnd_nvram_storevar.h"
66 * Manages in-memory and persistent representations of NVRAM data.
69 static int bhnd_nvstore_parse_data(
70 struct bhnd_nvram_store *sc);
72 static int bhnd_nvstore_parse_path_entries(
73 struct bhnd_nvram_store *sc);
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,
82 static int bhnd_nvstore_export_merge(
83 struct bhnd_nvram_store *sc,
84 bhnd_nvstore_path *path,
85 bhnd_nvram_plist *merged,
88 static int bhnd_nvstore_export_devpath_alias(
89 struct bhnd_nvram_store *sc,
90 bhnd_nvstore_path *path,
92 bhnd_nvram_plist *plist,
96 * Allocate and initialize a new NVRAM data store instance.
98 * The caller is responsible for deallocating the instance via
99 * bhnd_nvram_store_free().
101 * @param[out] store On success, a pointer to the newly allocated NVRAM data
103 * @param data The NVRAM data to be managed by the returned NVRAM data store
107 * @retval non-zero if an error occurs during allocation or initialization, a
108 * regular unix error code will be returned.
111 bhnd_nvram_store_new(struct bhnd_nvram_store **store,
112 struct bhnd_nvram_data *data)
114 struct bhnd_nvram_store *sc;
117 /* Allocate new instance */
118 sc = bhnd_nv_calloc(1, sizeof(*sc));
122 BHND_NVSTORE_LOCK_INIT(sc);
123 BHND_NVSTORE_LOCK(sc);
125 /* Initialize path hash table */
127 for (size_t i = 0; i < nitems(sc->paths); i++)
128 LIST_INIT(&sc->paths[i]);
130 /* Initialize alias hash table */
132 for (size_t i = 0; i < nitems(sc->aliases); i++)
133 LIST_INIT(&sc->aliases[i]);
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);
142 sc->data_opts = bhnd_nvram_plist_new();
143 if (sc->data_opts == NULL) {
149 /* Register required root path */
150 error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH,
151 BHND_NVSTORE_ROOT_PATH_LEN);
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"));
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)))
166 BHND_NVSTORE_UNLOCK(sc);
170 BHND_NVSTORE_UNLOCK(sc);
171 bhnd_nvram_store_free(sc);
176 * Allocate and initialize a new NVRAM data store instance, parsing the
177 * NVRAM data from @p io.
179 * The caller is responsible for deallocating the instance via
180 * bhnd_nvram_store_free().
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.
185 * @param[out] store On success, a pointer to the newly allocated NVRAM data
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.
192 * @retval non-zero if an error occurs during allocation or initialization, a
193 * regular unix error code will be returned.
196 bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
197 struct bhnd_nvram_io *io, bhnd_nvram_data_class *cls)
199 struct bhnd_nvram_data *data;
203 /* Try to parse the data */
204 if ((error = bhnd_nvram_data_new(cls, &data, io)))
207 /* Try to create our new store instance */
208 error = bhnd_nvram_store_new(store, data);
209 bhnd_nvram_data_release(data);
215 * Free an NVRAM store instance, releasing all associated resources.
217 * @param sc A store instance previously allocated via
218 * bhnd_nvram_store_new().
221 bhnd_nvram_store_free(struct bhnd_nvram_store *sc)
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)
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);
238 if (sc->data != NULL)
239 bhnd_nvram_data_release(sc->data);
241 if (sc->data_opts != NULL)
242 bhnd_nvram_plist_release(sc->data_opts);
244 BHND_NVSTORE_LOCK_DESTROY(sc);
249 * Parse all variables vended by our backing NVRAM data instance,
250 * generating all path entries, alias entries, and variable indexes.
252 * @param sc The NVRAM store instance to be initialized with
253 * paths, aliases, and data parsed from its backing
257 * @retval non-zero if an error occurs during parsing, a regular unix error
258 * code will be returned.
261 bhnd_nvstore_parse_data(struct bhnd_nvram_store *sc)
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)))
273 /* Calculate the per-path variable counts, and report dangling alias
274 * references as an error. */
276 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
277 bhnd_nvstore_path *path;
278 bhnd_nvstore_name_info info;
280 /* Parse the name info */
281 error = bhnd_nvstore_parse_name_info(name,
282 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
287 case BHND_NVSTORE_VAR:
288 /* Fetch referenced path */
289 path = bhnd_nvstore_var_get_path(sc, &info);
291 BHND_NV_LOG("variable '%s' has dangling "
292 "path reference\n", name);
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);
305 case BHND_NVSTORE_ALIAS_DECL:
306 /* Skip -- path alias already parsed and recorded */
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
314 if (sc->num_paths == 1) {
315 bhnd_nvstore_path *path;
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)
322 /* If the sole root path contains fewer variables than the
323 * minimum indexing threshhold, we do not need to generate an
325 path = bhnd_nvstore_get_root_path(sc);
326 if (path->num_vars < BHND_NV_IDX_VAR_THRESHOLD)
330 /* Allocate per-path index instances */
331 for (size_t i = 0; i < nitems(sc->paths); i++) {
332 bhnd_nvstore_path *path;
334 LIST_FOREACH(path, &sc->paths[i], np_link) {
335 path->index = bhnd_nvstore_index_new(path->num_vars);
336 if (path->index == NULL)
341 /* Populate per-path indexes */
343 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
344 bhnd_nvstore_name_info info;
345 bhnd_nvstore_path *path;
347 /* Parse the name info */
348 error = bhnd_nvstore_parse_name_info(name,
349 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
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"));
360 /* Append to index */
361 error = bhnd_nvstore_index_append(sc, path->index,
367 case BHND_NVSTORE_ALIAS_DECL:
373 /* Prepare indexes for querying */
374 for (size_t i = 0; i < nitems(sc->paths); i++) {
375 bhnd_nvstore_path *path;
377 LIST_FOREACH(path, &sc->paths[i], np_link) {
378 error = bhnd_nvstore_index_prepare(sc, path->index);
389 * Parse and register path and path alias entries for all declarations found in
390 * the NVRAM data backing @p nvram.
392 * @param sc The NVRAM store instance.
395 * @retval non-zero If parsing fails, a regular unix error code will be
399 bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc)
405 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
407 /* Skip path registration if the data source does not support device
409 if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) {
410 BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path"));
414 /* Otherwise, parse and register all paths and path aliases */
416 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
417 bhnd_nvstore_name_info info;
419 /* Parse the name info */
420 error = bhnd_nvstore_parse_name_info(name,
421 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
425 /* Register the path */
426 error = bhnd_nvstore_var_register_path(sc, &info, cookiep);
428 BHND_NV_LOG("failed to register path for %s: %d\n",
439 * Merge exported per-path variables (uncommitted, committed, or both) into
440 * the empty @p merged property list.
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_*.
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.
453 bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc,
454 bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags)
456 void *cookiep, *idxp;
459 /* Populate merged list with all pending variables */
460 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
461 bhnd_nvram_prop *prop;
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))
471 /* Append to merged list */
472 error = bhnd_nvram_plist_append(merged, prop);
478 /* Skip merging committed variables? */
479 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED))
482 /* Merge in the committed NVRAM variables */
484 while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) {
488 /* Fetch the variable name */
489 name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
491 /* Trim device path prefix */
492 if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)
493 name = bhnd_nvram_trim_path_name(name);
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))
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))
508 /* Fetch the variable's value representation */
509 if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val)))
512 /* Add to path variable list */
513 error = bhnd_nvram_plist_append_val(merged, name, val);
514 bhnd_nvram_val_release(val);
523 * Find a free alias value for @p path, and append the devpathXX alias
524 * declaration to @p plist.
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.
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.
541 bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc,
542 bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist,
545 bhnd_nvstore_alias *alias;
551 /* Prefer alias value already reserved for this path. */
552 alias = bhnd_nvstore_find_alias(sc, path->path_str);
554 *alias_val = alias->alias;
556 /* Allocate devpathXX variable name */
557 bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
561 /* Append alias variable to property list */
562 error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
564 BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use",
565 * alias_val, path->path_str));
567 bhnd_nv_free(pathvar);
571 /* Find the next free devpathXX alias entry */
573 /* Skip existing reserved alias values */
574 while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) {
575 if (*alias_val == ULONG_MAX)
581 /* Allocate devpathXX variable name */
582 bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
586 /* If not in-use, we can terminate the search */
587 if (!bhnd_nvram_plist_contains(plist, pathvar))
591 bhnd_nv_free(pathvar);
593 if (*alias_val == ULONG_MAX)
599 /* Append alias variable to property list */
600 error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
602 bhnd_nv_free(pathvar);
607 * Export a single @p child path's properties, appending the result to @p plist.
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_*.
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.
622 bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc,
623 bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist,
626 bhnd_nvram_plist *path_vars;
627 bhnd_nvram_prop *prop;
629 char *prefix, *namebuf;
630 size_t prefix_len, relpath_len;
631 size_t namebuf_size, num_props;
632 bool emit_compact_devpath;
635 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
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 */
648 relpath_len = strlen(relpath);
650 /* Skip sub-path if export of children was not requested, */
651 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0)
654 /* Collect all variables to be included in the export */
655 if ((path_vars = bhnd_nvram_plist_new()) == NULL)
658 if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) {
659 bhnd_nvram_plist_release(path_vars);
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);
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) */
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;
683 BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags);
688 /* Allocate variable device path prefix to use for all property names,
689 * and if using compact encoding, emit the devpathXX= variable */
692 if (emit_compact_devpath) {
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,
703 /* Allocate variable name prefix */
704 len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val);
705 if (prefix == NULL) {
711 } else if (relpath_len > 0) {
714 /* Allocate the variable name prefix, appending '/' to the
716 len = bhnd_nv_asprintf(&prefix, "%s/", relpath);
717 if (prefix == NULL) {
725 /* If prefixing of variable names is required, allocate a name
726 * formatting buffer */
728 if (prefix != NULL) {
731 /* Find the maximum name length */
734 while ((prop = bhnd_nvram_plist_next(path_vars, prop))) {
737 name = bhnd_nvram_prop_name(prop);
738 maxlen = bhnd_nv_ummax(strlen(name), maxlen);
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) {
750 /* Append all path variables to the export plist, prepending the
751 * device-path prefix to the variable names, if required */
753 while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) {
756 /* Prepend device prefix to the variable name */
757 name = bhnd_nvram_prop_name(prop);
758 if (prefix != NULL) {
762 * Write prefixed variable name to our name buffer.
764 * We precalcuate the size when scanning all names
765 * above, so this should always succeed.
767 len = snprintf(namebuf, namebuf_size, "%s%s", prefix,
769 if (len < 0 || (size_t)len >= namebuf_size)
770 BHND_NV_PANIC("invalid max_name_len");
775 /* Add property to export plist */
776 error = bhnd_nvram_plist_append_val(plist, name,
777 bhnd_nvram_prop_val(prop));
787 bhnd_nv_free(prefix);
790 bhnd_nv_free(namebuf);
792 if (path_vars != NULL)
793 bhnd_nvram_plist_release(path_vars);
799 * Export a flat, ordered NVRAM property list representation of all NVRAM
800 * properties at @p path.
802 * @param sc The NVRAM store instance.
803 * @param path The NVRAM path to export, or NULL to select the root
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_*.
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.
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)
830 bhnd_nvram_plist *unordered;
831 bhnd_nvstore_path *top;
832 bhnd_nvram_prop *prop;
835 size_t num_dpath_flags;
844 /* Default to exporting root path */
846 path = BHND_NVSTORE_ROOT_PATH;
848 /* Default to exporting all properties */
849 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) &&
850 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED))
852 flags |= BHND_NVSTORE_EXPORT_ALL_VARS;
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))
859 flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS;
862 /* Exactly one device path encoding flag must be set */
863 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS))
866 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
869 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS))
872 if (num_dpath_flags != 1)
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))
882 /* Lock internal state before querying paths/properties */
883 BHND_NVSTORE_LOCK(sc);
885 /* Fetch referenced path */
886 top = bhnd_nvstore_get_path(sc, path, strlen(path));
892 /* Allocate new, empty property list */
893 if ((unordered = bhnd_nvram_plist_new()) == NULL) {
898 /* Export the top-level path first */
899 error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags);
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;
907 LIST_FOREACH(child, &sc->paths[i], np_link) {
908 /* Top-level path was already exported */
912 error = bhnd_nvram_store_export_child(sc, top,
913 child, unordered, flags);
919 /* If requested, provide the current class and serialization options */
921 *cls = bhnd_nvram_data_get_class(sc->data);
924 *options = bhnd_nvram_plist_retain(sc->data_opts);
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.
931 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
939 * Re-order the flattened output to match the existing NVRAM variable
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.
948 if ((*props = bhnd_nvram_plist_new()) == NULL) {
953 /* Using the backing NVRAM data ordering to order all variables
954 * currently defined in the backing store */
956 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
957 prop = bhnd_nvram_plist_get_prop(unordered, name);
961 /* Append to ordered result */
962 if ((error = bhnd_nvram_plist_append(*props, prop)))
965 /* Remove from unordered list */
966 bhnd_nvram_plist_remove(unordered, name);
969 /* Any remaining variables are new, and should be appended to the
970 * end of the export list */
972 while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) {
973 if ((error = bhnd_nvram_plist_append(*props, prop)))
977 /* Export complete */
979 BHND_NVSTORE_UNLOCK(sc);
981 if (unordered != NULL)
982 bhnd_nvram_plist_release(unordered);
987 BHND_NVSTORE_UNLOCK(sc);
989 if (unordered != NULL)
990 bhnd_nvram_plist_release(unordered);
992 if (options != NULL && *options != NULL)
993 bhnd_nvram_plist_release(*options);
996 bhnd_nvram_plist_release(*props);
1002 * Encode all NVRAM properties at @p path, using the @p store's current NVRAM
1005 * @param sc The NVRAM store instance.
1006 * @param path The NVRAM path to export, or NULL to select the root
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_*.
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.
1021 bhnd_nvram_store_serialize(struct bhnd_nvram_store *sc, const char *path,
1022 struct bhnd_nvram_io **data, uint32_t flags)
1024 bhnd_nvram_plist *props;
1025 bhnd_nvram_plist *options;
1026 bhnd_nvram_data_class *cls;
1027 struct bhnd_nvram_io *io;
1036 /* Perform requested export */
1037 error = bhnd_nvram_store_export(sc, path, &cls, &props, &options,
1042 /* Determine serialized size */
1043 error = bhnd_nvram_data_serialize(cls, props, options, NULL, &olen);
1047 /* Allocate output buffer */
1048 if ((io = bhnd_nvram_iobuf_empty(olen, olen)) == NULL) {
1053 /* Fetch write pointer */
1054 if ((error = bhnd_nvram_io_write_ptr(io, 0, &outp, olen, NULL)))
1057 /* Perform serialization */
1058 error = bhnd_nvram_data_serialize(cls, props, options, outp, &olen);
1062 if ((error = bhnd_nvram_io_setsize(io, olen)))
1066 bhnd_nvram_plist_release(props);
1067 bhnd_nvram_plist_release(options);
1074 bhnd_nvram_plist_release(props);
1076 if (options != NULL)
1077 bhnd_nvram_plist_release(options);
1080 bhnd_nvram_io_free(io);
1086 * Read an NVRAM variable.
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
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.
1106 bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
1107 void *outp, size_t *olen, bhnd_nvram_type otype)
1109 bhnd_nvstore_name_info info;
1110 bhnd_nvstore_path *path;
1111 bhnd_nvram_prop *prop;
1115 BHND_NVSTORE_LOCK(sc);
1117 /* Parse the variable name */
1118 error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
1119 sc->data_caps, &info);
1123 /* Fetch the variable's enclosing path entry */
1124 if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) {
1129 /* Search uncommitted updates first */
1130 prop = bhnd_nvstore_path_get_update(sc, path, info.name);
1132 if (bhnd_nvram_prop_is_null(prop)) {
1133 /* NULL denotes a pending deletion */
1136 error = bhnd_nvram_prop_encode(prop, outp, olen, otype);
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,
1154 BHND_NVSTORE_UNLOCK(sc);
1159 * Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar()
1162 * If @p value is NULL, the variable will be marked for deletion.
1165 bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name,
1166 bhnd_nvram_val *value)
1168 bhnd_nvstore_path *path;
1169 bhnd_nvstore_name_info info;
1172 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
1174 /* Parse the variable name */
1175 error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
1176 sc->data_caps, &info);
1180 /* Fetch the variable's enclosing path entry */
1181 if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL)
1184 /* Register the update entry */
1185 return (bhnd_nvstore_path_register_update(sc, path, info.name, value));
1189 * Set an NVRAM variable.
1191 * @param sc The NVRAM parser state.
1192 * @param name The NVRAM variable name.
1193 * @param value The new value.
1196 * @retval ENOENT The requested variable @p name was not found.
1197 * @retval EINVAL If @p value is invalid.
1200 bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name,
1201 bhnd_nvram_val *value)
1205 BHND_NVSTORE_LOCK(sc);
1206 error = bhnd_nvram_store_setval_common(sc, name, value);
1207 BHND_NVSTORE_UNLOCK(sc);
1213 * Set an NVRAM variable.
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.
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.
1227 bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
1228 const void *inp, size_t ilen, bhnd_nvram_type itype)
1233 error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
1234 BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA);
1236 BHND_NV_LOG("error initializing value: %d\n", error);
1240 BHND_NVSTORE_LOCK(sc);
1241 error = bhnd_nvram_store_setval_common(sc, name, &val);
1242 BHND_NVSTORE_UNLOCK(sc);
1244 bhnd_nvram_val_release(&val);
1250 * Unset an NVRAM variable.
1252 * @param sc The NVRAM parser state.
1253 * @param name The NVRAM variable name.
1256 * @retval ENOENT The requested variable @p name was not found.
1257 * @retval EINVAL If @p name is read-only.
1260 bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name)
1264 BHND_NVSTORE_LOCK(sc);
1265 error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL);
1266 BHND_NVSTORE_UNLOCK(sc);