]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c
Update libdialog to 1.3-20180621
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / nvram / bhnd_nvram_store_subr.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/queue.h>
36
37 #ifdef _KERNEL
38
39 #include <sys/ctype.h>
40 #include <sys/systm.h>
41
42 #include <machine/_inttypes.h>
43
44 #else /* !_KERNEL */
45
46 #include <ctype.h>
47 #include <errno.h>
48 #include <inttypes.h>
49 #include <stdbool.h>
50 #include <stdio.h>
51 #include <stdint.h>
52 #include <stdlib.h>
53 #include <string.h>
54
55 #endif /* _KERNEL */
56
57 #include "bhnd_nvram_private.h"
58 #include "bhnd_nvram_datavar.h"
59
60 #include "bhnd_nvram_storevar.h"
61
62 static int                       bhnd_nvstore_idx_cmp(void *ctx,
63                                      const void *lhs, const void *rhs);
64
65 /**
66  * Allocate and initialize a new path instance.
67  * 
68  * The caller is responsible for deallocating the instance via
69  * bhnd_nvstore_path_free().
70  * 
71  * @param       path_str        The path's canonical string representation.
72  * @param       path_len        The length of @p path_str.
73  * 
74  * @retval non-NULL     success
75  * @retval NULL         if allocation fails.
76  */
77 bhnd_nvstore_path *
78 bhnd_nvstore_path_new(const char *path_str, size_t path_len)
79 {
80         bhnd_nvstore_path *path;
81
82         /* Allocate new entry */
83         path = bhnd_nv_malloc(sizeof(*path));
84         if (path == NULL)
85                 return (NULL);
86
87         path->index = NULL;
88         path->num_vars = 0;
89
90         path->pending = bhnd_nvram_plist_new();
91         if (path->pending == NULL)
92                 goto failed;
93
94         path->path_str = bhnd_nv_strndup(path_str, path_len);
95         if (path->path_str == NULL)
96                 goto failed;
97
98         return (path);
99
100 failed:
101         if (path->pending != NULL)
102                 bhnd_nvram_plist_release(path->pending);
103
104         if (path->path_str != NULL)
105                 bhnd_nv_free(path->path_str);
106
107         bhnd_nv_free(path);
108
109         return (NULL);
110 }
111
112 /**
113  * Free an NVRAM path instance, releasing all associated resources.
114  */
115 void
116 bhnd_nvstore_path_free(struct bhnd_nvstore_path *path)
117 {
118         /* Free the per-path index */
119         if (path->index != NULL)
120                 bhnd_nvstore_index_free(path->index);
121
122         bhnd_nvram_plist_release(path->pending);
123         bhnd_nv_free(path->path_str);
124         bhnd_nv_free(path);
125 }
126
127 /**
128  * Allocate and initialize a new index instance with @p capacity.
129  * 
130  * The caller is responsible for deallocating the instance via
131  * bhnd_nvstore_index_free().
132  * 
133  * @param       capacity        The maximum number of variables to be indexed.
134  * 
135  * @retval non-NULL     success
136  * @retval NULL         if allocation fails.
137  */
138 bhnd_nvstore_index *
139 bhnd_nvstore_index_new(size_t capacity)
140 {
141         bhnd_nvstore_index      *index;
142         size_t                   bytes;
143
144         /* Allocate and populate variable index */
145         bytes = sizeof(struct bhnd_nvstore_index) + (sizeof(void *) * capacity);
146         index = bhnd_nv_malloc(bytes);
147         if (index == NULL) {
148                 BHND_NV_LOG("error allocating %zu byte index\n", bytes);
149                 return (NULL);
150         }
151
152         index->count = 0;
153         index->capacity = capacity;
154
155         return (index);
156 }
157
158 /**
159  * Free an index instance, releasing all associated resources.
160  * 
161  * @param       index   An index instance previously allocated via
162  *                      bhnd_nvstore_index_new().
163  */
164 void
165 bhnd_nvstore_index_free(bhnd_nvstore_index *index)
166 {
167         bhnd_nv_free(index);
168 }
169
170 /**
171  * Append a new NVRAM variable's @p cookiep value to @p index.
172  * 
173  * After one or more append requests, the index must be prepared via
174  * bhnd_nvstore_index_prepare() before any indexed lookups are performed.
175  *
176  * @param       sc      The NVRAM store from which NVRAM values will be queried.
177  * @param       index   The index to be modified.
178  * @param       cookiep The cookiep value (as provided by the backing NVRAM
179  *                      data instance of @p sc) to be included in @p index.
180  * 
181  * @retval 0            success
182  * @retval ENOMEM       if appending an additional entry would exceed the
183  *                      capacity of @p index.
184  */
185 int
186 bhnd_nvstore_index_append(struct bhnd_nvram_store *sc,
187     bhnd_nvstore_index *index, void *cookiep)
188 {
189         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
190
191         if (index->count >= index->capacity)
192                 return (ENOMEM);
193
194         index->cookiep[index->count] = cookiep;
195         index->count++;
196         return (0);
197 }
198
199 /* sort function for bhnd_nvstore_index_prepare() */
200 static int
201 bhnd_nvstore_idx_cmp(void *ctx, const void *lhs, const void *rhs)
202 {
203         struct bhnd_nvram_store *sc;
204         void                    *l_cookiep, *r_cookiep;
205         const char              *l_str, *r_str;
206         const char              *l_name, *r_name;
207         int                      order;
208
209         sc = ctx;
210         l_cookiep = *(void * const *)lhs;
211         r_cookiep = *(void * const *)rhs;
212
213         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
214
215         /* Fetch string pointers from the cookiep values */
216         l_str = bhnd_nvram_data_getvar_name(sc->data, l_cookiep);
217         r_str = bhnd_nvram_data_getvar_name(sc->data, r_cookiep);
218
219         /* Trim device path prefixes */
220         if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {
221                 l_name = bhnd_nvram_trim_path_name(l_str);
222                 r_name = bhnd_nvram_trim_path_name(r_str);
223         } else {
224                 l_name = l_str;
225                 r_name = r_str;
226         }
227
228         /* Perform comparison */
229         order = strcmp(l_name, r_name);
230         if (order != 0 || lhs == rhs)
231                 return (order);
232
233         /* If the backing data incorrectly contains variables with duplicate
234          * names, we need a sort order that provides stable behavior.
235          * 
236          * Since Broadcom's own code varies wildly on this question, we just
237          * use a simple precedence rule: The first declaration of a variable
238          * takes precedence. */
239         return (bhnd_nvram_data_getvar_order(sc->data, l_cookiep, r_cookiep));
240 }
241
242 /**
243  * Prepare @p index for querying via bhnd_nvstore_index_lookup().
244  * 
245  * After one or more append requests, the index must be prepared via
246  * bhnd_nvstore_index_prepare() before any indexed lookups are performed.
247  *
248  * @param       sc      The NVRAM store from which NVRAM values will be queried.
249  * @param       index   The index to be prepared.
250  * 
251  * @retval 0            success
252  * @retval non-zero     if preparing @p index otherwise fails, a regular unix
253  *                      error code will be returned.
254  */
255 int
256 bhnd_nvstore_index_prepare(struct bhnd_nvram_store *sc,
257     bhnd_nvstore_index *index)
258 {
259         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
260
261         /* Sort the index table */
262         qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]), sc,
263             bhnd_nvstore_idx_cmp);
264
265         return (0);
266 }
267
268 /**
269  * Return a borrowed reference to the root path node.
270  * 
271  * @param       sc      The NVRAM store.
272  */
273 bhnd_nvstore_path *
274 bhnd_nvstore_get_root_path(struct bhnd_nvram_store *sc)
275 {
276         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
277         return (sc->root_path);
278 }
279
280 /**
281  * Return true if @p path is the root path node.
282  * 
283  * @param       sc      The NVRAM store.
284  * @param       path    The path to query.
285  */
286 bool
287 bhnd_nvstore_is_root_path(struct bhnd_nvram_store *sc, bhnd_nvstore_path *path)
288 {
289         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
290         return (sc->root_path == path);
291 }
292
293 /**
294  * Return the update entry matching @p name in @p path, or NULL if no entry
295  * found.
296  * 
297  * @param sc    The NVRAM store.
298  * @param path  The path to query.
299  * @param name  The NVRAM variable name to search for in @p path's update list.
300  * 
301  * @retval non-NULL     success
302  * @retval NULL         if @p name is not found in @p path.
303  */
304 bhnd_nvram_prop *
305 bhnd_nvstore_path_get_update(struct bhnd_nvram_store *sc,
306     bhnd_nvstore_path *path, const char *name)
307 {
308         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
309         return (bhnd_nvram_plist_get_prop(path->pending, name));
310 }
311
312 /**
313  * Register or remove an update record for @p name in @p path.
314  * 
315  * @param sc    The NVRAM store.
316  * @param path  The path to be modified.
317  * @param name  The path-relative variable name to be modified.
318  * @param value The new value. A value of BHND_NVRAM_TYPE_NULL denotes deletion.
319  * 
320  * @retval 0            success
321  * @retval ENOMEM       if allocation fails.
322  * @retval ENOENT       if @p name is unknown.
323  * @retval EINVAL       if @p value is NULL, and deletion of @p is not
324  *                      supported.
325  * @retval EINVAL       if @p value cannot be converted to a supported value
326  *                      type.
327  */
328 int
329 bhnd_nvstore_path_register_update(struct bhnd_nvram_store *sc,
330     bhnd_nvstore_path *path, const char *name, bhnd_nvram_val *value)
331 {
332         bhnd_nvram_val          *prop_val;
333         const char              *full_name;
334         void                    *cookiep;
335         char                    *namebuf;
336         int                      error;
337         bool                     nvram_committed;
338
339         namebuf = NULL;
340         prop_val = NULL;
341
342         /* Determine whether the variable is currently defined in the
343          * backing NVRAM data, and derive its full path-prefixed name */
344         nvram_committed = false;
345         cookiep = bhnd_nvstore_path_data_lookup(sc, path, name);
346         if (cookiep != NULL) {
347                 /* Variable is defined in the backing data */
348                 nvram_committed = true;
349
350                 /* Use the existing variable name */
351                 full_name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
352         } else if (path == sc->root_path) {
353                 /* No prefix required for root path */
354                 full_name = name;
355         } else {
356                 bhnd_nvstore_alias      *alias;
357                 int                      len;
358
359                 /* New variable is being set; we need to determine the
360                  * appropriate path prefix */
361                 alias = bhnd_nvstore_find_alias(sc, path->path_str);
362                 if (alias != NULL) {
363                         /* Use <alias>:name */
364                         len = bhnd_nv_asprintf(&namebuf, "%lu:%s", alias->alias,
365                             name);
366                 } else {
367                         /* Use path/name */
368                         len = bhnd_nv_asprintf(&namebuf, "%s/%s",
369                             path->path_str, name);
370                 }
371
372                 if (len < 0)
373                         return (ENOMEM);
374
375                 full_name = namebuf;
376         }
377
378         /* Allow the data store to filter the NVRAM operation */
379         if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
380                 error = bhnd_nvram_data_filter_unsetvar(sc->data, full_name);
381                 if (error) {
382                         BHND_NV_LOG("cannot unset property %s: %d\n", full_name,
383                             error);
384                         goto cleanup;
385                 }
386
387                 if ((prop_val = bhnd_nvram_val_copy(value)) == NULL) {
388                         error = ENOMEM;
389                         goto cleanup;
390                 }
391         } else {
392                 error = bhnd_nvram_data_filter_setvar(sc->data, full_name,
393                     value,  &prop_val);
394                 if (error) {
395                         BHND_NV_LOG("cannot set property %s: %d\n", full_name,
396                             error);
397                         goto cleanup;
398                 }
399         }
400
401         /* Add relative variable name to the per-path update list */
402         if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL &&
403             !nvram_committed)
404         {
405                 /* This is a deletion request for a variable not defined in
406                  * out backing store; we can simply remove the corresponding
407                  * update entry. */
408                 bhnd_nvram_plist_remove(path->pending, name);
409         } else {
410                 /* Update or append a pending update entry */
411                 error = bhnd_nvram_plist_replace_val(path->pending, name,
412                     prop_val);
413                 if (error)
414                         goto cleanup;
415         }
416
417         /* Success */
418         error = 0;
419
420 cleanup:
421         if (namebuf != NULL)
422                 bhnd_nv_free(namebuf);
423
424         if (prop_val != NULL)
425                 bhnd_nvram_val_release(prop_val);
426
427         return (error);
428 }
429
430 /**
431  * Iterate over all variable cookiep values retrievable from the backing
432  * data store in @p path.
433  * 
434  * @warning Pending updates in @p path are ignored by this function.
435  *
436  * @param               sc      The NVRAM store.
437  * @param               path    The NVRAM path to be iterated.
438  * @param[in,out]       indexp  A pointer to an opaque indexp value previously
439  *                              returned by bhnd_nvstore_path_data_next(), or a
440  *                              NULL value to begin iteration.
441  *
442  * @return Returns the next variable name, or NULL if there are no more
443  * variables defined in @p path.
444  */
445 void *
446 bhnd_nvstore_path_data_next(struct bhnd_nvram_store *sc,
447      bhnd_nvstore_path *path, void **indexp)
448 {
449         void **index_ref;
450
451         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
452
453         /* No index */
454         if (path->index == NULL) {
455                 /* An index is required for all non-empty, non-root path
456                  * instances */
457                 BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path),
458                     ("missing index for non-root path %s", path->path_str));
459
460                 /* Iterate NVRAM data directly, using the NVRAM data's cookiep
461                  * value as our indexp context */
462                 if ((bhnd_nvram_data_next(sc->data, indexp)) == NULL)
463                         return (NULL);
464
465                 return (*indexp);
466         }
467
468         /* Empty index */
469         if (path->index->count == 0)
470                 return (NULL);
471
472         if (*indexp == NULL) {
473                 /* First index entry */
474                 index_ref = &path->index->cookiep[0];
475         } else {
476                 size_t idxpos;
477
478                 /* Advance to next index entry */
479                 index_ref = *indexp;
480                 index_ref++;
481
482                 /* Hit end of index? */
483                 BHND_NV_ASSERT(index_ref > path->index->cookiep,
484                     ("invalid indexp"));
485
486                 idxpos = (index_ref - path->index->cookiep);
487                 if (idxpos >= path->index->count)
488                         return (NULL);
489         }
490
491         /* Provide new index position */
492         *indexp = index_ref;
493
494         /* Return the data's cookiep value */
495         return (*index_ref);
496 }
497
498 /**
499  * Perform an lookup of @p name in the backing NVRAM data for @p path,
500  * returning the associated cookiep value, or NULL if the variable is not found
501  * in the backing NVRAM data.
502  * 
503  * @warning Pending updates in @p path are ignored by this function.
504  * 
505  * @param       sc      The NVRAM store from which NVRAM values will be queried.
506  * @param       path    The path to be queried.
507  * @param       name    The variable name to be queried.
508  * 
509  * @retval non-NULL     success
510  * @retval NULL         if @p name is not found in @p index.
511  */
512 void *
513 bhnd_nvstore_path_data_lookup(struct bhnd_nvram_store *sc,
514     bhnd_nvstore_path *path, const char *name)
515 {
516         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
517
518         /* No index */
519         if (path->index == NULL) {
520                 /* An index is required for all non-empty, non-root path
521                  * instances */
522                 BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path),
523                     ("missing index for non-root path %s", path->path_str));
524
525                 /* Look up directly in NVRAM data */
526                 return (bhnd_nvram_data_find(sc->data, name));
527         }
528
529         /* Otherwise, delegate to an index-based lookup */
530         return (bhnd_nvstore_index_lookup(sc, path->index, name));
531 }
532
533 /**
534  * Perform an index lookup of @p name, returning the associated cookiep
535  * value, or NULL if the variable does not exist.
536  * 
537  * @param       sc      The NVRAM store from which NVRAM values will be queried.
538  * @param       index   The index to be queried.
539  * @param       name    The variable name to be queried.
540  * 
541  * @retval non-NULL     success
542  * @retval NULL         if @p name is not found in @p index.
543  */
544 void *
545 bhnd_nvstore_index_lookup(struct bhnd_nvram_store *sc,
546     bhnd_nvstore_index *index, const char *name)
547 {
548         void            *cookiep;
549         const char      *indexed_name;
550         size_t           min, mid, max;
551         uint32_t         data_caps;
552         int              order;
553
554         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
555         BHND_NV_ASSERT(index != NULL, ("NULL index"));
556
557         /*
558          * Locate the requested variable using a binary search.
559          */
560         if (index->count == 0)
561                 return (NULL);
562
563         data_caps = sc->data_caps;
564         min = 0;
565         max = index->count - 1;
566
567         while (max >= min) {
568                 /* Select midpoint */
569                 mid = (min + max) / 2;
570                 cookiep = index->cookiep[mid];
571
572                 /* Fetch variable name */
573                 indexed_name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
574
575                 /* Trim any path prefix */
576                 if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)
577                         indexed_name = bhnd_nvram_trim_path_name(indexed_name);
578
579                 /* Determine which side of the partition to search */
580                 order = strcmp(indexed_name, name);
581                 if (order < 0) {
582                         /* Search upper partition */
583                         min = mid + 1;
584                 } else if (order > 0) {
585                         /* Search (non-empty) lower partition */
586                         if (mid == 0)
587                                 break;
588                         max = mid - 1;
589                 } else if (order == 0) {
590                         size_t  idx;
591
592                         /*
593                          * Match found.
594                          * 
595                          * If this happens to be a key with multiple definitions
596                          * in the backing store, we need to find the entry with
597                          * the highest declaration precedence.
598                          * 
599                          * Duplicates are sorted in order of descending
600                          * precedence; to find the highest precedence entry,
601                          * we search backwards through the index.
602                          */
603                         idx = mid;
604                         while (idx > 0) {
605                                 void            *dup_cookiep;
606                                 const char      *dup_name;
607
608                                 /* Fetch preceding index entry */
609                                 idx--;
610                                 dup_cookiep = index->cookiep[idx];
611                                 dup_name = bhnd_nvram_data_getvar_name(sc->data,
612                                     dup_cookiep);
613
614                                 /* Trim any path prefix */
615                                 if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {
616                                         dup_name = bhnd_nvram_trim_path_name(
617                                             dup_name);
618                                 }
619
620                                 /* If no match, current cookiep is the variable
621                                  * definition with the highest precedence */
622                                 if (strcmp(indexed_name, dup_name) != 0)
623                                         return (cookiep);
624
625                                 /* Otherwise, prefer this earlier definition,
626                                  * and keep searching for a higher-precedence
627                                  * definitions */
628                                 cookiep = dup_cookiep;
629                         }
630
631                         return (cookiep);
632                 }
633         }
634
635         /* Not found */
636         return (NULL);
637 }
638
639 /**
640  * Return the device path entry registered for @p path, if any.
641  * 
642  * @param       sc              The NVRAM store to be queried.
643  * @param       path            The device path to search for.
644  * @param       path_len        The length of @p path.
645  *
646  * @retval non-NULL     if found.
647  * @retval NULL         if not found.
648  */
649 bhnd_nvstore_path *
650 bhnd_nvstore_get_path(struct bhnd_nvram_store *sc, const char *path,
651     size_t path_len)
652 {
653         bhnd_nvstore_path_list  *plist;
654         bhnd_nvstore_path       *p;
655         uint32_t                 h;
656
657         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
658
659         /* Use hash lookup */
660         h = hash32_strn(path, path_len, HASHINIT);
661         plist = &sc->paths[h % nitems(sc->paths)];
662
663         LIST_FOREACH(p, plist, np_link) {
664                 /* Check for prefix match */
665                 if (strncmp(p->path_str, path, path_len) != 0)
666                         continue;
667
668                 /* Check for complete match */
669                 if (strnlen(path, path_len) != strlen(p->path_str))
670                         continue;
671
672                 return (p);
673         }
674
675         /* Not found */
676         return (NULL);
677 }
678
679 /**
680  * Resolve @p aval to its corresponding device path entry, if any.
681  * 
682  * @param       sc              The NVRAM store to be queried.
683  * @param       aval            The device path alias value to search for.
684  *
685  * @retval non-NULL     if found.
686  * @retval NULL         if not found.
687  */
688 bhnd_nvstore_path *
689 bhnd_nvstore_resolve_path_alias(struct bhnd_nvram_store *sc, u_long aval)
690 {
691         bhnd_nvstore_alias *alias;
692
693         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
694
695         /* Fetch alias entry */
696         if ((alias = bhnd_nvstore_get_alias(sc, aval)) == NULL)
697                 return (NULL);
698
699         return (alias->path);
700 }
701
702 /**
703  * Register a device path entry for the path referenced by variable name
704  * @p info, if any.
705  *
706  * @param       sc              The NVRAM store to be updated.
707  * @param       info            The NVRAM variable name info.
708  * @param       cookiep         The NVRAM variable's cookiep value.
709  *
710  * @retval 0            if the path was successfully registered, or an identical
711  *                      path or alias entry exists.
712  * @retval EEXIST       if a conflicting entry already exists for the path or
713  *                      alias referenced by @p info.
714  * @retval ENOENT       if @p info contains a dangling alias reference.
715  * @retval EINVAL       if @p info contains an unsupported bhnd_nvstore_var_type
716  *                      and bhnd_nvstore_path_type combination.
717  * @retval ENOMEM       if allocation fails.
718  */
719 int
720 bhnd_nvstore_var_register_path(struct bhnd_nvram_store *sc,
721     bhnd_nvstore_name_info *info, void *cookiep)
722 {
723         switch (info->type) {
724         case BHND_NVSTORE_VAR:
725                 /* Variable */
726                 switch (info->path_type) {
727                 case BHND_NVSTORE_PATH_STRING:
728                         /* Variable contains a full path string
729                          * (pci/1/1/varname); register the path */
730                         return (bhnd_nvstore_register_path(sc,
731                             info->path.str.value, info->path.str.value_len));
732
733                 case BHND_NVSTORE_PATH_ALIAS:
734                         /* Variable contains an alias reference (0:varname).
735                          * There's no path to register */
736                         return (0);
737                 }
738
739                 BHND_NV_PANIC("unsupported path type %d", info->path_type);
740                 break;
741
742         case BHND_NVSTORE_ALIAS_DECL:
743                 /* Alias declaration */
744                 return (bhnd_nvstore_register_alias(sc, info, cookiep));
745         }
746
747         BHND_NV_PANIC("unsupported var type %d", info->type);
748 }
749
750 /**
751  * Resolve the device path entry referenced by @p info.
752  *
753  * @param       sc              The NVRAM store to be updated.
754  * @param       info            Variable name information descriptor containing
755  *                              the path or path alias to be resolved.
756  *
757  * @retval non-NULL     if found.
758  * @retval NULL         if not found.
759  */
760 bhnd_nvstore_path *
761 bhnd_nvstore_var_get_path(struct bhnd_nvram_store *sc,
762     bhnd_nvstore_name_info *info)
763 {
764         switch (info->path_type) {
765         case BHND_NVSTORE_PATH_STRING:
766                 return (bhnd_nvstore_get_path(sc, info->path.str.value,
767                     info->path.str.value_len));
768         case BHND_NVSTORE_PATH_ALIAS:
769                 return (bhnd_nvstore_resolve_path_alias(sc,
770                     info->path.alias.value));
771         }
772
773         BHND_NV_PANIC("unsupported path type %d", info->path_type);
774 }
775
776 /**
777  * Return the device path alias entry registered for @p alias_val, if any.
778  * 
779  * @param       sc              The NVRAM store to be queried.
780  * @param       alias_val       The alias value to search for.
781  *
782  * @retval non-NULL     if found.
783  * @retval NULL         if not found.
784  */
785 bhnd_nvstore_alias *
786 bhnd_nvstore_get_alias(struct bhnd_nvram_store *sc, u_long alias_val)
787 {
788         bhnd_nvstore_alias_list *alist;
789         bhnd_nvstore_alias      *alias;
790
791         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
792
793         /* Can use hash lookup */
794         alist = &sc->aliases[alias_val % nitems(sc->aliases)];
795         LIST_FOREACH(alias, alist, na_link) {
796                 if (alias->alias == alias_val)
797                         return (alias);                 
798         }
799
800         /* Not found */
801         return (NULL);
802 }
803
804 /**
805  * Return the device path alias entry registered for @p path, if any.
806  * 
807  * @param       sc      The NVRAM store to be queried.
808  * @param       path    The alias path to search for.
809  *
810  * @retval non-NULL     if found.
811  * @retval NULL         if not found.
812  */
813 bhnd_nvstore_alias *
814 bhnd_nvstore_find_alias(struct bhnd_nvram_store *sc, const char *path)
815 {
816         bhnd_nvstore_alias *alias;
817
818         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
819
820         /* Have to scan the full table */
821         for (size_t i = 0; i < nitems(sc->aliases); i++) {
822                 LIST_FOREACH(alias, &sc->aliases[i], na_link) {
823                         if (strcmp(alias->path->path_str, path) == 0)
824                                 return (alias);                 
825                 }
826         }
827
828         /* Not found */
829         return (NULL);
830 }
831
832 /**
833  * Register a device path entry for @p path.
834  * 
835  * @param       sc              The NVRAM store to be updated.
836  * @param       path_str        The absolute device path string.
837  * @param       path_len        The length of @p path_str.
838  * 
839  * @retval 0            if the path was successfully registered, or an identical
840  *                      path/alias entry already exists.
841  * @retval ENOMEM       if allocation fails.
842  */
843 int
844 bhnd_nvstore_register_path(struct bhnd_nvram_store *sc, const char *path_str,
845     size_t path_len)
846 {
847         bhnd_nvstore_path_list  *plist;
848         bhnd_nvstore_path       *path;
849         uint32_t                 h;
850
851         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
852
853         /* Already exists? */
854         if (bhnd_nvstore_get_path(sc, path_str, path_len) != NULL)
855                 return (0);
856
857         /* Can't represent more than SIZE_MAX paths */
858         if (sc->num_paths == SIZE_MAX)
859                 return (ENOMEM);
860
861         /* Allocate new entry */
862         path = bhnd_nvstore_path_new(path_str, path_len);
863         if (path == NULL)
864                 return (ENOMEM);
865
866         /* Insert in path hash table */
867         h = hash32_str(path->path_str, HASHINIT);
868         plist = &sc->paths[h % nitems(sc->paths)];
869         LIST_INSERT_HEAD(plist, path, np_link);
870
871         /* Increment path count */
872         sc->num_paths++;
873
874         return (0);
875 }
876
877 /**
878  * Register a device path alias for an NVRAM 'devpathX' variable.
879  * 
880  * The path value for the alias will be fetched from the backing NVRAM data.
881  * 
882  * @param       sc      The NVRAM store to be updated.
883  * @param       info    The NVRAM variable name info.
884  * @param       cookiep The NVRAM variable's cookiep value.
885  * 
886  * @retval 0            if the alias was successfully registered, or an
887  *                      identical alias entry exists.
888  * @retval EEXIST       if a conflicting alias or path entry already exists.
889  * @retval EINVAL       if @p info is not a BHND_NVSTORE_ALIAS_DECL or does
890  *                      not contain a BHND_NVSTORE_PATH_ALIAS entry.
891  * @retval ENOMEM       if allocation fails.
892  */
893 int
894 bhnd_nvstore_register_alias(struct bhnd_nvram_store *sc,
895     const bhnd_nvstore_name_info *info, void *cookiep)
896 {
897         bhnd_nvstore_alias_list *alist;
898         bhnd_nvstore_alias      *alias;
899         bhnd_nvstore_path       *path;
900         char                    *path_str;
901         size_t                   path_len;
902         int                      error;
903
904         BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
905
906         path_str = NULL;
907         alias = NULL;
908
909         /* Can't represent more than SIZE_MAX aliases */
910         if (sc->num_aliases == SIZE_MAX)
911                 return (ENOMEM);
912
913         /* Must be an alias declaration */
914         if (info->type != BHND_NVSTORE_ALIAS_DECL)
915                 return (EINVAL);
916
917         if (info->path_type != BHND_NVSTORE_PATH_ALIAS)
918                 return (EINVAL);
919
920         /* Fetch the devpath variable's value length */
921         error = bhnd_nvram_data_getvar(sc->data, cookiep, NULL, &path_len,
922             BHND_NVRAM_TYPE_STRING);
923         if (error)
924                 return (ENOMEM);
925
926         /* Allocate path string buffer */
927         if ((path_str = bhnd_nv_malloc(path_len)) == NULL)
928                 return (ENOMEM);
929
930         /* Decode to our new buffer */
931         error = bhnd_nvram_data_getvar(sc->data, cookiep, path_str, &path_len,
932             BHND_NVRAM_TYPE_STRING);
933         if (error)
934                 goto failed;
935
936         /* Trim trailing '/' character(s) from the path length */
937         path_len = strnlen(path_str, path_len);
938         while (path_len > 0 && path_str[path_len-1] == '/') {
939                 path_str[path_len-1] = '\0';
940                 path_len--;
941         }
942
943         /* Is a conflicting alias entry already registered for this alias
944          * value? */
945         alias = bhnd_nvstore_get_alias(sc, info->path.alias.value);
946         if (alias != NULL) {
947                 if (alias->cookiep != cookiep ||
948                     strcmp(alias->path->path_str, path_str) != 0)
949                 {
950                         error = EEXIST;
951                         goto failed;
952                 }
953         }
954
955         /* Is a conflicting entry already registered for the alias path? */
956         if ((alias = bhnd_nvstore_find_alias(sc, path_str)) != NULL) {
957                 if (alias->alias != info->path.alias.value ||
958                     alias->cookiep != cookiep ||
959                     strcmp(alias->path->path_str, path_str) != 0)
960                 {
961                         error = EEXIST;
962                         goto failed;
963                 }
964         }
965
966         /* Get (or register) the target path entry */
967         path = bhnd_nvstore_get_path(sc, path_str, path_len);
968         if (path == NULL) {
969                 error = bhnd_nvstore_register_path(sc, path_str, path_len);
970                 if (error)
971                         goto failed;
972
973                 path = bhnd_nvstore_get_path(sc, path_str, path_len);
974                 BHND_NV_ASSERT(path != NULL, ("missing registered path"));
975         }
976
977         /* Allocate alias entry */
978         alias = bhnd_nv_calloc(1, sizeof(*alias));
979         if (alias == NULL) {
980                 error = ENOMEM;
981                 goto failed;
982         }
983
984         alias->path = path;
985         alias->cookiep = cookiep;
986         alias->alias = info->path.alias.value;
987
988         /* Insert in alias hash table */
989         alist = &sc->aliases[alias->alias % nitems(sc->aliases)];
990         LIST_INSERT_HEAD(alist, alias, na_link);
991
992         /* Increment alias count */
993         sc->num_aliases++;
994
995         bhnd_nv_free(path_str);
996         return (0);
997
998 failed:
999         if (path_str != NULL)
1000                 bhnd_nv_free(path_str);
1001
1002         if (alias != NULL)
1003                 bhnd_nv_free(alias);
1004
1005         return (error);
1006 }
1007
1008 /**
1009  * If @p child is equal to or a child path of @p parent, return a pointer to
1010  * @p child's path component(s) relative to @p parent; otherwise, return NULL.
1011  */
1012 const char *
1013 bhnd_nvstore_parse_relpath(const char *parent, const char *child)
1014 {
1015         size_t prefix_len;
1016
1017         /* All paths have an implicit leading '/'; this allows us to treat
1018          * our manufactured root path of "/" as a prefix to all NVRAM-defined
1019          * paths (which do not necessarily include a leading '/' */
1020         if (*parent == '/')
1021                 parent++;
1022
1023         if (*child == '/')
1024                 child++;
1025
1026         /* Is parent a prefix of child? */
1027         prefix_len = strlen(parent);
1028         if (strncmp(parent, child, prefix_len) != 0)
1029                 return (NULL);
1030
1031         /* A zero-length prefix matches everything */
1032         if (prefix_len == 0)
1033                 return (child);
1034
1035         /* Is child equal to parent? */
1036         if (child[prefix_len] == '\0')
1037                 return (child + prefix_len);
1038
1039         /* Is child actually a child of parent? */
1040         if (child[prefix_len] == '/')
1041                 return (child + prefix_len + 1);
1042
1043         /* No match (e.g. parent=/foo..., child=/fooo...) */
1044         return (NULL);
1045 }
1046
1047 /**
1048  * Parse a raw NVRAM variable name and return its @p entry_type, its
1049  * type-specific @p prefix (e.g. '0:', 'pci/1/1', 'devpath'), and its
1050  * type-specific @p suffix (e.g. 'varname', '0').
1051  * 
1052  * @param       name            The NVRAM variable name to be parsed. This
1053  *                              value must remain valid for the lifetime of
1054  *                              @p info.
1055  * @param       type            The NVRAM name type -- either INTERNAL for names
1056  *                              parsed from backing NVRAM data, or EXTERNAL for
1057  *                              names provided by external NVRAM store clients.
1058  * @param       data_caps       The backing NVRAM data capabilities
1059  *                              (see bhnd_nvram_data_caps()).
1060  * @param[out]  info            On success, the parsed variable name info.
1061  * 
1062  * @retval 0            success
1063  * @retval non-zero     if parsing @p name otherwise fails, a regular unix
1064  *                      error code will be returned.
1065  */
1066 int
1067 bhnd_nvstore_parse_name_info(const char *name, bhnd_nvstore_name_type type,
1068     uint32_t data_caps, bhnd_nvstore_name_info *info)
1069 {
1070         const char      *p;
1071         char            *endp;
1072
1073         /* Skip path parsing? */
1074         if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) {
1075                 /* devpath declaration? (devpath0=pci/1/1) */
1076                 if (strncmp(name, "devpath", strlen("devpath")) == 0) {
1077                         u_long alias;
1078
1079                         /* Perform standard validation on the relative
1080                          * variable name */
1081                         if (type != BHND_NVSTORE_NAME_INTERNAL &&
1082                             !bhnd_nvram_validate_name(name))
1083                         {
1084                                 return (ENOENT);
1085                         }
1086
1087                         /* Parse alias value that should follow a 'devpath'
1088                          * prefix */
1089                         p = name + strlen("devpath");
1090                         alias = strtoul(p, &endp, 10);
1091                         if (endp != p && *endp == '\0') {
1092                                 info->type = BHND_NVSTORE_ALIAS_DECL;
1093                                 info->path_type = BHND_NVSTORE_PATH_ALIAS;
1094                                 info->name = name;
1095                                 info->path.alias.value = alias;
1096
1097                                 return (0);
1098                         }
1099                 }
1100
1101                 /* device aliased variable? (0:varname) */
1102                 if (bhnd_nv_isdigit(*name)) {
1103                         u_long alias;
1104
1105                         /* Parse '0:' alias prefix */
1106                         alias = strtoul(name, &endp, 10);
1107                         if (endp != name && *endp == ':') {
1108                                 /* Perform standard validation on the relative
1109                                  * variable name */
1110                                 if (type != BHND_NVSTORE_NAME_INTERNAL &&
1111                                     !bhnd_nvram_validate_name(name))
1112                                 {
1113                                         return (ENOENT);
1114                                 }
1115
1116                                 info->type = BHND_NVSTORE_VAR;
1117                                 info->path_type = BHND_NVSTORE_PATH_ALIAS;
1118
1119                                 /* name follows 0: prefix */
1120                                 info->name = endp + 1;
1121                                 info->path.alias.value = alias;
1122
1123                                 return (0);
1124                         }
1125                 }
1126
1127                 /* device variable? (pci/1/1/varname) */
1128                 if ((p = strrchr(name, '/')) != NULL) {
1129                         const char      *path, *relative_name;
1130                         size_t           path_len;
1131
1132                         /* Determine the path length; 'p' points at the last
1133                          * path separator in 'name' */
1134                         path_len = p - name;
1135                         path = name;
1136
1137                         /* The relative variable name directly follows the
1138                          * final path separator '/' */
1139                         relative_name = path + path_len + 1;
1140
1141                         /* Now that we calculated the name offset, exclude all
1142                          * trailing '/' characters from the path length */
1143                         while (path_len > 0 && path[path_len-1] == '/')
1144                                 path_len--;
1145
1146                         /* Perform standard validation on the relative
1147                          * variable name */
1148                         if (type != BHND_NVSTORE_NAME_INTERNAL &&
1149                             !bhnd_nvram_validate_name(relative_name))
1150                         {
1151                                 return (ENOENT);
1152                         }
1153
1154                         /* Initialize result with pointers into the name
1155                          * buffer */
1156                         info->type = BHND_NVSTORE_VAR;
1157                         info->path_type = BHND_NVSTORE_PATH_STRING;
1158                         info->name = relative_name;
1159                         info->path.str.value = path;
1160                         info->path.str.value_len = path_len;
1161
1162                         return (0);
1163                 }
1164         }
1165
1166         /* If all other parsing fails, the result is a simple variable with
1167          * an implicit path of "/" */
1168         if (type != BHND_NVSTORE_NAME_INTERNAL &&
1169             !bhnd_nvram_validate_name(name))
1170         {
1171                 /* Invalid relative name */
1172                 return (ENOENT);
1173         }
1174
1175         info->type = BHND_NVSTORE_VAR;
1176         info->path_type = BHND_NVSTORE_PATH_STRING;
1177         info->name = name;
1178         info->path.str.value = BHND_NVSTORE_ROOT_PATH;
1179         info->path.str.value_len = BHND_NVSTORE_ROOT_PATH_LEN;
1180
1181         return (0);
1182 }