1 /*-
2 * Copyright (c) 2015-2016 Landon Fuller <[email protected]>
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 *
bhnd_nvstore_path_new(const char * path_str,size_t path_len)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
bhnd_nvstore_path_free(struct bhnd_nvstore_path * path)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 *
bhnd_nvstore_index_new(size_t capacity)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
bhnd_nvstore_index_free(bhnd_nvstore_index * index)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
bhnd_nvstore_index_append(struct bhnd_nvram_store * sc,bhnd_nvstore_index * index,void * cookiep)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
bhnd_nvstore_idx_cmp(void * ctx,const void * lhs,const void * rhs)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
bhnd_nvstore_index_prepare(struct bhnd_nvram_store * sc,bhnd_nvstore_index * index)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 *
bhnd_nvstore_get_root_path(struct bhnd_nvram_store * sc)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
bhnd_nvstore_is_root_path(struct bhnd_nvram_store * sc,bhnd_nvstore_path * path)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 *
bhnd_nvstore_path_get_update(struct bhnd_nvram_store * sc,bhnd_nvstore_path * path,const char * name)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
bhnd_nvstore_path_register_update(struct bhnd_nvram_store * sc,bhnd_nvstore_path * path,const char * name,bhnd_nvram_val * value)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 *
bhnd_nvstore_path_data_next(struct bhnd_nvram_store * sc,bhnd_nvstore_path * path,void ** indexp)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 *
bhnd_nvstore_path_data_lookup(struct bhnd_nvram_store * sc,bhnd_nvstore_path * path,const char * name)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 *
bhnd_nvstore_index_lookup(struct bhnd_nvram_store * sc,bhnd_nvstore_index * index,const char * name)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 *
bhnd_nvstore_get_path(struct bhnd_nvram_store * sc,const char * path,size_t path_len)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 *
bhnd_nvstore_resolve_path_alias(struct bhnd_nvram_store * sc,u_long aval)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
bhnd_nvstore_var_register_path(struct bhnd_nvram_store * sc,bhnd_nvstore_name_info * info,void * cookiep)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 *
bhnd_nvstore_var_get_path(struct bhnd_nvram_store * sc,bhnd_nvstore_name_info * info)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 *
bhnd_nvstore_get_alias(struct bhnd_nvram_store * sc,u_long alias_val)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 *
bhnd_nvstore_find_alias(struct bhnd_nvram_store * sc,const char * path)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
bhnd_nvstore_register_path(struct bhnd_nvram_store * sc,const char * path_str,size_t path_len)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
bhnd_nvstore_register_alias(struct bhnd_nvram_store * sc,const bhnd_nvstore_name_info * info,void * cookiep)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 *
bhnd_nvstore_parse_relpath(const char * parent,const char * child)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
bhnd_nvstore_parse_name_info(const char * name,bhnd_nvstore_name_type type,uint32_t data_caps,bhnd_nvstore_name_info * info)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 }
1183