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