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