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