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