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