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