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