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