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