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