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