Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c
39536 views
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/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 *
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
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 *
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
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
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
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
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 *
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
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 *
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
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 *
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 *
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 *
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 *
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 *
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
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 *
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 *
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 *
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
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
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 *
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
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
1180