xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_value.c (revision 0957b409)
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 }
1937