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.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/limits.h>
33
#include <sys/queue.h>
34
35
#ifdef _KERNEL
36
37
#include <sys/ctype.h>
38
#include <sys/systm.h>
39
40
#include <machine/_inttypes.h>
41
42
#else /* !_KERNEL */
43
44
#include <ctype.h>
45
#include <errno.h>
46
#include <inttypes.h>
47
#include <stdbool.h>
48
#include <stdio.h>
49
#include <stdint.h>
50
#include <stdlib.h>
51
#include <string.h>
52
53
#endif /* _KERNEL */
54
55
#include "bhnd_nvram_private.h"
56
#include "bhnd_nvram_datavar.h"
57
58
#include "bhnd_nvram_storevar.h"
59
60
/*
61
* BHND NVRAM Store
62
*
63
* Manages in-memory and persistent representations of NVRAM data.
64
*/
65
66
static int bhnd_nvstore_parse_data(
67
struct bhnd_nvram_store *sc);
68
69
static int bhnd_nvstore_parse_path_entries(
70
struct bhnd_nvram_store *sc);
71
72
static int bhnd_nvram_store_export_child(
73
struct bhnd_nvram_store *sc,
74
bhnd_nvstore_path *top,
75
bhnd_nvstore_path *child,
76
bhnd_nvram_plist *plist,
77
uint32_t flags);
78
79
static int bhnd_nvstore_export_merge(
80
struct bhnd_nvram_store *sc,
81
bhnd_nvstore_path *path,
82
bhnd_nvram_plist *merged,
83
uint32_t flags);
84
85
static int bhnd_nvstore_export_devpath_alias(
86
struct bhnd_nvram_store *sc,
87
bhnd_nvstore_path *path,
88
const char *devpath,
89
bhnd_nvram_plist *plist,
90
u_long *alias_val);
91
92
/**
93
* Allocate and initialize a new NVRAM data store instance.
94
*
95
* The caller is responsible for deallocating the instance via
96
* bhnd_nvram_store_free().
97
*
98
* @param[out] store On success, a pointer to the newly allocated NVRAM data
99
* instance.
100
* @param data The NVRAM data to be managed by the returned NVRAM data store
101
* instance.
102
*
103
* @retval 0 success
104
* @retval non-zero if an error occurs during allocation or initialization, a
105
* regular unix error code will be returned.
106
*/
107
int
108
bhnd_nvram_store_new(struct bhnd_nvram_store **store,
109
struct bhnd_nvram_data *data)
110
{
111
struct bhnd_nvram_store *sc;
112
int error;
113
114
/* Allocate new instance */
115
sc = bhnd_nv_calloc(1, sizeof(*sc));
116
if (sc == NULL)
117
return (ENOMEM);
118
119
BHND_NVSTORE_LOCK_INIT(sc);
120
BHND_NVSTORE_LOCK(sc);
121
122
/* Initialize path hash table */
123
sc->num_paths = 0;
124
for (size_t i = 0; i < nitems(sc->paths); i++)
125
LIST_INIT(&sc->paths[i]);
126
127
/* Initialize alias hash table */
128
sc->num_aliases = 0;
129
for (size_t i = 0; i < nitems(sc->aliases); i++)
130
LIST_INIT(&sc->aliases[i]);
131
132
/* Retain the NVRAM data */
133
sc->data = bhnd_nvram_data_retain(data);
134
sc->data_caps = bhnd_nvram_data_caps(data);
135
sc->data_opts = bhnd_nvram_data_options(data);
136
if (sc->data_opts != NULL) {
137
bhnd_nvram_plist_retain(sc->data_opts);
138
} else {
139
sc->data_opts = bhnd_nvram_plist_new();
140
if (sc->data_opts == NULL) {
141
error = ENOMEM;
142
goto cleanup;
143
}
144
}
145
146
/* Register required root path */
147
error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH,
148
BHND_NVSTORE_ROOT_PATH_LEN);
149
if (error)
150
goto cleanup;
151
152
sc->root_path = bhnd_nvstore_get_path(sc, BHND_NVSTORE_ROOT_PATH,
153
BHND_NVSTORE_ROOT_PATH_LEN);
154
BHND_NV_ASSERT(sc->root_path, ("missing root path"));
155
156
/* Parse all variables vended by our backing NVRAM data instance,
157
* generating all path entries, alias entries, and variable indexes */
158
if ((error = bhnd_nvstore_parse_data(sc)))
159
goto cleanup;
160
161
*store = sc;
162
163
BHND_NVSTORE_UNLOCK(sc);
164
return (0);
165
166
cleanup:
167
BHND_NVSTORE_UNLOCK(sc);
168
bhnd_nvram_store_free(sc);
169
return (error);
170
}
171
172
/**
173
* Allocate and initialize a new NVRAM data store instance, parsing the
174
* NVRAM data from @p io.
175
*
176
* The caller is responsible for deallocating the instance via
177
* bhnd_nvram_store_free().
178
*
179
* The NVRAM data mapped by @p io will be copied, and @p io may be safely
180
* deallocated after bhnd_nvram_store_new() returns.
181
*
182
* @param[out] store On success, a pointer to the newly allocated NVRAM data
183
* instance.
184
* @param io An I/O context mapping the NVRAM data to be copied and parsed.
185
* @param cls The NVRAM data class to be used when parsing @p io, or NULL
186
* to perform runtime identification of the appropriate data class.
187
*
188
* @retval 0 success
189
* @retval non-zero if an error occurs during allocation or initialization, a
190
* regular unix error code will be returned.
191
*/
192
int
193
bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
194
struct bhnd_nvram_io *io, bhnd_nvram_data_class *cls)
195
{
196
struct bhnd_nvram_data *data;
197
int error;
198
199
/* Try to parse the data */
200
if ((error = bhnd_nvram_data_new(cls, &data, io)))
201
return (error);
202
203
/* Try to create our new store instance */
204
error = bhnd_nvram_store_new(store, data);
205
bhnd_nvram_data_release(data);
206
207
return (error);
208
}
209
210
/**
211
* Free an NVRAM store instance, releasing all associated resources.
212
*
213
* @param sc A store instance previously allocated via
214
* bhnd_nvram_store_new().
215
*/
216
void
217
bhnd_nvram_store_free(struct bhnd_nvram_store *sc)
218
{
219
220
/* Clean up alias hash table */
221
for (size_t i = 0; i < nitems(sc->aliases); i++) {
222
bhnd_nvstore_alias *alias, *anext;
223
LIST_FOREACH_SAFE(alias, &sc->aliases[i], na_link, anext)
224
bhnd_nv_free(alias);
225
}
226
227
/* Clean up path hash table */
228
for (size_t i = 0; i < nitems(sc->paths); i++) {
229
bhnd_nvstore_path *path, *pnext;
230
LIST_FOREACH_SAFE(path, &sc->paths[i], np_link, pnext)
231
bhnd_nvstore_path_free(path);
232
}
233
234
if (sc->data != NULL)
235
bhnd_nvram_data_release(sc->data);
236
237
if (sc->data_opts != NULL)
238
bhnd_nvram_plist_release(sc->data_opts);
239
240
BHND_NVSTORE_LOCK_DESTROY(sc);
241
bhnd_nv_free(sc);
242
}
243
244
/**
245
* Parse all variables vended by our backing NVRAM data instance,
246
* generating all path entries, alias entries, and variable indexes.
247
*
248
* @param sc The NVRAM store instance to be initialized with
249
* paths, aliases, and data parsed from its backing
250
* data.
251
*
252
* @retval 0 success
253
* @retval non-zero if an error occurs during parsing, a regular unix error
254
* code will be returned.
255
*/
256
static int
257
bhnd_nvstore_parse_data(struct bhnd_nvram_store *sc)
258
{
259
const char *name;
260
void *cookiep;
261
int error;
262
263
/* Parse and register all device paths and path aliases. This enables
264
* resolution of _forward_ references to device paths aliases when
265
* scanning variable entries below */
266
if ((error = bhnd_nvstore_parse_path_entries(sc)))
267
return (error);
268
269
/* Calculate the per-path variable counts, and report dangling alias
270
* references as an error. */
271
cookiep = NULL;
272
while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
273
bhnd_nvstore_path *path;
274
bhnd_nvstore_name_info info;
275
276
/* Parse the name info */
277
error = bhnd_nvstore_parse_name_info(name,
278
BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
279
if (error)
280
return (error);
281
282
switch (info.type) {
283
case BHND_NVSTORE_VAR:
284
/* Fetch referenced path */
285
path = bhnd_nvstore_var_get_path(sc, &info);
286
if (path == NULL) {
287
BHND_NV_LOG("variable '%s' has dangling "
288
"path reference\n", name);
289
return (EFTYPE);
290
}
291
292
/* Increment path variable count */
293
if (path->num_vars == SIZE_MAX) {
294
BHND_NV_LOG("more than SIZE_MAX variables in "
295
"path %s\n", path->path_str);
296
return (EFTYPE);
297
}
298
path->num_vars++;
299
break;
300
301
case BHND_NVSTORE_ALIAS_DECL:
302
/* Skip -- path alias already parsed and recorded */
303
break;
304
}
305
}
306
307
/* If the backing NVRAM data instance vends only a single root ("/")
308
* path, we may be able to skip generating an index for the root
309
* path */
310
if (sc->num_paths == 1) {
311
bhnd_nvstore_path *path;
312
313
/* If the backing instance provides its own name-based lookup
314
* indexing, we can skip generating a duplicate here */
315
if (sc->data_caps & BHND_NVRAM_DATA_CAP_INDEXED)
316
return (0);
317
318
/* If the sole root path contains fewer variables than the
319
* minimum indexing threshhold, we do not need to generate an
320
* index */
321
path = bhnd_nvstore_get_root_path(sc);
322
if (path->num_vars < BHND_NV_IDX_VAR_THRESHOLD)
323
return (0);
324
}
325
326
/* Allocate per-path index instances */
327
for (size_t i = 0; i < nitems(sc->paths); i++) {
328
bhnd_nvstore_path *path;
329
330
LIST_FOREACH(path, &sc->paths[i], np_link) {
331
path->index = bhnd_nvstore_index_new(path->num_vars);
332
if (path->index == NULL)
333
return (ENOMEM);
334
}
335
}
336
337
/* Populate per-path indexes */
338
cookiep = NULL;
339
while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
340
bhnd_nvstore_name_info info;
341
bhnd_nvstore_path *path;
342
343
/* Parse the name info */
344
error = bhnd_nvstore_parse_name_info(name,
345
BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
346
if (error)
347
return (error);
348
349
switch (info.type) {
350
case BHND_NVSTORE_VAR:
351
/* Fetch referenced path */
352
path = bhnd_nvstore_var_get_path(sc, &info);
353
BHND_NV_ASSERT(path != NULL,
354
("dangling path reference"));
355
356
/* Append to index */
357
error = bhnd_nvstore_index_append(sc, path->index,
358
cookiep);
359
if (error)
360
return (error);
361
break;
362
363
case BHND_NVSTORE_ALIAS_DECL:
364
/* Skip */
365
break;
366
}
367
}
368
369
/* Prepare indexes for querying */
370
for (size_t i = 0; i < nitems(sc->paths); i++) {
371
bhnd_nvstore_path *path;
372
373
LIST_FOREACH(path, &sc->paths[i], np_link) {
374
error = bhnd_nvstore_index_prepare(sc, path->index);
375
if (error)
376
return (error);
377
}
378
}
379
380
return (0);
381
}
382
383
/**
384
* Parse and register path and path alias entries for all declarations found in
385
* the NVRAM data backing @p nvram.
386
*
387
* @param sc The NVRAM store instance.
388
*
389
* @retval 0 success
390
* @retval non-zero If parsing fails, a regular unix error code will be
391
* returned.
392
*/
393
static int
394
bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc)
395
{
396
const char *name;
397
void *cookiep;
398
int error;
399
400
BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
401
402
/* Skip path registration if the data source does not support device
403
* paths. */
404
if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) {
405
BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path"));
406
return (0);
407
}
408
409
/* Otherwise, parse and register all paths and path aliases */
410
cookiep = NULL;
411
while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
412
bhnd_nvstore_name_info info;
413
414
/* Parse the name info */
415
error = bhnd_nvstore_parse_name_info(name,
416
BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
417
if (error)
418
return (error);
419
420
/* Register the path */
421
error = bhnd_nvstore_var_register_path(sc, &info, cookiep);
422
if (error) {
423
BHND_NV_LOG("failed to register path for %s: %d\n",
424
name, error);
425
return (error);
426
}
427
}
428
429
return (0);
430
}
431
432
/**
433
* Merge exported per-path variables (uncommitted, committed, or both) into
434
* the empty @p merged property list.
435
*
436
* @param sc The NVRAM store instance.
437
* @param path The NVRAM path to be exported.
438
* @param merged The property list to populate with the merged results.
439
* @param flags Export flags. See BHND_NVSTORE_EXPORT_*.
440
*
441
* @retval 0 success
442
* @retval ENOMEM If allocation fails.
443
* @retval non-zero If merging the variables defined in @p path otherwise
444
* fails, a regular unix error code will be returned.
445
*/
446
static int
447
bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc,
448
bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags)
449
{
450
void *cookiep, *idxp;
451
int error;
452
453
/* Populate merged list with all pending variables */
454
if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
455
bhnd_nvram_prop *prop;
456
457
prop = NULL;
458
while ((prop = bhnd_nvram_plist_next(path->pending, prop))) {
459
/* Skip variables marked for deletion */
460
if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) {
461
if (bhnd_nvram_prop_is_null(prop))
462
continue;
463
}
464
465
/* Append to merged list */
466
error = bhnd_nvram_plist_append(merged, prop);
467
if (error)
468
return (error);
469
}
470
}
471
472
/* Skip merging committed variables? */
473
if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED))
474
return (0);
475
476
/* Merge in the committed NVRAM variables */
477
idxp = NULL;
478
while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) {
479
const char *name;
480
bhnd_nvram_val *val;
481
482
/* Fetch the variable name */
483
name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
484
485
/* Trim device path prefix */
486
if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)
487
name = bhnd_nvram_trim_path_name(name);
488
489
/* Skip if already defined in pending updates */
490
if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
491
if (bhnd_nvram_plist_contains(path->pending, name))
492
continue;
493
}
494
495
/* Skip if higher precedence value was already defined. This
496
* may occur if the underlying data store contains duplicate
497
* keys; iteration will always return the definition with
498
* the highest precedence first */
499
if (bhnd_nvram_plist_contains(merged, name))
500
continue;
501
502
/* Fetch the variable's value representation */
503
if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val)))
504
return (error);
505
506
/* Add to path variable list */
507
error = bhnd_nvram_plist_append_val(merged, name, val);
508
bhnd_nvram_val_release(val);
509
if (error)
510
return (error);
511
}
512
513
return (0);
514
}
515
516
/**
517
* Find a free alias value for @p path, and append the devpathXX alias
518
* declaration to @p plist.
519
*
520
* @param sc The NVRAM store instance.
521
* @param path The NVRAM path for which a devpath alias
522
* variable should be produced.
523
* @param devpath The devpathXX path value for @p path.
524
* @param plist The property list to which @p path's devpath
525
* variable will be appended.
526
* @param[out] alias_val On success, will be set to the alias value
527
* allocated for @p path.
528
*
529
* @retval 0 success
530
* @retval ENOMEM If allocation fails.
531
* @retval non-zero If merging the variables defined in @p path otherwise
532
* fails, a regular unix error code will be returned.
533
*/
534
static int
535
bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc,
536
bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist,
537
u_long *alias_val)
538
{
539
bhnd_nvstore_alias *alias;
540
char *pathvar;
541
int error;
542
543
*alias_val = 0;
544
545
/* Prefer alias value already reserved for this path. */
546
alias = bhnd_nvstore_find_alias(sc, path->path_str);
547
if (alias != NULL) {
548
*alias_val = alias->alias;
549
550
/* Allocate devpathXX variable name */
551
bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
552
if (pathvar == NULL)
553
return (ENOMEM);
554
555
/* Append alias variable to property list */
556
error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
557
558
BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use",
559
* alias_val, path->path_str));
560
561
bhnd_nv_free(pathvar);
562
return (error);
563
}
564
565
/* Find the next free devpathXX alias entry */
566
while (1) {
567
/* Skip existing reserved alias values */
568
while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) {
569
if (*alias_val == ULONG_MAX)
570
return (ENOMEM);
571
572
(*alias_val)++;
573
}
574
575
/* Allocate devpathXX variable name */
576
bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
577
if (pathvar == NULL)
578
return (ENOMEM);
579
580
/* If not in-use, we can terminate the search */
581
if (!bhnd_nvram_plist_contains(plist, pathvar))
582
break;
583
584
/* Keep searching */
585
bhnd_nv_free(pathvar);
586
587
if (*alias_val == ULONG_MAX)
588
return (ENOMEM);
589
590
(*alias_val)++;
591
}
592
593
/* Append alias variable to property list */
594
error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
595
596
bhnd_nv_free(pathvar);
597
return (error);
598
}
599
600
/**
601
* Export a single @p child path's properties, appending the result to @p plist.
602
*
603
* @param sc The NVRAM store instance.
604
* @param top The root NVRAM path being exported.
605
* @param child The NVRAM path to be exported.
606
* @param plist The property list to which @p child's exported
607
* properties should be appended.
608
* @param flags Export flags. See BHND_NVSTORE_EXPORT_*.
609
*
610
* @retval 0 success
611
* @retval ENOMEM If allocation fails.
612
* @retval non-zero If merging the variables defined in @p path otherwise
613
* fails, a regular unix error code will be returned.
614
*/
615
static int
616
bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc,
617
bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist,
618
uint32_t flags)
619
{
620
bhnd_nvram_plist *path_vars;
621
bhnd_nvram_prop *prop;
622
const char *relpath;
623
char *prefix, *namebuf;
624
size_t prefix_len, relpath_len;
625
size_t namebuf_size;
626
bool emit_compact_devpath;
627
int error;
628
629
BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
630
631
prefix = NULL;
632
path_vars = NULL;
633
namebuf = NULL;
634
635
/* Determine the path relative to the top-level path */
636
relpath = bhnd_nvstore_parse_relpath(top->path_str, child->path_str);
637
if (relpath == NULL) {
638
/* Skip -- not a child of the root path */
639
return (0);
640
}
641
relpath_len = strlen(relpath);
642
643
/* Skip sub-path if export of children was not requested, */
644
if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0)
645
return (0);
646
647
/* Collect all variables to be included in the export */
648
if ((path_vars = bhnd_nvram_plist_new()) == NULL)
649
return (ENOMEM);
650
651
if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) {
652
bhnd_nvram_plist_release(path_vars);
653
return (error);
654
}
655
656
/* Skip if no children are to be exported */
657
if (bhnd_nvram_plist_count(path_vars) == 0) {
658
bhnd_nvram_plist_release(path_vars);
659
return (0);
660
}
661
662
/* Determine appropriate device path encoding */
663
emit_compact_devpath = false;
664
if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) {
665
/* Re-encode as compact (if non-empty path) */
666
if (relpath_len > 0)
667
emit_compact_devpath = true;
668
} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) {
669
/* Re-encode with fully expanded device path */
670
emit_compact_devpath = false;
671
} else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
672
/* Preserve existing encoding of this path */
673
if (bhnd_nvstore_find_alias(sc, child->path_str) != NULL)
674
emit_compact_devpath = true;
675
} else {
676
BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags);
677
error = EINVAL;
678
goto finished;
679
}
680
681
/* Allocate variable device path prefix to use for all property names,
682
* and if using compact encoding, emit the devpathXX= variable */
683
prefix = NULL;
684
prefix_len = 0;
685
if (emit_compact_devpath) {
686
u_long alias_val;
687
int len;
688
689
/* Reserve an alias value and append the devpathXX= variable to
690
* the property list */
691
error = bhnd_nvstore_export_devpath_alias(sc, child, relpath,
692
plist, &alias_val);
693
if (error)
694
goto finished;
695
696
/* Allocate variable name prefix */
697
len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val);
698
if (prefix == NULL) {
699
error = ENOMEM;
700
goto finished;
701
}
702
703
prefix_len = len;
704
} else if (relpath_len > 0) {
705
int len;
706
707
/* Allocate the variable name prefix, appending '/' to the
708
* relative path */
709
len = bhnd_nv_asprintf(&prefix, "%s/", relpath);
710
if (prefix == NULL) {
711
error = ENOMEM;
712
goto finished;
713
}
714
715
prefix_len = len;
716
}
717
718
/* If prefixing of variable names is required, allocate a name
719
* formatting buffer */
720
namebuf_size = 0;
721
if (prefix != NULL) {
722
size_t maxlen;
723
724
/* Find the maximum name length */
725
maxlen = 0;
726
prop = NULL;
727
while ((prop = bhnd_nvram_plist_next(path_vars, prop))) {
728
const char *name;
729
730
name = bhnd_nvram_prop_name(prop);
731
maxlen = bhnd_nv_ummax(strlen(name), maxlen);
732
}
733
734
/* Allocate name buffer (path-prefix + name + '\0') */
735
namebuf_size = prefix_len + maxlen + 1;
736
namebuf = bhnd_nv_malloc(namebuf_size);
737
if (namebuf == NULL) {
738
error = ENOMEM;
739
goto finished;
740
}
741
}
742
743
/* Append all path variables to the export plist, prepending the
744
* device-path prefix to the variable names, if required */
745
prop = NULL;
746
while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) {
747
const char *name;
748
749
/* Prepend device prefix to the variable name */
750
name = bhnd_nvram_prop_name(prop);
751
if (prefix != NULL) {
752
int len;
753
754
/*
755
* Write prefixed variable name to our name buffer.
756
*
757
* We precalcuate the size when scanning all names
758
* above, so this should always succeed.
759
*/
760
len = snprintf(namebuf, namebuf_size, "%s%s", prefix,
761
name);
762
if (len < 0 || (size_t)len >= namebuf_size)
763
BHND_NV_PANIC("invalid max_name_len");
764
765
name = namebuf;
766
}
767
768
/* Add property to export plist */
769
error = bhnd_nvram_plist_append_val(plist, name,
770
bhnd_nvram_prop_val(prop));
771
if (error)
772
goto finished;
773
}
774
775
/* Success */
776
error = 0;
777
778
finished:
779
if (prefix != NULL)
780
bhnd_nv_free(prefix);
781
782
if (namebuf != NULL)
783
bhnd_nv_free(namebuf);
784
785
if (path_vars != NULL)
786
bhnd_nvram_plist_release(path_vars);
787
788
return (error);
789
}
790
791
/**
792
* Export a flat, ordered NVRAM property list representation of all NVRAM
793
* properties at @p path.
794
*
795
* @param sc The NVRAM store instance.
796
* @param path The NVRAM path to export, or NULL to select the root
797
* path.
798
* @param[out] cls On success, will be set to the backing data class
799
* of @p sc. If the data class is are not desired,
800
* a NULL pointer may be provided.
801
* @param[out] props On success, will be set to a caller-owned property
802
* list containing the exported properties. The caller is
803
* responsible for releasing this value via
804
* bhnd_nvram_plist_release().
805
* @param[out] options On success, will be set to a caller-owned property
806
* list containing the current NVRAM serialization options
807
* for @p sc. The caller is responsible for releasing this
808
* value via bhnd_nvram_plist_release().
809
* @param flags Export flags. See BHND_NVSTORE_EXPORT_*.
810
*
811
* @retval 0 success
812
* @retval EINVAL If @p flags is invalid.
813
* @retval ENOENT The requested path was not found.
814
* @retval ENOMEM If allocation fails.
815
* @retval non-zero If export of @p path otherwise fails, a regular unix
816
* error code will be returned.
817
*/
818
int
819
bhnd_nvram_store_export(struct bhnd_nvram_store *sc, const char *path,
820
bhnd_nvram_data_class **cls, bhnd_nvram_plist **props,
821
bhnd_nvram_plist **options, uint32_t flags)
822
{
823
bhnd_nvram_plist *unordered;
824
bhnd_nvstore_path *top;
825
bhnd_nvram_prop *prop;
826
const char *name;
827
void *cookiep;
828
size_t num_dpath_flags;
829
int error;
830
831
*props = NULL;
832
unordered = NULL;
833
num_dpath_flags = 0;
834
if (options != NULL)
835
*options = NULL;
836
837
/* Default to exporting root path */
838
if (path == NULL)
839
path = BHND_NVSTORE_ROOT_PATH;
840
841
/* Default to exporting all properties */
842
if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) &&
843
!BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED))
844
{
845
flags |= BHND_NVSTORE_EXPORT_ALL_VARS;
846
}
847
848
/* Default to preserving the current device path encoding */
849
if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS) &&
850
!BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
851
{
852
flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS;
853
}
854
855
/* Exactly one device path encoding flag must be set */
856
if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS))
857
num_dpath_flags++;
858
859
if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
860
num_dpath_flags++;
861
862
if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS))
863
num_dpath_flags++;
864
865
if (num_dpath_flags != 1)
866
return (EINVAL);
867
868
/* If EXPORT_DELETED is set, EXPORT_UNCOMMITTED must be set too */
869
if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED) &&
870
!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED))
871
{
872
return (EINVAL);
873
}
874
875
/* Lock internal state before querying paths/properties */
876
BHND_NVSTORE_LOCK(sc);
877
878
/* Fetch referenced path */
879
top = bhnd_nvstore_get_path(sc, path, strlen(path));
880
if (top == NULL) {
881
error = ENOENT;
882
goto failed;
883
}
884
885
/* Allocate new, empty property list */
886
if ((unordered = bhnd_nvram_plist_new()) == NULL) {
887
error = ENOMEM;
888
goto failed;
889
}
890
891
/* Export the top-level path first */
892
error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags);
893
if (error)
894
goto failed;
895
896
/* Attempt to export any children of the root path */
897
for (size_t i = 0; i < nitems(sc->paths); i++) {
898
bhnd_nvstore_path *child;
899
900
LIST_FOREACH(child, &sc->paths[i], np_link) {
901
/* Top-level path was already exported */
902
if (child == top)
903
continue;
904
905
error = bhnd_nvram_store_export_child(sc, top,
906
child, unordered, flags);
907
if (error)
908
goto failed;
909
}
910
}
911
912
/* If requested, provide the current class and serialization options */
913
if (cls != NULL)
914
*cls = bhnd_nvram_data_get_class(sc->data);
915
916
if (options != NULL)
917
*options = bhnd_nvram_plist_retain(sc->data_opts);
918
919
/*
920
* If we're re-encoding device paths, don't bother preserving the
921
* existing NVRAM variable order; our variable names will not match
922
* the existing backing NVRAM data.
923
*/
924
if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
925
*props = unordered;
926
unordered = NULL;
927
928
goto finished;
929
}
930
931
/*
932
* Re-order the flattened output to match the existing NVRAM variable
933
* ordering.
934
*
935
* We append all new variables at the end of the input; this should
936
* reduce the delta that needs to be written (e.g. to flash) when
937
* committing NVRAM updates, and should result in a serialization
938
* identical to the input serialization if uncommitted updates are
939
* excluded from the export.
940
*/
941
if ((*props = bhnd_nvram_plist_new()) == NULL) {
942
error = ENOMEM;
943
goto failed;
944
}
945
946
/* Using the backing NVRAM data ordering to order all variables
947
* currently defined in the backing store */
948
cookiep = NULL;
949
while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
950
prop = bhnd_nvram_plist_get_prop(unordered, name);
951
if (prop == NULL)
952
continue;
953
954
/* Append to ordered result */
955
if ((error = bhnd_nvram_plist_append(*props, prop)))
956
goto failed;
957
958
/* Remove from unordered list */
959
bhnd_nvram_plist_remove(unordered, name);
960
}
961
962
/* Any remaining variables are new, and should be appended to the
963
* end of the export list */
964
prop = NULL;
965
while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) {
966
if ((error = bhnd_nvram_plist_append(*props, prop)))
967
goto failed;
968
}
969
970
/* Export complete */
971
finished:
972
BHND_NVSTORE_UNLOCK(sc);
973
974
if (unordered != NULL)
975
bhnd_nvram_plist_release(unordered);
976
977
return (0);
978
979
failed:
980
BHND_NVSTORE_UNLOCK(sc);
981
982
if (unordered != NULL)
983
bhnd_nvram_plist_release(unordered);
984
985
if (options != NULL && *options != NULL)
986
bhnd_nvram_plist_release(*options);
987
988
if (*props != NULL)
989
bhnd_nvram_plist_release(*props);
990
991
return (error);
992
}
993
994
/**
995
* Encode all NVRAM properties at @p path, using the @p store's current NVRAM
996
* data format.
997
*
998
* @param sc The NVRAM store instance.
999
* @param path The NVRAM path to export, or NULL to select the root
1000
* path.
1001
* @param[out] data On success, will be set to the newly serialized value.
1002
* The caller is responsible for freeing this value
1003
* via bhnd_nvram_io_free().
1004
* @param flags Export flags. See BHND_NVSTORE_EXPORT_*.
1005
*
1006
* @retval 0 success
1007
* @retval EINVAL If @p flags is invalid.
1008
* @retval ENOENT The requested path was not found.
1009
* @retval ENOMEM If allocation fails.
1010
* @retval non-zero If serialization of @p path otherwise fails, a regular
1011
* unix error code will be returned.
1012
*/
1013
int
1014
bhnd_nvram_store_serialize(struct bhnd_nvram_store *sc, const char *path,
1015
struct bhnd_nvram_io **data, uint32_t flags)
1016
{
1017
bhnd_nvram_plist *props;
1018
bhnd_nvram_plist *options;
1019
bhnd_nvram_data_class *cls;
1020
struct bhnd_nvram_io *io;
1021
void *outp;
1022
size_t olen;
1023
int error;
1024
1025
props = NULL;
1026
options = NULL;
1027
io = NULL;
1028
1029
/* Perform requested export */
1030
error = bhnd_nvram_store_export(sc, path, &cls, &props, &options,
1031
flags);
1032
if (error)
1033
return (error);
1034
1035
/* Determine serialized size */
1036
error = bhnd_nvram_data_serialize(cls, props, options, NULL, &olen);
1037
if (error)
1038
goto failed;
1039
1040
/* Allocate output buffer */
1041
if ((io = bhnd_nvram_iobuf_empty(olen, olen)) == NULL) {
1042
error = ENOMEM;
1043
goto failed;
1044
}
1045
1046
/* Fetch write pointer */
1047
if ((error = bhnd_nvram_io_write_ptr(io, 0, &outp, olen, NULL)))
1048
goto failed;
1049
1050
/* Perform serialization */
1051
error = bhnd_nvram_data_serialize(cls, props, options, outp, &olen);
1052
if (error)
1053
goto failed;
1054
1055
if ((error = bhnd_nvram_io_setsize(io, olen)))
1056
goto failed;
1057
1058
/* Success */
1059
bhnd_nvram_plist_release(props);
1060
bhnd_nvram_plist_release(options);
1061
1062
*data = io;
1063
return (0);
1064
1065
failed:
1066
if (props != NULL)
1067
bhnd_nvram_plist_release(props);
1068
1069
if (options != NULL)
1070
bhnd_nvram_plist_release(options);
1071
1072
if (io != NULL)
1073
bhnd_nvram_io_free(io);
1074
1075
return (error);
1076
}
1077
1078
/**
1079
* Read an NVRAM variable.
1080
*
1081
* @param sc The NVRAM parser state.
1082
* @param name The NVRAM variable name.
1083
* @param[out] outp On success, the requested value will be written
1084
* to this buffer. This argment may be NULL if
1085
* the value is not desired.
1086
* @param[in,out] olen The capacity of @p outp. On success, will be set
1087
* to the actual size of the requested value.
1088
* @param otype The requested data type to be written to
1089
* @p outp.
1090
*
1091
* @retval 0 success
1092
* @retval ENOENT The requested variable was not found.
1093
* @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too
1094
* small to hold the requested value.
1095
* @retval non-zero If reading @p name otherwise fails, a regular unix
1096
* error code will be returned.
1097
*/
1098
int
1099
bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
1100
void *outp, size_t *olen, bhnd_nvram_type otype)
1101
{
1102
bhnd_nvstore_name_info info;
1103
bhnd_nvstore_path *path;
1104
bhnd_nvram_prop *prop;
1105
void *cookiep;
1106
int error;
1107
1108
BHND_NVSTORE_LOCK(sc);
1109
1110
/* Parse the variable name */
1111
error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
1112
sc->data_caps, &info);
1113
if (error)
1114
goto finished;
1115
1116
/* Fetch the variable's enclosing path entry */
1117
if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) {
1118
error = ENOENT;
1119
goto finished;
1120
}
1121
1122
/* Search uncommitted updates first */
1123
prop = bhnd_nvstore_path_get_update(sc, path, info.name);
1124
if (prop != NULL) {
1125
if (bhnd_nvram_prop_is_null(prop)) {
1126
/* NULL denotes a pending deletion */
1127
error = ENOENT;
1128
} else {
1129
error = bhnd_nvram_prop_encode(prop, outp, olen, otype);
1130
}
1131
goto finished;
1132
}
1133
1134
/* Search the backing NVRAM data */
1135
cookiep = bhnd_nvstore_path_data_lookup(sc, path, info.name);
1136
if (cookiep != NULL) {
1137
/* Found in backing store */
1138
error = bhnd_nvram_data_getvar(sc->data, cookiep, outp, olen,
1139
otype);
1140
goto finished;
1141
}
1142
1143
/* Not found */
1144
error = ENOENT;
1145
1146
finished:
1147
BHND_NVSTORE_UNLOCK(sc);
1148
return (error);
1149
}
1150
1151
/**
1152
* Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar()
1153
* implementation.
1154
*
1155
* If @p value is NULL, the variable will be marked for deletion.
1156
*/
1157
static int
1158
bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name,
1159
bhnd_nvram_val *value)
1160
{
1161
bhnd_nvstore_path *path;
1162
bhnd_nvstore_name_info info;
1163
int error;
1164
1165
BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
1166
1167
/* Parse the variable name */
1168
error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
1169
sc->data_caps, &info);
1170
if (error)
1171
return (error);
1172
1173
/* Fetch the variable's enclosing path entry */
1174
if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL)
1175
return (error);
1176
1177
/* Register the update entry */
1178
return (bhnd_nvstore_path_register_update(sc, path, info.name, value));
1179
}
1180
1181
/**
1182
* Set an NVRAM variable.
1183
*
1184
* @param sc The NVRAM parser state.
1185
* @param name The NVRAM variable name.
1186
* @param value The new value.
1187
*
1188
* @retval 0 success
1189
* @retval ENOENT The requested variable @p name was not found.
1190
* @retval EINVAL If @p value is invalid.
1191
*/
1192
int
1193
bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name,
1194
bhnd_nvram_val *value)
1195
{
1196
int error;
1197
1198
BHND_NVSTORE_LOCK(sc);
1199
error = bhnd_nvram_store_setval_common(sc, name, value);
1200
BHND_NVSTORE_UNLOCK(sc);
1201
1202
return (error);
1203
}
1204
1205
/**
1206
* Set an NVRAM variable.
1207
*
1208
* @param sc The NVRAM parser state.
1209
* @param name The NVRAM variable name.
1210
* @param[out] inp The new value.
1211
* @param[in,out] ilen The size of @p inp.
1212
* @param itype The data type of @p inp.
1213
*
1214
* @retval 0 success
1215
* @retval ENOENT The requested variable @p name was not found.
1216
* @retval EINVAL If the new value is invalid.
1217
* @retval EINVAL If @p name is read-only.
1218
*/
1219
int
1220
bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
1221
const void *inp, size_t ilen, bhnd_nvram_type itype)
1222
{
1223
bhnd_nvram_val val;
1224
int error;
1225
1226
error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
1227
BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA);
1228
if (error) {
1229
BHND_NV_LOG("error initializing value: %d\n", error);
1230
return (EINVAL);
1231
}
1232
1233
BHND_NVSTORE_LOCK(sc);
1234
error = bhnd_nvram_store_setval_common(sc, name, &val);
1235
BHND_NVSTORE_UNLOCK(sc);
1236
1237
bhnd_nvram_val_release(&val);
1238
1239
return (error);
1240
}
1241
1242
/**
1243
* Unset an NVRAM variable.
1244
*
1245
* @param sc The NVRAM parser state.
1246
* @param name The NVRAM variable name.
1247
*
1248
* @retval 0 success
1249
* @retval ENOENT The requested variable @p name was not found.
1250
* @retval EINVAL If @p name is read-only.
1251
*/
1252
int
1253
bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name)
1254
{
1255
int error;
1256
1257
BHND_NVSTORE_LOCK(sc);
1258
error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL);
1259
BHND_NVSTORE_UNLOCK(sc);
1260
1261
return (error);
1262
}
1263
1264