Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_value.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/limits.h>
32
#include <sys/sbuf.h>
33
34
#ifdef _KERNEL
35
36
#include <sys/ctype.h>
37
#include <sys/kernel.h>
38
#include <sys/malloc.h>
39
#include <sys/systm.h>
40
41
#include <machine/_inttypes.h>
42
43
#else /* !_KERNEL */
44
45
#include <ctype.h>
46
#include <inttypes.h>
47
#include <errno.h>
48
#include <stdlib.h>
49
#include <string.h>
50
51
#endif /* _KERNEL */
52
53
#include "bhnd_nvram_private.h"
54
55
#include "bhnd_nvram_valuevar.h"
56
57
static int bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt,
58
const void *inp, size_t ilen, bhnd_nvram_type itype);
59
60
static void *bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen,
61
bhnd_nvram_type itype, uint32_t flags);
62
static int bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp,
63
size_t ilen, bhnd_nvram_type itype, uint32_t flags);
64
static int bhnd_nvram_val_set_inline(bhnd_nvram_val *value,
65
const void *inp, size_t ilen, bhnd_nvram_type itype);
66
67
static int bhnd_nvram_val_encode_data(const void *inp, size_t ilen,
68
bhnd_nvram_type itype, void *outp, size_t *olen,
69
bhnd_nvram_type otype);
70
static int bhnd_nvram_val_encode_int(const void *inp, size_t ilen,
71
bhnd_nvram_type itype, void *outp, size_t *olen,
72
bhnd_nvram_type otype);
73
static int bhnd_nvram_val_encode_null(const void *inp, size_t ilen,
74
bhnd_nvram_type itype, void *outp, size_t *olen,
75
bhnd_nvram_type otype);
76
static int bhnd_nvram_val_encode_bool(const void *inp, size_t ilen,
77
bhnd_nvram_type itype, void *outp, size_t *olen,
78
bhnd_nvram_type otype);
79
static int bhnd_nvram_val_encode_string(const void *inp, size_t ilen,
80
bhnd_nvram_type itype, void *outp, size_t *olen,
81
bhnd_nvram_type otype);
82
83
/** Initialize an empty value instance with @p _fmt, @p _storage, and
84
* an implicit callee-owned reference */
85
#define BHND_NVRAM_VAL_INITIALIZER(_fmt, _storage) \
86
(bhnd_nvram_val) { \
87
.refs = 1, \
88
.val_storage = _storage, \
89
.fmt = _fmt, \
90
.data_storage = BHND_NVRAM_VAL_DATA_NONE, \
91
};
92
93
/** Assert that @p value's backing representation state has initialized
94
* as empty. */
95
#define BHND_NVRAM_VAL_ASSERT_EMPTY(_value) \
96
BHND_NV_ASSERT( \
97
value->data_storage == BHND_NVRAM_VAL_DATA_NONE && \
98
value->data_len == 0 && \
99
value->data.ptr == NULL, \
100
("previously initialized value"))
101
102
/** Return true if BHND_NVRAM_VAL_BORROW_DATA or BHND_NVRAM_VAL_STATIC_DATA is
103
* set in @p _flags (e.g. we should attempt to directly reference external
104
* data */
105
#define BHND_NVRAM_VAL_EXTREF_BORROWED_DATA(_flags) \
106
(((_flags) & BHND_NVRAM_VAL_BORROW_DATA) || \
107
((_flags) & BHND_NVRAM_VAL_STATIC_DATA))
108
109
/** Flags permitted when performing val-based initialization via
110
* bhnd_nvram_val_convert_init() or bhnd_nvram_val_convert_new() */
111
#define BHND_NVRAM_VALID_CONV_FLAGS \
112
(BHND_NVRAM_VAL_FIXED | \
113
BHND_NVRAM_VAL_DYNAMIC | \
114
BHND_NVRAM_VAL_COPY_DATA)
115
116
/** Returns true if @p _val must be copied in bhnd_nvram_val_copy(), false
117
* if its reference count may be safely incremented */
118
#define BHND_NVRAM_VAL_NEED_COPY(_val) \
119
((_val)->val_storage == BHND_NVRAM_VAL_STORAGE_AUTO || \
120
(_val)->data_storage == BHND_NVRAM_VAL_DATA_EXT_WEAK)
121
122
volatile u_int refs; /**< reference count */
123
bhnd_nvram_val_storage val_storage; /**< value structure storage */
124
const bhnd_nvram_val_fmt *fmt; /**< value format */
125
bhnd_nvram_val_data_storage data_storage; /**< data storage */
126
bhnd_nvram_type data_type; /**< data type */
127
size_t data_len; /**< data size */
128
129
/* Shared NULL value instance */
130
bhnd_nvram_val bhnd_nvram_val_null = {
131
.refs = 1,
132
.val_storage = BHND_NVRAM_VAL_STORAGE_STATIC,
133
.fmt = &bhnd_nvram_val_null_fmt,
134
.data_storage = BHND_NVRAM_VAL_DATA_INLINE,
135
.data_type = BHND_NVRAM_TYPE_NULL,
136
.data_len = 0,
137
};
138
139
/**
140
* Return the human-readable name of @p fmt.
141
*/
142
const char *
143
bhnd_nvram_val_fmt_name(const bhnd_nvram_val_fmt *fmt)
144
{
145
return (fmt->name);
146
}
147
148
/**
149
* Return the default format for values of @p type.
150
*/
151
const bhnd_nvram_val_fmt *
152
bhnd_nvram_val_default_fmt(bhnd_nvram_type type)
153
{
154
switch (type) {
155
case BHND_NVRAM_TYPE_UINT8:
156
return (&bhnd_nvram_val_uint8_fmt);
157
case BHND_NVRAM_TYPE_UINT16:
158
return (&bhnd_nvram_val_uint16_fmt);
159
case BHND_NVRAM_TYPE_UINT32:
160
return (&bhnd_nvram_val_uint32_fmt);
161
case BHND_NVRAM_TYPE_UINT64:
162
return (&bhnd_nvram_val_uint64_fmt);
163
case BHND_NVRAM_TYPE_INT8:
164
return (&bhnd_nvram_val_int8_fmt);
165
case BHND_NVRAM_TYPE_INT16:
166
return (&bhnd_nvram_val_int16_fmt);
167
case BHND_NVRAM_TYPE_INT32:
168
return (&bhnd_nvram_val_int32_fmt);
169
case BHND_NVRAM_TYPE_INT64:
170
return (&bhnd_nvram_val_int64_fmt);
171
case BHND_NVRAM_TYPE_CHAR:
172
return (&bhnd_nvram_val_char_fmt);
173
case BHND_NVRAM_TYPE_STRING:
174
return (&bhnd_nvram_val_string_fmt);
175
case BHND_NVRAM_TYPE_BOOL:
176
return (&bhnd_nvram_val_bool_fmt);
177
case BHND_NVRAM_TYPE_NULL:
178
return (&bhnd_nvram_val_null_fmt);
179
case BHND_NVRAM_TYPE_DATA:
180
return (&bhnd_nvram_val_data_fmt);
181
case BHND_NVRAM_TYPE_UINT8_ARRAY:
182
return (&bhnd_nvram_val_uint8_array_fmt);
183
case BHND_NVRAM_TYPE_UINT16_ARRAY:
184
return (&bhnd_nvram_val_uint16_array_fmt);
185
case BHND_NVRAM_TYPE_UINT32_ARRAY:
186
return (&bhnd_nvram_val_uint32_array_fmt);
187
case BHND_NVRAM_TYPE_UINT64_ARRAY:
188
return (&bhnd_nvram_val_uint64_array_fmt);
189
case BHND_NVRAM_TYPE_INT8_ARRAY:
190
return (&bhnd_nvram_val_int8_array_fmt);
191
case BHND_NVRAM_TYPE_INT16_ARRAY:
192
return (&bhnd_nvram_val_int16_array_fmt);
193
case BHND_NVRAM_TYPE_INT32_ARRAY:
194
return (&bhnd_nvram_val_int32_array_fmt);
195
case BHND_NVRAM_TYPE_INT64_ARRAY:
196
return (&bhnd_nvram_val_int64_array_fmt);
197
case BHND_NVRAM_TYPE_CHAR_ARRAY:
198
return (&bhnd_nvram_val_char_array_fmt);
199
case BHND_NVRAM_TYPE_STRING_ARRAY:
200
return (&bhnd_nvram_val_string_array_fmt);
201
case BHND_NVRAM_TYPE_BOOL_ARRAY:
202
return (&bhnd_nvram_val_bool_array_fmt);
203
}
204
205
/* Quiesce gcc4.2 */
206
BHND_NV_PANIC("bhnd nvram type %u unknown", type);
207
}
208
209
/**
210
* Determine whether @p fmt (or new format delegated to by @p fmt) is
211
* capable of direct initialization from buffer @p inp.
212
*
213
* @param[in,out] fmt Indirect pointer to the NVRAM value format. If
214
* the format instance cannot handle the data type
215
* directly, it may delegate to a new format
216
* instance. On success, this parameter will be
217
* set to the format that should be used when
218
* performing initialization from @p inp.
219
* @param inp Input data.
220
* @param ilen Input data length.
221
* @param itype Input data type.
222
*
223
* @retval 0 If initialization from @p inp is supported.
224
* @retval EFTYPE If initialization from @p inp is unsupported.
225
* @retval EFAULT if @p ilen is not correctly aligned for elements of
226
* @p itype.
227
*/
228
static int
229
bhnd_nvram_val_fmt_filter(const bhnd_nvram_val_fmt **fmt, const void *inp,
230
size_t ilen, bhnd_nvram_type itype)
231
{
232
const bhnd_nvram_val_fmt *ofmt, *nfmt;
233
int error;
234
235
nfmt = ofmt = *fmt;
236
237
/* Validate alignment */
238
if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype)))
239
return (error);
240
241
/* If the format does not provide a filter function, it only supports
242
* direct initialization from its native type */
243
if (ofmt->op_filter == NULL) {
244
if (itype == ofmt->native_type)
245
return (0);
246
247
return (EFTYPE);
248
}
249
250
/* Use the filter function to determine whether direct initialization
251
* from itype is permitted */
252
error = ofmt->op_filter(&nfmt, inp, ilen, itype);
253
if (error)
254
return (error);
255
256
/* Retry filter with new format? */
257
if (ofmt != nfmt) {
258
error = bhnd_nvram_val_fmt_filter(&nfmt, inp, ilen, itype);
259
if (error)
260
return (error);
261
262
/* Success -- provide delegated format to caller */
263
*fmt = nfmt;
264
}
265
266
/* Value can be initialized with provided format and input type */
267
return (0);
268
}
269
270
/* Common initialization support for bhnd_nvram_val_init() and
271
* bhnd_nvram_val_new() */
272
static int
273
bhnd_nvram_val_init_common(bhnd_nvram_val *value,
274
bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt,
275
const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)
276
{
277
void *outp;
278
bhnd_nvram_type otype;
279
size_t olen;
280
int error;
281
282
/* If the value format is unspecified, we use the default format
283
* for the input data type */
284
if (fmt == NULL)
285
fmt = bhnd_nvram_val_default_fmt(itype);
286
287
/* Determine expected data type, and allow the format to delegate to
288
* a new format instance */
289
if ((error = bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype))) {
290
/* Direct initialization from the provided input type is
291
* not supported; alue must be initialized with the format's
292
* native type */
293
otype = fmt->native_type;
294
} else {
295
/* Value can be initialized with provided input type */
296
otype = itype;
297
}
298
299
/* Initialize value instance */
300
*value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage);
301
302
/* If input data already in native format, init directly. */
303
if (otype == itype) {
304
error = bhnd_nvram_val_set(value, inp, ilen, itype, flags);
305
if (error)
306
return (error);
307
308
return (0);
309
}
310
311
/* Determine size when encoded in native format */
312
error = bhnd_nvram_value_coerce(inp, ilen, itype, NULL, &olen, otype);
313
if (error)
314
return (error);
315
316
/* Fetch reference to (or allocate) an appropriately sized buffer */
317
outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags);
318
if (outp == NULL)
319
return (ENOMEM);
320
321
/* Perform encode */
322
error = bhnd_nvram_value_coerce(inp, ilen, itype, outp, &olen, otype);
323
if (error)
324
return (error);
325
326
return (0);
327
}
328
329
/**
330
* Initialize an externally allocated instance of @p value with @p fmt from the
331
* given @p inp buffer of @p itype and @p ilen.
332
*
333
* On success, the caller owns a reference to @p value, and is responsible for
334
* freeing any resources allocated for @p value via bhnd_nvram_val_release().
335
*
336
* @param value The externally allocated value instance to be
337
* initialized.
338
* @param fmt The value's format, or NULL to use the default format
339
* for @p itype.
340
* @param inp Input buffer.
341
* @param ilen Input buffer length.
342
* @param itype Input buffer type.
343
* @param flags Value flags (see BHND_NVRAM_VAL_*).
344
*
345
* @retval 0 success
346
* @retval ENOMEM If allocation fails.
347
* @retval EFTYPE If @p fmt initialization from @p itype is unsupported.
348
* @retval EFAULT if @p ilen is not correctly aligned for elements of
349
* @p itype.
350
* @retval ERANGE If value coercion would overflow (or underflow) the
351
* @p fmt representation.
352
*/
353
int
354
bhnd_nvram_val_init(bhnd_nvram_val *value, const bhnd_nvram_val_fmt *fmt,
355
const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)
356
{
357
int error;
358
359
error = bhnd_nvram_val_init_common(value, BHND_NVRAM_VAL_STORAGE_AUTO,
360
fmt, inp, ilen, itype, flags);
361
if (error)
362
bhnd_nvram_val_release(value);
363
364
return (error);
365
}
366
367
/**
368
* Allocate a value instance with @p fmt, and attempt to initialize its internal
369
* representation from the given @p inp buffer of @p itype and @p ilen.
370
*
371
* On success, the caller owns a reference to @p value, and is responsible for
372
* freeing any resources allocated for @p value via bhnd_nvram_val_release().
373
*
374
* @param[out] value On success, the allocated value instance.
375
* @param fmt The value's format, or NULL to use the default format
376
* for @p itype.
377
* @param inp Input buffer.
378
* @param ilen Input buffer length.
379
* @param itype Input buffer type.
380
* @param flags Value flags (see BHND_NVRAM_VAL_*).
381
*
382
* @retval 0 success
383
* @retval ENOMEM If allocation fails.
384
* @retval EFTYPE If @p fmt initialization from @p itype is unsupported.
385
* @retval EFAULT if @p ilen is not correctly aligned for elements of
386
* @p itype.
387
* @retval ERANGE If value coercion would overflow (or underflow) the
388
* @p fmt representation.
389
*/
390
int
391
bhnd_nvram_val_new(bhnd_nvram_val **value, const bhnd_nvram_val_fmt *fmt,
392
const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)
393
{
394
int error;
395
396
/* Allocate new instance */
397
if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL)
398
return (ENOMEM);
399
400
/* Perform common initialization. */
401
error = bhnd_nvram_val_init_common(*value,
402
BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, inp, ilen, itype, flags);
403
if (error) {
404
/* Will also free() the value allocation */
405
bhnd_nvram_val_release(*value);
406
}
407
408
return (error);
409
}
410
411
/* Common initialization support for bhnd_nvram_val_convert_init() and
412
* bhnd_nvram_val_convert_new() */
413
static int
414
bhnd_nvram_val_convert_common(bhnd_nvram_val *value,
415
bhnd_nvram_val_storage val_storage, const bhnd_nvram_val_fmt *fmt,
416
bhnd_nvram_val *src, uint32_t flags)
417
{
418
const void *inp;
419
void *outp;
420
bhnd_nvram_type itype, otype;
421
size_t ilen, olen;
422
int error;
423
424
/* Determine whether direct initialization from the source value's
425
* existing data type is supported by the new format */
426
inp = bhnd_nvram_val_bytes(src, &ilen, &itype);
427
if (bhnd_nvram_val_fmt_filter(&fmt, inp, ilen, itype) == 0) {
428
/* Adjust value flags based on the source data storage */
429
switch (src->data_storage) {
430
case BHND_NVRAM_VAL_DATA_NONE:
431
case BHND_NVRAM_VAL_DATA_INLINE:
432
case BHND_NVRAM_VAL_DATA_EXT_WEAK:
433
case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
434
break;
435
436
case BHND_NVRAM_VAL_DATA_EXT_STATIC:
437
/* If the source data has static storage duration,
438
* we should apply that transitively */
439
if (flags & BHND_NVRAM_VAL_BORROW_DATA)
440
flags |= BHND_NVRAM_VAL_STATIC_DATA;
441
442
break;
443
}
444
445
/* Delegate to standard initialization */
446
return (bhnd_nvram_val_init_common(value, val_storage, fmt, inp,
447
ilen, itype, flags));
448
}
449
450
/* Value must be initialized with the format's native type */
451
otype = fmt->native_type;
452
453
/* Initialize value instance */
454
*value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage);
455
456
/* Determine size when encoded in native format */
457
if ((error = bhnd_nvram_val_encode(src, NULL, &olen, otype)))
458
return (error);
459
460
/* Fetch reference to (or allocate) an appropriately sized buffer */
461
outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags);
462
if (outp == NULL)
463
return (ENOMEM);
464
465
/* Perform encode */
466
if ((error = bhnd_nvram_val_encode(src, outp, &olen, otype)))
467
return (error);
468
469
return (0);
470
}
471
472
/**
473
* Initialize an externally allocated instance of @p value with @p fmt, and
474
* attempt to initialize its internal representation from the given @p src
475
* value.
476
*
477
* On success, the caller owns a reference to @p value, and is responsible for
478
* freeing any resources allocated for @p value via bhnd_nvram_val_release().
479
*
480
* @param value The externally allocated value instance to be
481
* initialized.
482
* @param fmt The value's format.
483
* @param src Input value to be converted.
484
* @param flags Value flags (see BHND_NVRAM_VAL_*).
485
*
486
* @retval 0 success
487
* @retval ENOMEM If allocation fails.
488
* @retval EFTYPE If @p fmt initialization from @p src is unsupported.
489
* @retval EFAULT if @p ilen is not correctly aligned for elements of
490
* @p itype.
491
* @retval ERANGE If value coercion of @p src would overflow
492
* (or underflow) the @p fmt representation.
493
*/
494
int
495
bhnd_nvram_val_convert_init(bhnd_nvram_val *value,
496
const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags)
497
{
498
int error;
499
500
error = bhnd_nvram_val_convert_common(value,
501
BHND_NVRAM_VAL_STORAGE_AUTO, fmt, src, flags);
502
if (error)
503
bhnd_nvram_val_release(value);
504
505
return (error);
506
}
507
508
/**
509
* Allocate a value instance with @p fmt, and attempt to initialize its internal
510
* representation from the given @p src value.
511
*
512
* On success, the caller owns a reference to @p value, and is responsible for
513
* freeing any resources allocated for @p value via bhnd_nvram_val_release().
514
*
515
* @param[out] value On success, the allocated value instance.
516
* @param fmt The value's format.
517
* @param src Input value to be converted.
518
* @param flags Value flags (see BHND_NVRAM_VAL_*).
519
*
520
* @retval 0 success
521
* @retval ENOMEM If allocation fails.
522
* @retval EFTYPE If @p fmt initialization from @p src is unsupported.
523
* @retval EFAULT if @p ilen is not correctly aligned for elements of
524
* @p itype.
525
* @retval ERANGE If value coercion of @p src would overflow
526
* (or underflow) the @p fmt representation.
527
*/
528
int
529
bhnd_nvram_val_convert_new(bhnd_nvram_val **value,
530
const bhnd_nvram_val_fmt *fmt, bhnd_nvram_val *src, uint32_t flags)
531
{
532
int error;
533
534
/* Allocate new instance */
535
if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL)
536
return (ENOMEM);
537
538
/* Perform common initialization. */
539
error = bhnd_nvram_val_convert_common(*value,
540
BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, src, flags);
541
if (error) {
542
/* Will also free() the value allocation */
543
bhnd_nvram_val_release(*value);
544
}
545
546
return (error);
547
}
548
549
/**
550
* Copy or retain a reference to @p value.
551
*
552
* On success, the caller is responsible for freeing the result via
553
* bhnd_nvram_val_release().
554
*
555
* @param value The value to be copied (or retained).
556
*
557
* @retval bhnd_nvram_val if @p value was successfully copied or retained.
558
* @retval NULL if allocation failed.
559
*/
560
bhnd_nvram_val *
561
bhnd_nvram_val_copy(bhnd_nvram_val *value)
562
{
563
bhnd_nvram_val *result;
564
const void *bytes;
565
bhnd_nvram_type type;
566
size_t len;
567
uint32_t flags;
568
int error;
569
570
switch (value->val_storage) {
571
case BHND_NVRAM_VAL_STORAGE_STATIC:
572
/* If static, can return as-is */
573
return (value);
574
575
case BHND_NVRAM_VAL_STORAGE_DYNAMIC:
576
if (!BHND_NVRAM_VAL_NEED_COPY(value)) {
577
refcount_acquire(&value->refs);
578
return (value);
579
}
580
581
/* Perform copy below */
582
break;
583
584
case BHND_NVRAM_VAL_STORAGE_AUTO:
585
BHND_NV_ASSERT(value->refs == 1, ("non-allocated value has "
586
"active refcount (%u)", value->refs));
587
588
/* Perform copy below */
589
break;
590
}
591
592
/* Compute the new value's flags based on the source value */
593
switch (value->data_storage) {
594
case BHND_NVRAM_VAL_DATA_NONE:
595
case BHND_NVRAM_VAL_DATA_INLINE:
596
case BHND_NVRAM_VAL_DATA_EXT_WEAK:
597
case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
598
/* Copy the source data and permit additional allocation if the
599
* value cannot be represented inline */
600
flags = BHND_NVRAM_VAL_COPY_DATA|BHND_NVRAM_VAL_DYNAMIC;
601
break;
602
case BHND_NVRAM_VAL_DATA_EXT_STATIC:
603
flags = BHND_NVRAM_VAL_STATIC_DATA;
604
break;
605
default:
606
BHND_NV_PANIC("invalid storage type: %d", value->data_storage);
607
}
608
609
/* Allocate new value copy */
610
bytes = bhnd_nvram_val_bytes(value, &len, &type);
611
error = bhnd_nvram_val_new(&result, value->fmt, bytes, len, type,
612
flags);
613
if (error) {
614
BHND_NV_LOG("copy failed: %d", error);
615
return (NULL);
616
}
617
618
return (result);
619
}
620
621
/**
622
* Release a reference to @p value.
623
*
624
* If this is the last reference, all associated resources will be freed.
625
*
626
* @param value The value to be released.
627
*/
628
void
629
bhnd_nvram_val_release(bhnd_nvram_val *value)
630
{
631
BHND_NV_ASSERT(value->refs >= 1, ("value over-released"));
632
633
/* Skip if value is static */
634
if (value->val_storage == BHND_NVRAM_VAL_STORAGE_STATIC)
635
return;
636
637
/* Drop reference */
638
if (!refcount_release(&value->refs))
639
return;
640
641
/* Free allocated external representation data */
642
switch (value->data_storage) {
643
case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
644
bhnd_nv_free(__DECONST(void *, value->data.ptr));
645
break;
646
case BHND_NVRAM_VAL_DATA_NONE:
647
case BHND_NVRAM_VAL_DATA_INLINE:
648
case BHND_NVRAM_VAL_DATA_EXT_WEAK:
649
case BHND_NVRAM_VAL_DATA_EXT_STATIC:
650
/* Nothing to free */
651
break;
652
}
653
654
/* Free instance if dynamically allocated */
655
if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC)
656
bhnd_nv_free(value);
657
}
658
659
/**
660
* Standard BHND_NVRAM_TYPE_NULL encoding implementation.
661
*/
662
static int
663
bhnd_nvram_val_encode_null(const void *inp, size_t ilen, bhnd_nvram_type itype,
664
void *outp, size_t *olen, bhnd_nvram_type otype)
665
{
666
size_t limit, nbytes;
667
668
BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_NULL,
669
("unsupported type: %d", itype));
670
671
/* Determine output byte limit */
672
if (outp != NULL)
673
limit = *olen;
674
else
675
limit = 0;
676
677
nbytes = 0;
678
679
/* Write to output */
680
switch (otype) {
681
case BHND_NVRAM_TYPE_NULL:
682
/* Can be directly encoded as a zero-length NULL value */
683
nbytes = 0;
684
break;
685
default:
686
/* Not representable */
687
return (EFTYPE);
688
}
689
690
/* Provide required length */
691
*olen = nbytes;
692
if (limit < *olen) {
693
if (outp == NULL)
694
return (0);
695
696
return (ENOMEM);
697
}
698
699
return (0);
700
}
701
702
/**
703
* Standard BHND_NVRAM_TYPE_BOOL encoding implementation.
704
*/
705
static int
706
bhnd_nvram_val_encode_bool(const void *inp, size_t ilen, bhnd_nvram_type itype,
707
void *outp, size_t *olen, bhnd_nvram_type otype)
708
{
709
bhnd_nvram_bool_t bval;
710
size_t limit, nbytes, nelem;
711
int error;
712
713
BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_BOOL,
714
("unsupported type: %d", itype));
715
716
/* Determine output byte limit */
717
if (outp != NULL)
718
limit = *olen;
719
else
720
limit = 0;
721
722
/* Must be exactly one element in input */
723
if ((error = bhnd_nvram_value_nelem(inp, ilen, itype, &nelem)))
724
return (error);
725
726
if (nelem != 1)
727
return (EFTYPE);
728
729
/* Fetch (and normalize) boolean value */
730
bval = (*(const bhnd_nvram_bool_t *)inp != 0) ? true : false;
731
732
/* Write to output */
733
switch (otype) {
734
case BHND_NVRAM_TYPE_NULL:
735
/* False can be directly encoded as a zero-length NULL value */
736
if (bval != false)
737
return (EFTYPE);
738
739
nbytes = 0;
740
break;
741
742
case BHND_NVRAM_TYPE_STRING:
743
case BHND_NVRAM_TYPE_STRING_ARRAY: {
744
/* Can encode as "true" or "false" */
745
const char *str = bval ? "true" : "false";
746
747
nbytes = strlen(str) + 1;
748
if (limit > nbytes)
749
strcpy(outp, str);
750
751
break;
752
}
753
754
default:
755
/* If output type is an integer, we can delegate to standard
756
* integer encoding to encode as zero or one. */
757
if (bhnd_nvram_is_int_type(otype)) {
758
uint8_t ival = bval ? 1 : 0;
759
760
return (bhnd_nvram_val_encode_int(&ival, sizeof(ival),
761
BHND_NVRAM_TYPE_UINT8, outp, olen, otype));
762
}
763
764
/* Otherwise not representable */
765
return (EFTYPE);
766
}
767
768
/* Provide required length */
769
*olen = nbytes;
770
if (limit < *olen) {
771
if (outp == NULL)
772
return (0);
773
774
return (ENOMEM);
775
}
776
777
return (0);
778
}
779
780
/**
781
* Standard BHND_NVRAM_TYPE_DATA encoding implementation.
782
*/
783
static int
784
bhnd_nvram_val_encode_data(const void *inp, size_t ilen, bhnd_nvram_type itype,
785
void *outp, size_t *olen, bhnd_nvram_type otype)
786
{
787
BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_DATA,
788
("unsupported type: %d", itype));
789
790
/* Write to output */
791
switch (otype) {
792
case BHND_NVRAM_TYPE_STRING:
793
case BHND_NVRAM_TYPE_STRING_ARRAY:
794
/* If encoding as a string, produce an EFI-style hexadecimal
795
* byte array (HF1F...) by interpreting the octet string
796
* as an array of uint8 values */
797
return (bhnd_nvram_value_printf("H%[]02hhX", inp, ilen,
798
BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, ""));
799
800
default:
801
/* Fall back on direct interpretation as an array of 8-bit
802
* integers array */
803
return (bhnd_nvram_value_coerce(inp, ilen,
804
BHND_NVRAM_TYPE_UINT8_ARRAY, outp, olen, otype));
805
}
806
}
807
808
/**
809
* Standard string/char array/char encoding implementation.
810
*
811
* Input type must be one of:
812
* - BHND_NVRAM_TYPE_STRING
813
* - BHND_NVRAM_TYPE_CHAR
814
* - BHND_NVRAM_TYPE_CHAR_ARRAY
815
*/
816
static int
817
bhnd_nvram_val_encode_string(const void *inp, size_t ilen,
818
bhnd_nvram_type itype, void *outp, size_t *olen, bhnd_nvram_type otype)
819
{
820
const char *cstr;
821
bhnd_nvram_type otype_base;
822
size_t cstr_size, cstr_len;
823
size_t limit, nbytes;
824
825
BHND_NV_ASSERT(
826
itype == BHND_NVRAM_TYPE_STRING ||
827
itype == BHND_NVRAM_TYPE_CHAR ||
828
itype == BHND_NVRAM_TYPE_CHAR_ARRAY,
829
("unsupported type: %d", itype));
830
831
cstr = inp;
832
cstr_size = ilen;
833
nbytes = 0;
834
otype_base = bhnd_nvram_base_type(otype);
835
836
/* Determine output byte limit */
837
if (outp != NULL)
838
limit = *olen;
839
else
840
limit = 0;
841
842
/* Determine string length, minus trailing NUL (if any) */
843
cstr_len = strnlen(cstr, cstr_size);
844
845
/* Parse the string data and write to output */
846
switch (otype) {
847
case BHND_NVRAM_TYPE_NULL:
848
/* Only an empty string may be represented as a NULL value */
849
if (cstr_len != 0)
850
return (EFTYPE);
851
852
*olen = 0;
853
return (0);
854
855
case BHND_NVRAM_TYPE_CHAR:
856
case BHND_NVRAM_TYPE_CHAR_ARRAY:
857
/* String must contain exactly 1 non-terminating-NUL character
858
* to be represented as a single char */
859
if (!bhnd_nvram_is_array_type(otype)) {
860
if (cstr_len != 1)
861
return (EFTYPE);
862
}
863
864
/* Copy out the characters directly (excluding trailing NUL) */
865
for (size_t i = 0; i < cstr_len; i++) {
866
if (limit > nbytes)
867
*((uint8_t *)outp + nbytes) = cstr[i];
868
nbytes++;
869
}
870
871
/* Provide required length */
872
*olen = nbytes;
873
if (limit < *olen && outp != NULL)
874
return (ENOMEM);
875
876
return (0);
877
878
case BHND_NVRAM_TYPE_BOOL:
879
case BHND_NVRAM_TYPE_BOOL_ARRAY: {
880
const char *p;
881
size_t plen;
882
bhnd_nvram_bool_t bval;
883
884
/* Trim leading/trailing whitespace */
885
p = cstr;
886
plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');
887
888
/* Parse string representation */
889
if (strncasecmp(p, "true", plen) == 0 ||
890
strncasecmp(p, "yes", plen) == 0 ||
891
strncmp(p, "1", plen) == 0)
892
{
893
bval = true;
894
} else if (strncasecmp(p, "false", plen) == 0 ||
895
strncasecmp(p, "no", plen) == 0 ||
896
strncmp(p, "0", plen) == 0)
897
{
898
bval = false;
899
} else {
900
/* Not a recognized boolean string */
901
return (EFTYPE);
902
}
903
904
/* Write to output */
905
nbytes = sizeof(bhnd_nvram_bool_t);
906
if (limit >= nbytes)
907
*((bhnd_nvram_bool_t *)outp) = bval;
908
909
/* Provide required length */
910
*olen = nbytes;
911
if (limit < *olen && outp != NULL)
912
return (ENOMEM);
913
914
return (0);
915
}
916
917
case BHND_NVRAM_TYPE_DATA: {
918
const char *p;
919
size_t plen, parsed_len;
920
int error;
921
922
/* Trim leading/trailing whitespace */
923
p = cstr;
924
plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');
925
926
/* Check for EFI-style hexadecimal byte array string format.
927
* Must have a 'H' prefix */
928
if (plen < 1 || bhnd_nv_toupper(*p) != 'H')
929
return (EFTYPE);
930
931
/* Skip leading 'H' */
932
p++;
933
plen--;
934
935
/* Parse the input string's two-char octets until the end
936
* of input is reached. The last octet may contain only
937
* one char */
938
while (plen > 0) {
939
uint8_t byte;
940
size_t byte_len = sizeof(byte);
941
942
/* Parse next two-character hex octet */
943
error = bhnd_nvram_parse_int(p, bhnd_nv_ummin(plen, 2),
944
16, &parsed_len, &byte, &byte_len, otype_base);
945
if (error) {
946
BHND_NV_DEBUG("error parsing '%.*s' as "
947
"integer: %d\n", BHND_NV_PRINT_WIDTH(plen),
948
p, error);
949
950
return (error);
951
}
952
953
/* Write to output */
954
if (limit > nbytes)
955
*((uint8_t *)outp + nbytes) = byte;
956
nbytes++;
957
958
/* Advance input */
959
p += parsed_len;
960
plen -= parsed_len;
961
}
962
963
/* Provide required length */
964
*olen = nbytes;
965
if (limit < *olen && outp != NULL)
966
return (ENOMEM);
967
968
return (0);
969
}
970
971
case BHND_NVRAM_TYPE_UINT8:
972
case BHND_NVRAM_TYPE_UINT8_ARRAY:
973
case BHND_NVRAM_TYPE_UINT16:
974
case BHND_NVRAM_TYPE_UINT16_ARRAY:
975
case BHND_NVRAM_TYPE_UINT32:
976
case BHND_NVRAM_TYPE_UINT32_ARRAY:
977
case BHND_NVRAM_TYPE_UINT64:
978
case BHND_NVRAM_TYPE_UINT64_ARRAY:
979
case BHND_NVRAM_TYPE_INT8:
980
case BHND_NVRAM_TYPE_INT8_ARRAY:
981
case BHND_NVRAM_TYPE_INT16:
982
case BHND_NVRAM_TYPE_INT16_ARRAY:
983
case BHND_NVRAM_TYPE_INT32:
984
case BHND_NVRAM_TYPE_INT32_ARRAY:
985
case BHND_NVRAM_TYPE_INT64:
986
case BHND_NVRAM_TYPE_INT64_ARRAY: {
987
const char *p;
988
size_t plen, parsed_len;
989
int error;
990
991
/* Trim leading/trailing whitespace */
992
p = cstr;
993
plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');
994
995
/* Try to parse the integer value */
996
error = bhnd_nvram_parse_int(p, plen, 0, &parsed_len, outp,
997
olen, otype_base);
998
if (error) {
999
BHND_NV_DEBUG("error parsing '%.*s' as integer: %d\n",
1000
BHND_NV_PRINT_WIDTH(plen), p, error);
1001
return (error);
1002
}
1003
1004
/* Do additional bytes remain unparsed? */
1005
if (plen != parsed_len) {
1006
BHND_NV_DEBUG("error parsing '%.*s' as a single "
1007
"integer value; trailing garbage '%.*s'\n",
1008
BHND_NV_PRINT_WIDTH(plen), p,
1009
BHND_NV_PRINT_WIDTH(plen-parsed_len), p+parsed_len);
1010
return (EFTYPE);
1011
}
1012
1013
return (0);
1014
}
1015
1016
case BHND_NVRAM_TYPE_STRING:
1017
case BHND_NVRAM_TYPE_STRING_ARRAY:
1018
/* Copy out the string representation as-is */
1019
*olen = cstr_size;
1020
1021
/* Need additional space for trailing NUL? */
1022
if (cstr_len == cstr_size)
1023
(*olen)++;
1024
1025
/* Skip output? */
1026
if (outp == NULL)
1027
return (0);
1028
1029
/* Verify required length */
1030
if (limit < *olen)
1031
return (ENOMEM);
1032
1033
/* Copy and NUL terminate */
1034
strncpy(outp, cstr, cstr_len);
1035
*((char *)outp + cstr_len) = '\0';
1036
1037
return (0);
1038
}
1039
1040
BHND_NV_PANIC("unknown type %s", bhnd_nvram_type_name(otype));
1041
}
1042
1043
/**
1044
* Standard integer encoding implementation.
1045
*/
1046
static int
1047
bhnd_nvram_val_encode_int(const void *inp, size_t ilen, bhnd_nvram_type itype,
1048
void *outp, size_t *olen, bhnd_nvram_type otype)
1049
{
1050
bhnd_nvram_type otype_base;
1051
size_t limit, nbytes;
1052
bool itype_signed, otype_signed, otype_int;
1053
union {
1054
uint64_t u64;
1055
int64_t i64;
1056
} intv;
1057
1058
BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("non-integer type"));
1059
1060
/* Determine output byte limit */
1061
if (outp != NULL)
1062
limit = *olen;
1063
else
1064
limit = 0;
1065
1066
/* Fetch output type info */
1067
otype_base = bhnd_nvram_base_type(otype);
1068
otype_int = bhnd_nvram_is_int_type(otype);
1069
otype_signed = bhnd_nvram_is_signed_type(otype_base);
1070
1071
/*
1072
* Promote integer value to a common 64-bit representation.
1073
*/
1074
switch (itype) {
1075
case BHND_NVRAM_TYPE_UINT8:
1076
if (ilen != sizeof(uint8_t))
1077
return (EFAULT);
1078
1079
itype_signed = false;
1080
intv.u64 = *(const uint8_t *)inp;
1081
break;
1082
1083
case BHND_NVRAM_TYPE_UINT16:
1084
if (ilen != sizeof(uint16_t))
1085
return (EFAULT);
1086
1087
itype_signed = false;
1088
intv.u64 = *(const uint16_t *)inp;
1089
break;
1090
1091
case BHND_NVRAM_TYPE_UINT32:
1092
if (ilen != sizeof(uint32_t))
1093
return (EFAULT);
1094
1095
itype_signed = false;
1096
intv.u64 = *(const uint32_t *)inp;
1097
break;
1098
1099
case BHND_NVRAM_TYPE_UINT64:
1100
if (ilen != sizeof(uint64_t))
1101
return (EFAULT);
1102
1103
itype_signed = false;
1104
intv.u64 = *(const uint64_t *)inp;
1105
break;
1106
1107
case BHND_NVRAM_TYPE_INT8:
1108
if (ilen != sizeof(int8_t))
1109
return (EFAULT);
1110
1111
itype_signed = true;
1112
intv.i64 = *(const int8_t *)inp;
1113
break;
1114
1115
case BHND_NVRAM_TYPE_INT16:
1116
if (ilen != sizeof(int16_t))
1117
return (EFAULT);
1118
1119
itype_signed = true;
1120
intv.i64 = *(const int16_t *)inp;
1121
break;
1122
1123
case BHND_NVRAM_TYPE_INT32:
1124
if (ilen != sizeof(int32_t))
1125
return (EFAULT);
1126
1127
itype_signed = true;
1128
intv.i64 = *(const int32_t *)inp;
1129
break;
1130
1131
case BHND_NVRAM_TYPE_INT64:
1132
if (ilen != sizeof(int32_t))
1133
return (EFAULT);
1134
1135
itype_signed = true;
1136
intv.i64 = *(const int32_t *)inp;
1137
break;
1138
1139
default:
1140
BHND_NV_PANIC("invalid type %d\n", itype);
1141
}
1142
1143
/* Perform signed/unsigned conversion */
1144
if (itype_signed && otype_int && !otype_signed) {
1145
if (intv.i64 < 0) {
1146
/* Can't represent negative value */
1147
BHND_NV_LOG("cannot represent %" PRId64 " as %s\n",
1148
intv.i64, bhnd_nvram_type_name(otype));
1149
1150
return (ERANGE);
1151
}
1152
1153
/* Convert to unsigned representation */
1154
intv.u64 = intv.i64;
1155
1156
} else if (!itype_signed && otype_int && otype_signed) {
1157
/* Handle unsigned -> signed coercions */
1158
if (intv.u64 > INT64_MAX) {
1159
/* Can't represent positive value */
1160
BHND_NV_LOG("cannot represent %" PRIu64 " as %s\n",
1161
intv.u64, bhnd_nvram_type_name(otype));
1162
return (ERANGE);
1163
}
1164
1165
/* Convert to signed representation */
1166
intv.i64 = intv.u64;
1167
}
1168
1169
/* Write output */
1170
switch (otype) {
1171
case BHND_NVRAM_TYPE_NULL:
1172
/* Cannot encode an integer value as NULL */
1173
return (EFTYPE);
1174
1175
case BHND_NVRAM_TYPE_BOOL: {
1176
bhnd_nvram_bool_t bval;
1177
1178
if (intv.u64 == 0 || intv.u64 == 1) {
1179
bval = intv.u64;
1180
} else {
1181
/* Encoding as a bool would lose information */
1182
return (ERANGE);
1183
}
1184
1185
nbytes = sizeof(bhnd_nvram_bool_t);
1186
if (limit >= nbytes)
1187
*((bhnd_nvram_bool_t *)outp) = bval;
1188
1189
break;
1190
}
1191
1192
case BHND_NVRAM_TYPE_CHAR:
1193
case BHND_NVRAM_TYPE_CHAR_ARRAY:
1194
case BHND_NVRAM_TYPE_DATA:
1195
case BHND_NVRAM_TYPE_UINT8:
1196
case BHND_NVRAM_TYPE_UINT8_ARRAY:
1197
if (intv.u64 > UINT8_MAX)
1198
return (ERANGE);
1199
1200
nbytes = sizeof(uint8_t);
1201
if (limit >= nbytes)
1202
*((uint8_t *)outp) = (uint8_t)intv.u64;
1203
break;
1204
1205
case BHND_NVRAM_TYPE_UINT16:
1206
case BHND_NVRAM_TYPE_UINT16_ARRAY:
1207
if (intv.u64 > UINT16_MAX)
1208
return (ERANGE);
1209
1210
nbytes = sizeof(uint16_t);
1211
if (limit >= nbytes)
1212
*((uint16_t *)outp) = (uint16_t)intv.u64;
1213
break;
1214
1215
case BHND_NVRAM_TYPE_UINT32:
1216
case BHND_NVRAM_TYPE_UINT32_ARRAY:
1217
if (intv.u64 > UINT32_MAX)
1218
return (ERANGE);
1219
1220
nbytes = sizeof(uint32_t);
1221
if (limit >= nbytes)
1222
*((uint32_t *)outp) = (uint32_t)intv.u64;
1223
break;
1224
1225
case BHND_NVRAM_TYPE_UINT64:
1226
case BHND_NVRAM_TYPE_UINT64_ARRAY:
1227
nbytes = sizeof(uint64_t);
1228
if (limit >= nbytes)
1229
*((uint64_t *)outp) = intv.u64;
1230
break;
1231
1232
case BHND_NVRAM_TYPE_INT8:
1233
case BHND_NVRAM_TYPE_INT8_ARRAY:
1234
if (intv.i64 < INT8_MIN || intv.i64 > INT8_MAX)
1235
return (ERANGE);
1236
1237
nbytes = sizeof(int8_t);
1238
if (limit >= nbytes)
1239
*((int8_t *)outp) = (int8_t)intv.i64;
1240
break;
1241
1242
case BHND_NVRAM_TYPE_INT16:
1243
case BHND_NVRAM_TYPE_INT16_ARRAY:
1244
if (intv.i64 < INT16_MIN || intv.i64 > INT16_MAX)
1245
return (ERANGE);
1246
1247
nbytes = sizeof(int16_t);
1248
if (limit >= nbytes)
1249
*((int16_t *)outp) = (int16_t)intv.i64;
1250
break;
1251
1252
case BHND_NVRAM_TYPE_INT32:
1253
case BHND_NVRAM_TYPE_INT32_ARRAY:
1254
if (intv.i64 < INT32_MIN || intv.i64 > INT32_MAX)
1255
return (ERANGE);
1256
1257
nbytes = sizeof(int32_t);
1258
if (limit >= nbytes)
1259
*((int32_t *)outp) = (int32_t)intv.i64;
1260
break;
1261
1262
case BHND_NVRAM_TYPE_INT64:
1263
case BHND_NVRAM_TYPE_INT64_ARRAY:
1264
nbytes = sizeof(int64_t);
1265
if (limit >= nbytes)
1266
*((int64_t *)outp) = intv.i64;
1267
break;
1268
1269
case BHND_NVRAM_TYPE_STRING:
1270
case BHND_NVRAM_TYPE_STRING_ARRAY: {
1271
ssize_t len;
1272
1273
/* Attempt to write the entry + NUL */
1274
if (otype_signed) {
1275
len = snprintf(outp, limit, "%" PRId64, intv.i64);
1276
} else {
1277
len = snprintf(outp, limit, "%" PRIu64, intv.u64);
1278
}
1279
1280
if (len < 0) {
1281
BHND_NV_LOG("snprintf() failed: %zd\n", len);
1282
return (EFTYPE);
1283
}
1284
1285
/* Set total length to the formatted string length, plus
1286
* trailing NUL */
1287
nbytes = len + 1;
1288
break;
1289
}
1290
1291
default:
1292
BHND_NV_LOG("unknown type %s\n", bhnd_nvram_type_name(otype));
1293
return (EFTYPE);
1294
}
1295
1296
/* Provide required length */
1297
*olen = nbytes;
1298
if (limit < *olen) {
1299
if (outp == NULL)
1300
return (0);
1301
1302
return (ENOMEM);
1303
}
1304
1305
return (0);
1306
}
1307
1308
/**
1309
* Encode the given @p value as @p otype, writing the result to @p outp.
1310
*
1311
* @param value The value to be encoded.
1312
* @param[out] outp On success, the value will be written to this
1313
* buffer. This argment may be NULL if the value is
1314
* not desired.
1315
* @param[in,out] olen The capacity of @p outp. On success, will be set
1316
* to the actual size of the requested value.
1317
* @param otype The data type to be written to @p outp.
1318
*
1319
* @retval 0 success
1320
* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
1321
* is too small to hold the encoded value.
1322
* @retval EFTYPE If value coercion from @p value to @p otype is
1323
* impossible.
1324
* @retval ERANGE If value coercion would overflow (or underflow) the
1325
* a @p otype representation.
1326
*/
1327
int
1328
bhnd_nvram_val_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
1329
bhnd_nvram_type otype)
1330
{
1331
/* Prefer format implementation */
1332
if (value->fmt->op_encode != NULL)
1333
return (value->fmt->op_encode(value, outp, olen, otype));
1334
1335
return (bhnd_nvram_val_generic_encode(value, outp, olen, otype));
1336
}
1337
1338
/**
1339
* Encode the given @p value's element as @p otype, writing the result to
1340
* @p outp.
1341
*
1342
* @param inp The element to be encoded. Must be a value
1343
* previously returned by bhnd_nvram_val_next()
1344
* or bhnd_nvram_val_elem().
1345
* @param ilen The size of @p inp, as returned by
1346
* bhnd_nvram_val_next() or bhnd_nvram_val_elem().
1347
* @param[out] outp On success, the value will be written to this
1348
* buffer. This argment may be NULL if the value is
1349
* not desired.
1350
* @param[in,out] olen The capacity of @p outp. On success, will be set
1351
* to the actual size of the requested value.
1352
* @param otype The data type to be written to @p outp.
1353
*
1354
* @retval 0 success
1355
* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
1356
* is too small to hold the encoded value.
1357
* @retval EFTYPE If value coercion from @p value to @p otype is
1358
* impossible.
1359
* @retval ERANGE If value coercion would overflow (or underflow) the
1360
* a @p otype representation.
1361
*/
1362
int
1363
bhnd_nvram_val_encode_elem(bhnd_nvram_val *value, const void *inp,
1364
size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
1365
{
1366
/* Prefer format implementation */
1367
if (value->fmt->op_encode_elem != NULL) {
1368
return (value->fmt->op_encode_elem(value, inp, ilen, outp,
1369
olen, otype));
1370
}
1371
1372
return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp,
1373
olen, otype));
1374
}
1375
1376
/**
1377
* Return the type, size, and a pointer to the internal representation
1378
* of @p value.
1379
*
1380
* @param value The value to be queried.
1381
* @param[out] olen Size of the returned data, in bytes.
1382
* @param[out] otype Data type.
1383
*/
1384
const void *
1385
bhnd_nvram_val_bytes(bhnd_nvram_val *value, size_t *olen,
1386
bhnd_nvram_type *otype)
1387
{
1388
/* Provide type and length */
1389
*otype = value->data_type;
1390
*olen = value->data_len;
1391
1392
switch (value->data_storage) {
1393
case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
1394
case BHND_NVRAM_VAL_DATA_EXT_STATIC:
1395
case BHND_NVRAM_VAL_DATA_EXT_WEAK:
1396
/* Return a pointer to external storage */
1397
return (value->data.ptr);
1398
1399
case BHND_NVRAM_VAL_DATA_INLINE:
1400
/* Return a pointer to inline storage */
1401
return (&value->data);
1402
1403
case BHND_NVRAM_VAL_DATA_NONE:
1404
BHND_NV_PANIC("uninitialized value");
1405
}
1406
1407
BHND_NV_PANIC("unknown storage type: %d", value->data_storage);
1408
}
1409
1410
/**
1411
* Iterate over all array elements in @p value.
1412
*
1413
* @param value The value to be iterated
1414
* @param prev A value pointer previously returned by
1415
* bhnd_nvram_val_next() or bhnd_nvram_val_elem(),
1416
* or NULL to begin iteration at the first element.
1417
* @param[in,out] olen If @p prev is non-NULL, @p olen must be a
1418
* pointer to the length previously returned by
1419
* bhnd_nvram_val_next() or bhnd_nvram_val_elem().
1420
* On success, will be set to the next element's
1421
* length, in bytes.
1422
*
1423
* @retval non-NULL A borrowed reference to the element data.
1424
* @retval NULL If the end of the element array is reached.
1425
*/
1426
const void *
1427
bhnd_nvram_val_next(bhnd_nvram_val *value, const void *prev, size_t *olen)
1428
{
1429
/* Prefer the format implementation */
1430
if (value->fmt->op_next != NULL)
1431
return (value->fmt->op_next(value, prev, olen));
1432
1433
return (bhnd_nvram_val_generic_next(value, prev, olen));
1434
}
1435
1436
/**
1437
* Return the value's data type.
1438
*
1439
* @param value The value to be queried.
1440
*/
1441
bhnd_nvram_type
1442
bhnd_nvram_val_type(bhnd_nvram_val *value)
1443
{
1444
return (value->data_type);
1445
}
1446
1447
/**
1448
* Return value's element data type.
1449
*
1450
* @param value The value to be queried.
1451
*/
1452
bhnd_nvram_type
1453
bhnd_nvram_val_elem_type(bhnd_nvram_val *value)
1454
{
1455
return (bhnd_nvram_base_type(value->data_type));
1456
}
1457
1458
/**
1459
* Return the total number of elements represented by @p value.
1460
*/
1461
size_t
1462
bhnd_nvram_val_nelem(bhnd_nvram_val *value)
1463
{
1464
const void *bytes;
1465
bhnd_nvram_type type;
1466
size_t nelem, len;
1467
int error;
1468
1469
/* Prefer format implementation */
1470
if (value->fmt->op_nelem != NULL)
1471
return (value->fmt->op_nelem(value));
1472
1473
/*
1474
* If a custom op_next() is defined, bhnd_nvram_value_nelem() almost
1475
* certainly cannot produce a valid element count; it assumes a standard
1476
* data format that may not apply when custom iteration is required.
1477
*
1478
* Instead, use bhnd_nvram_val_next() to parse the backing data and
1479
* produce a total count.
1480
*/
1481
if (value->fmt->op_next != NULL) {
1482
const void *next;
1483
1484
next = NULL;
1485
nelem = 0;
1486
while ((next = bhnd_nvram_val_next(value, next, &len)) != NULL)
1487
nelem++;
1488
1489
return (nelem);
1490
}
1491
1492
/* Otherwise, compute the standard element count */
1493
bytes = bhnd_nvram_val_bytes(value, &len, &type);
1494
if ((error = bhnd_nvram_value_nelem(bytes, len, type, &nelem))) {
1495
/* Should always succeed */
1496
BHND_NV_PANIC("error calculating element count for type '%s' "
1497
"with length %zu: %d\n", bhnd_nvram_type_name(type), len,
1498
error);
1499
}
1500
1501
return (nelem);
1502
}
1503
1504
/**
1505
* Generic implementation of bhnd_nvram_val_op_encode(), compatible with
1506
* all supported NVRAM data types.
1507
*/
1508
int
1509
bhnd_nvram_val_generic_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
1510
bhnd_nvram_type otype)
1511
{
1512
const void *inp;
1513
bhnd_nvram_type itype;
1514
size_t ilen;
1515
const void *next;
1516
bhnd_nvram_type otype_base;
1517
size_t limit, nelem, nbytes;
1518
size_t next_len;
1519
int error;
1520
1521
nbytes = 0;
1522
nelem = 0;
1523
otype_base = bhnd_nvram_base_type(otype);
1524
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
1525
1526
/*
1527
* Normally, an array type is not universally representable as
1528
* non-array type.
1529
*
1530
* As exceptions, we support conversion directly to/from:
1531
* - CHAR_ARRAY/STRING:
1532
* ->STRING Interpret the character array as a
1533
* non-NUL-terminated string.
1534
* ->CHAR_ARRAY Trim the trailing NUL from the string.
1535
*/
1536
#define BHND_NV_IS_ISO_CONV(_lhs, _rhs) \
1537
((itype == BHND_NVRAM_TYPE_ ## _lhs && \
1538
otype == BHND_NVRAM_TYPE_ ## _rhs) || \
1539
(itype == BHND_NVRAM_TYPE_ ## _rhs && \
1540
otype == BHND_NVRAM_TYPE_ ## _lhs))
1541
1542
if (BHND_NV_IS_ISO_CONV(CHAR_ARRAY, STRING)) {
1543
return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen,
1544
otype));
1545
}
1546
1547
#undef BHND_NV_IS_ISO_CONV
1548
1549
/*
1550
* If both input and output are non-array types, try to encode them
1551
* without performing element iteration.
1552
*/
1553
if (!bhnd_nvram_is_array_type(itype) &&
1554
!bhnd_nvram_is_array_type(otype))
1555
{
1556
return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen,
1557
otype));
1558
}
1559
1560
/* Determine output byte limit */
1561
if (outp != NULL)
1562
limit = *olen;
1563
else
1564
limit = 0;
1565
1566
/* Iterate over our array elements and encode as the requested
1567
* type */
1568
next = NULL;
1569
while ((next = bhnd_nvram_val_next(value, next, &next_len))) {
1570
void *elem_outp;
1571
size_t elem_nbytes;
1572
1573
/* If the output type is not an array type, we can only encode
1574
* one element */
1575
nelem++;
1576
if (nelem > 1 && !bhnd_nvram_is_array_type(otype)) {
1577
return (EFTYPE);
1578
}
1579
1580
/* Determine output offset / limit */
1581
if (nbytes >= limit) {
1582
elem_nbytes = 0;
1583
elem_outp = NULL;
1584
} else {
1585
elem_nbytes = limit - nbytes;
1586
elem_outp = (uint8_t *)outp + nbytes;
1587
}
1588
1589
/* Attempt encode */
1590
error = bhnd_nvram_val_encode_elem(value, next, next_len,
1591
elem_outp, &elem_nbytes, otype_base);
1592
1593
/* If encoding failed for any reason other than ENOMEM (which
1594
* we'll detect and report below), return immediately */
1595
if (error && error != ENOMEM)
1596
return (error);
1597
1598
/* Add to total length */
1599
if (SIZE_MAX - nbytes < elem_nbytes)
1600
return (EFTYPE); /* would overflow size_t */
1601
1602
nbytes += elem_nbytes;
1603
}
1604
1605
/* Provide the actual length */
1606
*olen = nbytes;
1607
1608
/* If no output was requested, nothing left to do */
1609
if (outp == NULL)
1610
return (0);
1611
1612
/* Otherwise, report a memory error if the output buffer was too
1613
* small */
1614
if (limit < nbytes)
1615
return (ENOMEM);
1616
1617
return (0);
1618
}
1619
1620
/**
1621
* Generic implementation of bhnd_nvram_val_op_encode_elem(), compatible with
1622
* all supported NVRAM data types.
1623
*/
1624
int
1625
bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val *value, const void *inp,
1626
size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
1627
{
1628
bhnd_nvram_type itype;
1629
1630
itype = bhnd_nvram_val_elem_type(value);
1631
switch (itype) {
1632
case BHND_NVRAM_TYPE_NULL:
1633
return (bhnd_nvram_val_encode_null(inp, ilen, itype, outp, olen,
1634
otype));
1635
1636
case BHND_NVRAM_TYPE_DATA:
1637
return (bhnd_nvram_val_encode_data(inp, ilen, itype, outp,
1638
olen, otype));
1639
1640
case BHND_NVRAM_TYPE_STRING:
1641
case BHND_NVRAM_TYPE_CHAR:
1642
return (bhnd_nvram_val_encode_string(inp, ilen, itype, outp,
1643
olen, otype));
1644
1645
case BHND_NVRAM_TYPE_BOOL:
1646
return (bhnd_nvram_val_encode_bool(inp, ilen, itype, outp, olen,
1647
otype));
1648
1649
case BHND_NVRAM_TYPE_UINT8:
1650
case BHND_NVRAM_TYPE_UINT16:
1651
case BHND_NVRAM_TYPE_UINT32:
1652
case BHND_NVRAM_TYPE_UINT64:
1653
case BHND_NVRAM_TYPE_INT8:
1654
case BHND_NVRAM_TYPE_INT16:
1655
case BHND_NVRAM_TYPE_INT32:
1656
case BHND_NVRAM_TYPE_INT64:
1657
return (bhnd_nvram_val_encode_int(inp, ilen, itype, outp, olen,
1658
otype));
1659
default:
1660
BHND_NV_PANIC("missing encode_elem() implementation");
1661
}
1662
}
1663
1664
/**
1665
* Generic implementation of bhnd_nvram_val_op_next(), compatible with
1666
* all supported NVRAM data types.
1667
*/
1668
const void *
1669
bhnd_nvram_val_generic_next(bhnd_nvram_val *value, const void *prev,
1670
size_t *olen)
1671
{
1672
const uint8_t *inp;
1673
bhnd_nvram_type itype;
1674
size_t ilen;
1675
1676
/* Iterate over the backing representation */
1677
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
1678
return (bhnd_nvram_value_array_next(inp, ilen, itype, prev, olen));
1679
}
1680
1681
/**
1682
* Initialize the representation of @p value with @p ptr.
1683
*
1684
* @param value The value to be initialized.
1685
* @param inp The external representation.
1686
* @param ilen The external representation length, in bytes.
1687
* @param itype The external representation's data type.
1688
* @param flags Value flags.
1689
*
1690
* @retval 0 success.
1691
* @retval ENOMEM if allocation fails
1692
* @retval EFTYPE if @p itype is not an array type, and @p ilen is not
1693
* equal to the size of a single element of @p itype.
1694
* @retval EFAULT if @p ilen is not correctly aligned for elements of
1695
* @p itype.
1696
*/
1697
static int
1698
bhnd_nvram_val_set(bhnd_nvram_val *value, const void *inp, size_t ilen,
1699
bhnd_nvram_type itype, uint32_t flags)
1700
{
1701
void *bytes;
1702
int error;
1703
1704
BHND_NVRAM_VAL_ASSERT_EMPTY(value);
1705
1706
/* Validate alignment */
1707
if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype)))
1708
return (error);
1709
1710
/* Reference the external data */
1711
if ((flags & BHND_NVRAM_VAL_BORROW_DATA) ||
1712
(flags & BHND_NVRAM_VAL_STATIC_DATA))
1713
{
1714
if (flags & BHND_NVRAM_VAL_STATIC_DATA)
1715
value->data_storage = BHND_NVRAM_VAL_DATA_EXT_STATIC;
1716
else
1717
value->data_storage = BHND_NVRAM_VAL_DATA_EXT_WEAK;
1718
1719
value->data.ptr = inp;
1720
value->data_type = itype;
1721
value->data_len = ilen;
1722
return (0);
1723
}
1724
1725
/* Fetch reference to (or allocate) an appropriately sized buffer */
1726
bytes = bhnd_nvram_val_alloc_bytes(value, ilen, itype, flags);
1727
if (bytes == NULL)
1728
return (ENOMEM);
1729
1730
/* Copy data */
1731
memcpy(bytes, inp, ilen);
1732
1733
return (0);
1734
}
1735
1736
/**
1737
* Initialize the internal inline representation of @p value with a copy of
1738
* the data referenced by @p inp of @p itype.
1739
*
1740
* If @p inp is NULL, @p itype and @p ilen will be validated, but no data will
1741
* be copied.
1742
*
1743
* @param value The value to be initialized.
1744
* @param inp The input data to be copied, or NULL to verify
1745
* that data of @p ilen and @p itype can be represented
1746
* inline.
1747
* @param ilen The size of the external buffer to be allocated.
1748
* @param itype The type of the external buffer to be allocated.
1749
*
1750
* @retval 0 success
1751
* @retval ENOMEM if @p ilen is too large to be represented inline.
1752
* @retval EFAULT if @p ilen is not correctly aligned for elements of
1753
* @p itype.
1754
*/
1755
static int
1756
bhnd_nvram_val_set_inline(bhnd_nvram_val *value, const void *inp, size_t ilen,
1757
bhnd_nvram_type itype)
1758
{
1759
BHND_NVRAM_VAL_ASSERT_EMPTY(value);
1760
1761
#define NV_STORE_INIT_INLINE() do { \
1762
value->data_len = ilen; \
1763
value->data_type = itype; \
1764
} while(0)
1765
1766
#define NV_STORE_INLINE(_type, _dest) do { \
1767
if (ilen != sizeof(_type)) \
1768
return (EFAULT); \
1769
\
1770
if (inp != NULL) { \
1771
value->data._dest[0] = *(const _type *)inp; \
1772
NV_STORE_INIT_INLINE(); \
1773
} \
1774
} while (0)
1775
1776
#define NV_COPY_ARRRAY_INLINE(_type, _dest) do { \
1777
if (ilen % sizeof(_type) != 0) \
1778
return (EFAULT); \
1779
\
1780
if (ilen > nitems(value->data. _dest)) \
1781
return (ENOMEM); \
1782
\
1783
if (inp == NULL) \
1784
return (0); \
1785
\
1786
memcpy(&value->data._dest, inp, ilen); \
1787
if (inp != NULL) { \
1788
memcpy(&value->data._dest, inp, ilen); \
1789
NV_STORE_INIT_INLINE(); \
1790
} \
1791
} while (0)
1792
1793
/* Attempt to copy to inline storage */
1794
switch (itype) {
1795
case BHND_NVRAM_TYPE_NULL:
1796
if (ilen != 0)
1797
return (EFAULT);
1798
1799
/* Nothing to copy */
1800
NV_STORE_INIT_INLINE();
1801
return (0);
1802
1803
case BHND_NVRAM_TYPE_CHAR:
1804
NV_STORE_INLINE(uint8_t, ch);
1805
return (0);
1806
1807
case BHND_NVRAM_TYPE_BOOL:
1808
NV_STORE_INLINE(bhnd_nvram_bool_t, b);
1809
return(0);
1810
1811
case BHND_NVRAM_TYPE_UINT8:
1812
case BHND_NVRAM_TYPE_INT8:
1813
NV_STORE_INLINE(uint8_t, u8);
1814
return (0);
1815
1816
case BHND_NVRAM_TYPE_UINT16:
1817
case BHND_NVRAM_TYPE_INT16:
1818
NV_STORE_INLINE(uint16_t, u16);
1819
return (0);
1820
1821
case BHND_NVRAM_TYPE_UINT32:
1822
case BHND_NVRAM_TYPE_INT32:
1823
NV_STORE_INLINE(uint32_t, u32);
1824
return (0);
1825
1826
case BHND_NVRAM_TYPE_UINT64:
1827
case BHND_NVRAM_TYPE_INT64:
1828
NV_STORE_INLINE(uint32_t, u32);
1829
return (0);
1830
1831
case BHND_NVRAM_TYPE_CHAR_ARRAY:
1832
NV_COPY_ARRRAY_INLINE(uint8_t, ch);
1833
return (0);
1834
1835
case BHND_NVRAM_TYPE_DATA:
1836
case BHND_NVRAM_TYPE_UINT8_ARRAY:
1837
case BHND_NVRAM_TYPE_INT8_ARRAY:
1838
NV_COPY_ARRRAY_INLINE(uint8_t, u8);
1839
return (0);
1840
1841
case BHND_NVRAM_TYPE_UINT16_ARRAY:
1842
case BHND_NVRAM_TYPE_INT16_ARRAY:
1843
NV_COPY_ARRRAY_INLINE(uint16_t, u16);
1844
return (0);
1845
1846
case BHND_NVRAM_TYPE_UINT32_ARRAY:
1847
case BHND_NVRAM_TYPE_INT32_ARRAY:
1848
NV_COPY_ARRRAY_INLINE(uint32_t, u32);
1849
return (0);
1850
1851
case BHND_NVRAM_TYPE_UINT64_ARRAY:
1852
case BHND_NVRAM_TYPE_INT64_ARRAY:
1853
NV_COPY_ARRRAY_INLINE(uint64_t, u64);
1854
return (0);
1855
1856
case BHND_NVRAM_TYPE_BOOL_ARRAY:
1857
NV_COPY_ARRRAY_INLINE(bhnd_nvram_bool_t, b);
1858
return(0);
1859
1860
case BHND_NVRAM_TYPE_STRING:
1861
case BHND_NVRAM_TYPE_STRING_ARRAY:
1862
if (ilen > sizeof(value->data.ch))
1863
return (ENOMEM);
1864
1865
if (inp != NULL) {
1866
memcpy(&value->data.ch, inp, ilen);
1867
NV_STORE_INIT_INLINE();
1868
}
1869
1870
return (0);
1871
}
1872
1873
#undef NV_STORE_INIT_INLINE
1874
#undef NV_STORE_INLINE
1875
#undef NV_COPY_ARRRAY_INLINE
1876
1877
BHND_NV_PANIC("unknown data type %d", itype);
1878
}
1879
1880
/**
1881
* Initialize the internal representation of @p value with a buffer allocation
1882
* of @p len and @p itype, returning a pointer to the allocated buffer.
1883
*
1884
* If a buffer of @p len and @p itype can be represented inline, no
1885
* external buffer will be allocated, and instead a pointer to the inline
1886
* data representation will be returned.
1887
*
1888
* @param value The value to be initialized.
1889
* @param ilen The size of the external buffer to be allocated.
1890
* @param itype The type of the external buffer to be allocated.
1891
* @param flags Value flags.
1892
*
1893
* @retval non-null The newly allocated buffer.
1894
* @retval NULL If allocation failed.
1895
* @retval NULL If @p value is an externally allocated instance.
1896
*/
1897
static void *
1898
bhnd_nvram_val_alloc_bytes(bhnd_nvram_val *value, size_t ilen,
1899
bhnd_nvram_type itype, uint32_t flags)
1900
{
1901
void *ptr;
1902
1903
BHND_NVRAM_VAL_ASSERT_EMPTY(value);
1904
1905
/* Can we use inline storage? */
1906
if (bhnd_nvram_val_set_inline(value, NULL, ilen, itype) == 0) {
1907
BHND_NV_ASSERT(sizeof(value->data) >= ilen,
1908
("ilen exceeds inline storage"));
1909
1910
value->data_type = itype;
1911
value->data_len = ilen;
1912
value->data_storage = BHND_NVRAM_VAL_DATA_INLINE;
1913
return (&value->data);
1914
}
1915
1916
/* Is allocation permitted? */
1917
if (!(flags & BHND_NVRAM_VAL_DYNAMIC))
1918
return (NULL);
1919
1920
/* Allocate external storage */
1921
if ((ptr = bhnd_nv_malloc(ilen)) == NULL)
1922
return (NULL);
1923
1924
value->data.ptr = ptr;
1925
value->data_len = ilen;
1926
value->data_type = itype;
1927
value->data_storage = BHND_NVRAM_VAL_DATA_EXT_ALLOC;
1928
1929
return (ptr);
1930
}
1931
1932