xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_value.c (revision 7f9dff23d3092aa33ad45b2b63e52469b3c13a6e)
1 /*-
2  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/sbuf.h>
35 
36 #ifdef _KERNEL
37 
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/systm.h>
41 
42 #include <machine/_inttypes.h>
43 
44 #else /* !_KERNEL */
45 
46 #include <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 
58 static void	*bhnd_nvram_val_alloc_bytes(bhnd_nvram_val_t *value,
59 					    size_t ilen, bhnd_nvram_type itype,
60 					    uint32_t flags);
61 static int	 bhnd_nvram_val_set(bhnd_nvram_val_t *value, const void *inp,
62 				    size_t ilen, bhnd_nvram_type itype,
63 				    uint32_t flags);
64 static int	 bhnd_nvram_val_set_inline(bhnd_nvram_val_t *value,
65 		     const void *inp, size_t ilen, bhnd_nvram_type itype);
66 
67 #define	BHND_NVRAM_VAL_INITIALIZER(_fmt, _storage)		\
68 	(bhnd_nvram_val_t) {					\
69 		.refs = 1,					\
70 		.val_storage = _storage,			\
71 		.fmt = _fmt,					\
72 		.data_storage = BHND_NVRAM_VAL_DATA_NONE,	\
73 	};
74 
75 
76 /** Assert that @p value's backing representation state has initialized
77  *  as empty. */
78 #define	BHND_NVRAM_VAL_ASSERT_EMPTY(_value)			\
79 	BHND_NV_ASSERT(						\
80 	    value->data_storage == BHND_NVRAM_VAL_DATA_NONE &&	\
81 	    value->data_len == 0 &&				\
82 	    value->data.ptr == NULL,				\
83 	    ("previously initialized value"))
84 
85 /* Common initialization support for bhnd_nvram_val_init() and
86  * bhnd_nvram_val_new() */
87 static int
88 bhnd_nvram_val_init_common(bhnd_nvram_val_t *value, bhnd_nvram_val_storage_t
89     val_storage, const bhnd_nvram_val_fmt_t *fmt, const void *inp, size_t ilen,
90     bhnd_nvram_type itype, uint32_t flags)
91 {
92 	void		*outp;
93 	bhnd_nvram_type	 otype;
94 	size_t		 olen;
95 	int		 error;
96 
97 	/* Determine expected data type, and allow the format to delegate to
98 	 * a new format instance */
99 	if (fmt != NULL && fmt->op_filter != NULL) {
100 		const bhnd_nvram_val_fmt_t *nfmt = fmt;
101 
102 		/* Use the filter function to determine whether direct
103 		 * initialization from is itype permitted */
104 		error = fmt->op_filter(&nfmt, inp, ilen, itype);
105 		if (error)
106 			return (error);
107 
108 		/* Retry initialization with new format? */
109 		if (nfmt != fmt) {
110 			return (bhnd_nvram_val_init_common(value, val_storage,
111 			    nfmt, inp, ilen, itype, flags));
112 		}
113 
114 		/* Value can be initialized with provided input type */
115 		otype = itype;
116 
117 	} else if (fmt != NULL) {
118 		/* Value must be initialized with the format's native
119 		 * type */
120 		otype = fmt->native_type;
121 
122 	} else {
123 		/* No format specified; we can initialize directly from the
124 		 * input data, and we'll handle all format operations
125 		 * internally. */
126 		otype = itype;
127 	}
128 
129 	/* Initialize value instance */
130 	*value = BHND_NVRAM_VAL_INITIALIZER(fmt, val_storage);
131 
132 	/* If input data already in native format, init directly. */
133 	if (otype == itype) {
134 		error = bhnd_nvram_val_set(value, inp, ilen, itype, flags);
135 		if (error)
136 			return (error);
137 
138 		return (0);
139 	}
140 
141 	/* Determine size when encoded in native format */
142 	error = bhnd_nvram_value_coerce(inp, ilen, itype, NULL, &olen, otype);
143 	if (error)
144 		return (error);
145 
146 	/* Fetch reference to (or allocate) an appropriately sized buffer */
147 	outp = bhnd_nvram_val_alloc_bytes(value, olen, otype, flags);
148 	if (outp == NULL)
149 		return (ENOMEM);
150 
151 	/* Perform encode */
152 	error = bhnd_nvram_value_coerce(inp, ilen, itype, outp, &olen, otype);
153 	if (error)
154 		return (error);
155 
156 	return (0);
157 }
158 
159 /**
160  * Initialize an externally allocated instance of @p value with @p fmt from the
161  * given @p inp buffer of @p itype and @p ilen.
162  *
163  * On success, the caller owns a reference to @p value, and is responsible for
164  * freeing any resources allocated for @p value via bhnd_nvram_val_release().
165  *
166  * @param	value	The externally allocated value instance to be
167  *			initialized.
168  * @param	fmt	The value's format, or NULL to use the default format
169  *			for @p itype.
170  * @param	inp	Input buffer.
171  * @param	ilen	Input buffer length.
172  * @param	itype	Input buffer type.
173  * @param	flags	Value flags (see BHND_NVRAM_VAL_*).
174  *
175  * @retval 0		success
176  * @retval ENOMEM	If allocation fails.
177  * @retval EFTYPE	If @p fmt initialization from @p itype is unsupported.
178  * @retval EFAULT	if @p ilen is not correctly aligned for elements of
179  *			@p itype.
180  * @retval ERANGE	If value coercion would overflow (or underflow) the
181  *			@p fmt representation.
182  */
183 int
184 bhnd_nvram_val_init(bhnd_nvram_val_t *value, const bhnd_nvram_val_fmt_t *fmt,
185     const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)
186 {
187 	int error;
188 
189 	error = bhnd_nvram_val_init_common(value, BHND_NVRAM_VAL_STORAGE_AUTO,
190 	    fmt, inp, ilen, itype, flags);
191 	if (error)
192 		bhnd_nvram_val_release(value);
193 
194 	return (error);
195 }
196 
197 /**
198  * Allocate a value instance with @p fmt, and attempt to initialize its internal
199  * representation from the given @p inp buffer of @p itype and @p ilen.
200  *
201  * On success, the caller owns a reference to @p value, and is responsible for
202  * freeing any resources allocated for @p value via bhnd_nvram_val_release().
203  *
204  * @param[out]	value	On success, the allocated value instance.
205  * @param	fmt	The value's format, or NULL to use the default format
206  *			for @p itype.
207  * @param	inp	Input buffer.
208  * @param	ilen	Input buffer length.
209  * @param	itype	Input buffer type.
210  * @param	flags	Value flags (see BHND_NVRAM_VAL_*).
211  *
212  * @retval 0		success
213  * @retval ENOMEM	If allocation fails.
214  * @retval EFTYPE	If @p fmt initialization from @p itype is unsupported.
215  * @retval EFAULT	if @p ilen is not correctly aligned for elements of
216  *			@p itype.
217  * @retval ERANGE	If value coercion would overflow (or underflow) the
218  *			@p fmt representation.
219  */
220 int
221 bhnd_nvram_val_new(bhnd_nvram_val_t **value, const bhnd_nvram_val_fmt_t *fmt,
222     const void *inp, size_t ilen, bhnd_nvram_type itype, uint32_t flags)
223 {
224 	int error;
225 
226 	/* Allocate new instance */
227 	if ((*value = bhnd_nv_malloc(sizeof(**value))) == NULL)
228 		return (ENOMEM);
229 
230 	/* Perform common initialization. */
231 	error = bhnd_nvram_val_init_common(*value,
232 	    BHND_NVRAM_VAL_STORAGE_DYNAMIC, fmt, inp, ilen, itype, flags);
233 	if (error) {
234 		/* Will also free() the value allocation */
235 		bhnd_nvram_val_release(*value);
236 	}
237 
238 	return (error);
239 }
240 
241 /**
242  * Copy or retain a reference to @p value.
243  *
244  * On success, the caller is responsible for freeing the result via
245  * bhnd_nvram_val_release().
246  *
247  * @param	value	The value to be copied (or retained).
248  *
249  * @retval bhnd_nvram_val_t	if @p value was successfully copied or retained.
250  * @retval NULL			if allocation failed.
251  */
252 bhnd_nvram_val_t *
253 bhnd_nvram_val_copy(bhnd_nvram_val_t *value)
254 {
255 	bhnd_nvram_val_t	*result;
256 	const void		*bytes;
257 	bhnd_nvram_type		 type;
258 	size_t			 len;
259 	uint32_t		 flags;
260 	int			 error;
261 
262 	/* If dynamically allocated, simply bump the reference count */
263 	if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC) {
264 		refcount_acquire(&value->refs);
265 		return (value);
266 	}
267 
268 	/* Otherwise, we need to perform an actual copy */
269 	BHND_NV_ASSERT(value->refs == 1, ("non-allocated value has "
270 	    "active refcount (%u)", value->refs));
271 
272 	/* Compute the new value's flags based on the source value */
273 	switch (value->data_storage) {
274 	case BHND_NVRAM_VAL_DATA_NONE:
275 	case BHND_NVRAM_VAL_DATA_INLINE:
276 	case BHND_NVRAM_VAL_DATA_EXT_WEAK:
277 	case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
278 		/* Copy the source data and permit additional allocation if the
279 		 * value cannot be represented inline */
280 		flags = BHND_NVRAM_VAL_COPY_DATA|BHND_NVRAM_VAL_DYNAMIC;
281 		break;
282 	case BHND_NVRAM_VAL_DATA_EXT_STATIC:
283 		flags = BHND_NVRAM_VAL_STATIC_DATA;
284 		break;
285 	default:
286 		BHND_NV_PANIC("invalid storage type: %d", value->data_storage);
287 	}
288 
289 	/* Allocate new value copy */
290 	bytes = bhnd_nvram_val_bytes(value, &len, &type);
291 	error = bhnd_nvram_val_new(&result, value->fmt, bytes, len, type,
292 	    flags);
293 	if (error) {
294 		BHND_NV_LOG("copy failed: %d", error);
295 		return (NULL);
296 	}
297 
298 	return (result);
299 }
300 
301 /**
302  * Release a reference to @p value.
303  *
304  * If this is the last reference, all associated resources will be freed.
305  *
306  * @param	value	The value to be released.
307  */
308 void
309 bhnd_nvram_val_release(bhnd_nvram_val_t *value)
310 {
311 	BHND_NV_ASSERT(value->refs >= 1, ("value over-released"));
312 
313 	/* Drop reference */
314 	if (!refcount_release(&value->refs))
315 		return;
316 
317 	/* Free allocated external representation data */
318 	if (value->data_storage == BHND_NVRAM_VAL_DATA_EXT_ALLOC)
319 		bhnd_nv_free(__DECONST(void *, value->data.ptr));
320 
321 	/* Free instance if dynamically allocated */
322 	if (value->val_storage == BHND_NVRAM_VAL_STORAGE_DYNAMIC)
323 		bhnd_nv_free(value);
324 }
325 
326 /**
327  * Standard string/char array/char encoding implementation.
328  *
329  * Input type must be one of:
330  * - BHND_NVRAM_TYPE_STRING
331  * - BHND_NVRAM_TYPE_CHAR
332  * - BHND_NVRAM_TYPE_CHAR_ARRAY
333  */
334 static int
335 bhnd_nvram_val_encode_string(void *outp, size_t *olen, bhnd_nvram_type otype,
336      const void *inp, size_t ilen, bhnd_nvram_type itype)
337 {
338 	const char	*cstr;
339 	bhnd_nvram_type	 otype_base;
340 	size_t		 cstr_size, cstr_len;
341 	size_t		 limit, nbytes;
342 
343 	BHND_NV_ASSERT(
344 	    itype == BHND_NVRAM_TYPE_STRING ||
345 	    itype == BHND_NVRAM_TYPE_CHAR ||
346 	    itype == BHND_NVRAM_TYPE_CHAR_ARRAY,
347 	    ("unsupported type: %d", itype));
348 
349 	cstr = inp;
350 	cstr_size = ilen;
351 	nbytes = 0;
352 	otype_base = bhnd_nvram_base_type(otype);
353 
354 	/* Determine output byte limit */
355 	if (outp != NULL)
356 		limit = *olen;
357 	else
358 		limit = 0;
359 
360 	/* Determine string length, minus trailing NUL (if any) */
361 	cstr_len = strnlen(cstr, cstr_size);
362 
363 	/* Parse the field data */
364 	switch (otype) {
365 	case BHND_NVRAM_TYPE_CHAR:
366 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
367 		/* String must contain exactly 1 non-terminating-NUL character
368 		 * to be represented as a single char */
369 		if (!bhnd_nvram_is_array_type(otype)) {
370 			if (cstr_len != 1)
371 				return (EFTYPE);
372 		}
373 
374 		/* Copy out the characters directly (excluding trailing NUL) */
375 		for (size_t i = 0; i < cstr_len; i++) {
376 			if (limit > nbytes)
377 				*((uint8_t *)outp + nbytes) = cstr[i];
378 			nbytes++;
379 		}
380 
381 		/* Provide required length */
382 		*olen = nbytes;
383 		if (limit < *olen && outp != NULL)
384 			return (ENOMEM);
385 
386 		return (0);
387 
388 	case BHND_NVRAM_TYPE_UINT8:
389 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
390 	case BHND_NVRAM_TYPE_UINT16:
391 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
392 	case BHND_NVRAM_TYPE_UINT32:
393 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
394 	case BHND_NVRAM_TYPE_UINT64:
395 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
396 	case BHND_NVRAM_TYPE_INT8:
397 	case BHND_NVRAM_TYPE_INT8_ARRAY:
398 	case BHND_NVRAM_TYPE_INT16:
399 	case BHND_NVRAM_TYPE_INT16_ARRAY:
400 	case BHND_NVRAM_TYPE_INT32:
401 	case BHND_NVRAM_TYPE_INT32_ARRAY:
402 	case BHND_NVRAM_TYPE_INT64:
403 	case BHND_NVRAM_TYPE_INT64_ARRAY: {
404 		const char	*p;
405 		size_t		 plen, parsed_len;
406 		int		 error;
407 
408 		/* Trim leading/trailing whitespace */
409 		p = cstr;
410 		plen = bhnd_nvram_trim_field(&p, cstr_len, '\0');
411 
412 		/* Try to parse the integer value */
413 		error = bhnd_nvram_parse_int(p, plen, 0, &parsed_len, outp,
414 		    olen, otype_base);
415 		if (error) {
416 			BHND_NV_DEBUG("error parsing '%.*s' as integer: %d\n",
417 			    BHND_NV_PRINT_WIDTH(plen), p, error);
418 			return (error);
419 		}
420 
421 		/* Do additional bytes remain unparsed? */
422 		if (plen != parsed_len) {
423 			BHND_NV_DEBUG("error parsing '%.*s' as a single "
424 			    "integer value; trailing garbage '%.*s'\n",
425 			    BHND_NV_PRINT_WIDTH(plen), p,
426 			    BHND_NV_PRINT_WIDTH(plen-parsed_len), p+parsed_len);
427 			return (EFTYPE);
428 		}
429 
430 		return (0);
431 	}
432 
433 	case BHND_NVRAM_TYPE_STRING:
434 	case BHND_NVRAM_TYPE_STRING_ARRAY:
435 		/* Copy out the string representation as-is */
436 		*olen = cstr_size;
437 
438 		/* Need additional space for trailing NUL? */
439 		if (cstr_len == cstr_size)
440 			(*olen)++;
441 
442 		/* Skip output? */
443 		if (outp == NULL)
444 			return (0);
445 
446 		/* Verify required length */
447 		if (limit < *olen)
448 			return (ENOMEM);
449 
450 		/* Copy and NUL terminate */
451 		strncpy(outp, cstr, cstr_len);
452 		*((char *)outp + cstr_len) = '\0';
453 
454 		return (0);
455 	}
456 
457 	BHND_NV_PANIC("unknown type %s", bhnd_nvram_type_name(otype));
458 }
459 
460 /**
461  * Standard integer encoding implementation.
462  */
463 static int
464 bhnd_nvram_val_encode_int(void *outp, size_t *olen, bhnd_nvram_type otype,
465      const void *inp, size_t ilen, bhnd_nvram_type itype)
466 {
467 	bhnd_nvram_type	 otype_base;
468 	size_t		 limit, nbytes;
469 	bool		 itype_signed, otype_signed, otype_int;
470 	union {
471 		uint64_t	u64;
472 		int64_t		i64;
473 	} intv;
474 
475 	BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("non-integer type"));
476 
477 	/* Determine output byte limit */
478 	if (outp != NULL)
479 		limit = *olen;
480 	else
481 		limit = 0;
482 
483 	/* Fetch output type info */
484 	otype_base = bhnd_nvram_base_type(otype);
485 	otype_int = bhnd_nvram_is_int_type(otype);
486 	otype_signed = bhnd_nvram_is_signed_type(otype_base);
487 
488 	/*
489 	 * Promote integer value to a common 64-bit representation.
490 	 */
491 	switch (itype) {
492 	case BHND_NVRAM_TYPE_UINT8:
493 		if (ilen != sizeof(uint8_t))
494 			return (EFAULT);
495 
496 		itype_signed = false;
497 		intv.u64 = *(const uint8_t *)inp;
498 		break;
499 
500 	case BHND_NVRAM_TYPE_UINT16:
501 		if (ilen != sizeof(uint16_t))
502 			return (EFAULT);
503 
504 		itype_signed = false;
505 		intv.u64 = *(const uint16_t *)inp;
506 		break;
507 
508 	case BHND_NVRAM_TYPE_UINT32:
509 		if (ilen != sizeof(uint32_t))
510 			return (EFAULT);
511 
512 		itype_signed = false;
513 		intv.u64 = *(const uint32_t *)inp;
514 		break;
515 
516 	case BHND_NVRAM_TYPE_UINT64:
517 		if (ilen != sizeof(uint64_t))
518 			return (EFAULT);
519 
520 		itype_signed = false;
521 		intv.u64 = *(const uint64_t *)inp;
522 		break;
523 
524 	case BHND_NVRAM_TYPE_INT8:
525 		if (ilen != sizeof(int8_t))
526 			return (EFAULT);
527 
528 		itype_signed = true;
529 		intv.i64 = *(const int8_t *)inp;
530 		break;
531 
532 	case BHND_NVRAM_TYPE_INT16:
533 		if (ilen != sizeof(int16_t))
534 			return (EFAULT);
535 
536 		itype_signed = true;
537 		intv.i64 = *(const int16_t *)inp;
538 		break;
539 
540 	case BHND_NVRAM_TYPE_INT32:
541 		if (ilen != sizeof(int32_t))
542 			return (EFAULT);
543 
544 		itype_signed = true;
545 		intv.i64 = *(const int32_t *)inp;
546 		break;
547 
548 	case BHND_NVRAM_TYPE_INT64:
549 		if (ilen != sizeof(int32_t))
550 			return (EFAULT);
551 
552 		itype_signed = true;
553 		intv.i64 = *(const int32_t *)inp;
554 		break;
555 
556 	default:
557 		BHND_NV_PANIC("invalid type %d\n", itype);
558 	}
559 
560 	/* Perform signed/unsigned conversion */
561 	if (itype_signed && otype_int && !otype_signed) {
562 		if (intv.i64 < 0) {
563 			/* Can't represent negative value */
564 			BHND_NV_LOG("cannot represent %" PRId64 " as %s\n",
565 			    intv.i64, bhnd_nvram_type_name(otype));
566 
567 			return (ERANGE);
568 		}
569 
570 		/* Convert to unsigned representation */
571 		intv.u64 = intv.i64;
572 
573 	} else if (!itype_signed && otype_int && otype_signed) {
574 		/* Handle unsigned -> signed coercions */
575 		if (intv.u64 > INT64_MAX) {
576 			/* Can't represent positive value */
577 			BHND_NV_LOG("cannot represent %" PRIu64 " as %s\n",
578 			    intv.u64, bhnd_nvram_type_name(otype));
579 			return (ERANGE);
580 		}
581 
582 		/* Convert to signed representation */
583 		intv.i64 = intv.u64;
584 	}
585 
586 	/* Write output */
587 	switch (otype) {
588 	case BHND_NVRAM_TYPE_CHAR:
589 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
590 	case BHND_NVRAM_TYPE_UINT8:
591 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
592 		if (intv.u64 > UINT8_MAX)
593 			return (ERANGE);
594 
595 		nbytes = sizeof(uint8_t);
596 		if (limit >= nbytes)
597 			*((uint8_t *)outp) = (uint8_t)intv.u64;
598 		break;
599 
600 	case BHND_NVRAM_TYPE_UINT16:
601 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
602 		if (intv.u64 > UINT16_MAX)
603 			return (ERANGE);
604 
605 		nbytes = sizeof(uint16_t);
606 		if (limit >= nbytes)
607 			*((uint16_t *)outp) = (uint16_t)intv.u64;
608 		break;
609 
610 	case BHND_NVRAM_TYPE_UINT32:
611 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
612 		if (intv.u64 > UINT32_MAX)
613 			return (ERANGE);
614 
615 		nbytes = sizeof(uint32_t);
616 		if (limit >= nbytes)
617 			*((uint32_t *)outp) = (uint32_t)intv.u64;
618 		break;
619 
620 	case BHND_NVRAM_TYPE_UINT64:
621 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
622 		nbytes = sizeof(uint64_t);
623 		if (limit >= nbytes)
624 			*((uint64_t *)outp) = intv.u64;
625 		break;
626 
627 	case BHND_NVRAM_TYPE_INT8:
628 	case BHND_NVRAM_TYPE_INT8_ARRAY:
629 		if (intv.i64 < INT8_MIN || intv.i64 > INT8_MAX)
630 			return (ERANGE);
631 
632 		nbytes = sizeof(int8_t);
633 		if (limit >= nbytes)
634 			*((int8_t *)outp) = (int8_t)intv.i64;
635 		break;
636 
637 	case BHND_NVRAM_TYPE_INT16:
638 	case BHND_NVRAM_TYPE_INT16_ARRAY:
639 		if (intv.i64 < INT16_MIN || intv.i64 > INT16_MAX)
640 			return (ERANGE);
641 
642 		nbytes = sizeof(int16_t);
643 		if (limit >= nbytes)
644 			*((int16_t *)outp) = (int16_t)intv.i64;
645 		break;
646 
647 	case BHND_NVRAM_TYPE_INT32:
648 	case BHND_NVRAM_TYPE_INT32_ARRAY:
649 		if (intv.i64 < INT32_MIN || intv.i64 > INT32_MAX)
650 			return (ERANGE);
651 
652 		nbytes = sizeof(int32_t);
653 		if (limit >= nbytes)
654 			*((int32_t *)outp) = (int32_t)intv.i64;
655 		break;
656 
657 	case BHND_NVRAM_TYPE_INT64:
658 	case BHND_NVRAM_TYPE_INT64_ARRAY:
659 		nbytes = sizeof(int64_t);
660 		if (limit >= nbytes)
661 			*((int64_t *)outp) = intv.i64;
662 		break;
663 
664 	case BHND_NVRAM_TYPE_STRING:
665 	case BHND_NVRAM_TYPE_STRING_ARRAY: {
666 		ssize_t len;
667 
668 		/* Attempt to write the entry + NUL */
669 		if (otype_signed) {
670 			len = snprintf(outp, limit, "%" PRId64, intv.i64);
671 		} else {
672 			len = snprintf(outp, limit, "%" PRIu64, intv.u64);
673 		}
674 
675 		if (len < 0) {
676 			BHND_NV_LOG("snprintf() failed: %zd\n", len);
677 			return (EFTYPE);
678 		}
679 
680 		/* Set total length to the formatted string length, plus
681 		 * trailing NUL */
682 		nbytes = len + 1;
683 		break;
684 	}
685 
686 	default:
687 		BHND_NV_LOG("unknown type %s\n", bhnd_nvram_type_name(otype));
688 		return (EFTYPE);
689 	}
690 
691 	/* Provide required length */
692 	*olen = nbytes;
693 	if (limit < *olen) {
694 		if (outp == NULL)
695 			return (0);
696 
697 		return (ENOMEM);
698 	}
699 
700 	return (0);
701 }
702 
703 /**
704  * Encode the given @p value as @p otype, writing the result to @p outp.
705  *
706  * @param		value	The value to be encoded.
707  * @param[out]		outp	On success, the value will be written to this
708  *				buffer. This argment may be NULL if the value is
709  *				not desired.
710  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
711  *				to the actual size of the requested value.
712  * @param		otype	The data type to be written to @p outp.
713  *
714  * @retval 0		success
715  * @retval ENOMEM	If the @p outp is non-NULL, and the provided @p olen
716  *			is too small to hold the encoded value.
717  * @retval EFTYPE	If value coercion from @p value to @p otype is
718  *			impossible.
719  * @retval ERANGE	If value coercion would overflow (or underflow) the
720  *			a @p otype representation.
721  */
722 int
723 bhnd_nvram_val_encode(bhnd_nvram_val_t *value, void *outp, size_t *olen,
724     bhnd_nvram_type otype)
725 {
726 	/* Prefer format implementation */
727 	if (value->fmt != NULL && value->fmt->op_encode != NULL)
728 		return (value->fmt->op_encode(value, outp, olen, otype));
729 
730 	return (bhnd_nvram_val_generic_encode(value, outp, olen, otype));
731 }
732 
733 /**
734  * Encode the given @p value's element as @p otype, writing the result to
735  * @p outp.
736  *
737  * @param		inp	The element to be be encoded. Must be a value
738  *				previously returned by bhnd_nvram_val_next()
739  *				or bhnd_nvram_val_elem().
740  * @param		ilen	The size of @p inp, as returned by
741  *				bhnd_nvram_val_next() or bhnd_nvram_val_elem().
742  * @param[out]		outp	On success, the value will be written to this
743  *				buffer. This argment may be NULL if the value is
744  *				not desired.
745  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
746  *				to the actual size of the requested value.
747  * @param		otype	The data type to be written to @p outp.
748  *
749  * @retval 0		success
750  * @retval ENOMEM	If the @p outp is non-NULL, and the provided @p olen
751  *			is too small to hold the encoded value.
752  * @retval EFTYPE	If value coercion from @p value to @p otype is
753  *			impossible.
754  * @retval ERANGE	If value coercion would overflow (or underflow) the
755  *			a @p otype representation.
756  */
757 int
758 bhnd_nvram_val_encode_elem(bhnd_nvram_val_t *value, const void *inp,
759     size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
760 {
761 	/* Prefer format implementation */
762 	if (value->fmt != NULL && value->fmt->op_encode_elem != NULL) {
763 		return (value->fmt->op_encode_elem(value, inp, ilen, outp,
764 		    olen, otype));
765 	}
766 
767 	return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen, outp,
768 	    olen, otype));
769 }
770 
771 /**
772  * Return the type, size, and a pointer to the internal representation
773  * of @p value.
774  *
775  * @param	value	The value to be queried.
776  * @param[out]	olen	Size of the returned data, in bytes.
777  * @param[out]	otype	Data type.
778  */
779 const void *
780 bhnd_nvram_val_bytes(bhnd_nvram_val_t *value, size_t *olen,
781     bhnd_nvram_type *otype)
782 {
783 	/* Provide type and length */
784 	*otype = value->data_type;
785 	*olen = value->data_len;
786 
787 	switch (value->data_storage) {
788 	case BHND_NVRAM_VAL_DATA_EXT_ALLOC:
789 	case BHND_NVRAM_VAL_DATA_EXT_STATIC:
790 	case BHND_NVRAM_VAL_DATA_EXT_WEAK:
791 		/* Return a pointer to external storage */
792 		return (value->data.ptr);
793 
794 	case BHND_NVRAM_VAL_DATA_INLINE:
795 		/* Return a pointer to inline storage */
796 		return (&value->data);
797 
798 	case BHND_NVRAM_VAL_DATA_NONE:
799 		BHND_NV_PANIC("uninitialized value");
800 	}
801 
802 	BHND_NV_PANIC("unknown storage type: %d", value->data_storage);
803 }
804 
805 /**
806  * Iterate over all array elements in @p value.
807  *
808  * @param		value	The value to be iterated
809  * @param		prev	A value pointer previously returned by
810  *				bhnd_nvram_val_next() or bhnd_nvram_val_elem(),
811  *				or NULL to begin iteration at the first element.
812  * @param[in,out]	len	If prev is non-NULL, len must be a pointer
813  *				to the length previously returned by
814  *				bhnd_nvram_val_next() or bhnd_nvram_val_elem().
815  *				On success, will be set to the next element's
816  *				length, in bytes.
817  *
818  * @retval non-NULL	A borrowed reference to the element data.
819  * @retval NULL		If the end of the element array is reached.
820  */
821 const void *
822 bhnd_nvram_val_next(bhnd_nvram_val_t *value, const void *prev, size_t *len)
823 {
824 	/* Prefer the format implementation */
825 	if (value->fmt != NULL && value->fmt->op_next != NULL)
826 		return (value->fmt->op_next(value, prev, len));
827 
828 	return (bhnd_nvram_val_generic_next(value, prev, len));
829 }
830 
831 /**
832  * Return value's element data type.
833  *
834  * @param	value	The value to be queried.
835  */
836 bhnd_nvram_type
837 bhnd_nvram_val_elem_type(bhnd_nvram_val_t *value)
838 {
839 	return (bhnd_nvram_base_type(value->data_type));
840 }
841 
842 /**
843  * Return the total number of elements represented by @p value.
844  */
845 size_t
846 bhnd_nvram_val_nelem(bhnd_nvram_val_t *value)
847 {
848 	const void	*bytes;
849 	bhnd_nvram_type	 type;
850 	size_t		 nelem, len;
851 	int		 error;
852 
853 	/* Prefer format implementation */
854 	if (value->fmt != NULL && value->fmt->op_nelem != NULL)
855 		return (value->fmt->op_nelem(value));
856 
857 	/*
858 	 * If a custom op_next() is defined, bhnd_nvram_value_nelem() almost
859 	 * certainly cannot produce a valid element count; it assumes a standard
860 	 * data format that may not apply when custom iteration is required.
861 	 *
862 	 * Instead, use bhnd_nvram_val_next() to parse the backing data and
863 	 * produce a total count.
864 	 */
865 	if (value->fmt != NULL && value->fmt->op_next != NULL) {
866 		const void *next;
867 
868 		next = NULL;
869 		nelem = 0;
870 		while ((next = bhnd_nvram_val_next(value, next, &len)) != NULL)
871 			nelem++;
872 
873 		return (nelem);
874 	}
875 
876 	/* Otherwise, compute the standard element count */
877 	bytes = bhnd_nvram_val_bytes(value, &len, &type);
878 	if ((error = bhnd_nvram_value_nelem(type, bytes, len, &nelem))) {
879 		/* Should always succeed */
880 		BHND_NV_PANIC("error calculating element count for type '%s' "
881 		    "with length %zu: %d\n", bhnd_nvram_type_name(type), len,
882 		    error);
883 	}
884 
885 	return (nelem);
886 }
887 
888 /**
889  * Generic implementation of bhnd_nvram_val_op_encode(), compatible with
890  * all supported NVRAM data types.
891  */
892 int
893 bhnd_nvram_val_generic_encode(bhnd_nvram_val_t *value, void *outp, size_t *olen,
894     bhnd_nvram_type otype)
895 {
896 	const void	*inp;
897 	bhnd_nvram_type	 itype;
898 	size_t		 ilen;
899 	const void	*next;
900 	bhnd_nvram_type	 otype_base;
901 	size_t		 limit, nelem, nbytes;
902 	size_t		 next_len;
903 	int		 error;
904 
905 	nbytes = 0;
906 	nelem = 0;
907 	otype_base = bhnd_nvram_base_type(otype);
908 
909 	/*
910 	 * Normally, a rank polymorphic type like a character array would not
911 	 * be representable as a rank 1 type.
912 	 *
913 	 * As a special-cased exception, we can support conversion directly
914 	 * from CHAR_ARRAY to STRING by treating the character array as a
915 	 * non-NUL-terminated string.
916 	 *
917 	 * This conversion is isomorphic; we also support conversion directly
918 	 * from a STRING to a CHAR_ARRAY by the same mechanism.
919 	 */
920 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
921 	if ((itype == BHND_NVRAM_TYPE_CHAR_ARRAY &&
922 	     otype == BHND_NVRAM_TYPE_STRING) ||
923 	    (itype == BHND_NVRAM_TYPE_STRING &&
924 	     otype == BHND_NVRAM_TYPE_CHAR_ARRAY))
925 	{
926 		return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen,
927 		    otype));
928 	}
929 
930 	/*
931 	 * If both input and output are non-array types, try to encode them
932 	 * without performing element iteration.
933 	 */
934 	if (!bhnd_nvram_is_array_type(itype) &&
935 	    !bhnd_nvram_is_array_type(otype))
936 	{
937 		return (bhnd_nvram_val_encode_elem(value, inp, ilen, outp, olen,
938 		    otype));
939 	}
940 
941 	/* Determine output byte limit */
942 	if (outp != NULL)
943 		limit = *olen;
944 	else
945 		limit = 0;
946 
947 	/* Iterate over our array elements and encode as the requested
948 	 * type */
949 	next = NULL;
950 	while ((next = bhnd_nvram_val_next(value, next, &next_len))) {
951 		void			*elem_outp;
952 		size_t			 elem_nbytes;
953 
954 		/* If the output type is not an array type, we can only encode
955 		 * one element */
956 		nelem++;
957 		if (nelem > 1 && !bhnd_nvram_is_array_type(otype)) {
958 			return (EFTYPE);
959 		}
960 
961 		/* Determine output offset / limit */
962 		if (nbytes >= limit) {
963 			elem_nbytes = 0;
964 			elem_outp = NULL;
965 		} else {
966 			elem_nbytes = limit - nbytes;
967 			elem_outp = (uint8_t *)outp + nbytes;
968 		}
969 
970 		/* Attempt encode */
971 		error = bhnd_nvram_val_encode_elem(value, next, next_len,
972 		    elem_outp, &elem_nbytes, otype_base);
973 
974 		/* If encoding failed for any reason other than ENOMEM (which
975 		 * we'll detect and report below), return immediately */
976 		if (error && error != ENOMEM)
977 			return (error);
978 
979 		/* Add to total length */
980 		if (SIZE_MAX - nbytes < elem_nbytes)
981 			return (EFTYPE); /* would overflow size_t */
982 
983 		nbytes += elem_nbytes;
984 	}
985 
986 	/* Provide the actual length */
987 	*olen = nbytes;
988 
989 	/* If no output was requested, nothing left to do */
990 	if (outp == NULL)
991 		return (0);
992 
993 	/* Otherwise, report a memory error if the output buffer was too
994 	 * small */
995 	if (limit < nbytes)
996 		return (ENOMEM);
997 
998 	return (0);
999 }
1000 
1001 /**
1002  * Generic implementation of bhnd_nvram_val_op_encode_elem(), compatible with
1003  * all supported NVRAM data types.
1004  */
1005 int
1006 bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val_t *value, const void *inp,
1007     size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
1008 {
1009 	bhnd_nvram_type itype;
1010 
1011 	itype = bhnd_nvram_val_elem_type(value);
1012 	switch (itype) {
1013 	case BHND_NVRAM_TYPE_STRING:
1014 	case BHND_NVRAM_TYPE_CHAR:
1015 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
1016 		return (bhnd_nvram_val_encode_string(outp, olen, otype, inp,
1017 		    ilen, itype));
1018 
1019 	case BHND_NVRAM_TYPE_UINT8:
1020 	case BHND_NVRAM_TYPE_UINT16:
1021 	case BHND_NVRAM_TYPE_UINT32:
1022 	case BHND_NVRAM_TYPE_UINT64:
1023 	case BHND_NVRAM_TYPE_INT8:
1024 	case BHND_NVRAM_TYPE_INT16:
1025 	case BHND_NVRAM_TYPE_INT32:
1026 	case BHND_NVRAM_TYPE_INT64:
1027 		return (bhnd_nvram_val_encode_int(outp, olen, otype, inp, ilen,
1028 		    itype));
1029 
1030 	default:
1031 		BHND_NV_PANIC("missing encode_elem() implementation");
1032 	}
1033 }
1034 
1035 /**
1036  * Generic implementation of bhnd_nvram_val_op_next(), compatible with
1037  * all supported NVRAM data types.
1038  */
1039 const void *
1040 bhnd_nvram_val_generic_next(bhnd_nvram_val_t *value, const void *prev,
1041     size_t *len)
1042 {
1043 	const uint8_t	*inp;
1044 	const uint8_t	*next;
1045 	bhnd_nvram_type	 itype;
1046 	size_t		 ilen;
1047 	size_t		 offset;
1048 
1049 	/* Otherwise, default to iterating over the backing representation
1050 	 * according to its native representation */
1051 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
1052 
1053 	/* First element */
1054 	if (prev == NULL) {
1055 		/* Zero-length array? */
1056 		if (ilen == 0)
1057 			return (NULL);
1058 
1059 		*len = bhnd_nvram_value_size(itype, inp, ilen, 1);
1060 		return (inp);
1061 	}
1062 
1063 	/* Advance to next element */
1064 	BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep"));
1065 	next = (const uint8_t *)prev + *len;
1066 	offset = (size_t)(next - inp);
1067 
1068 	if (offset >= ilen) {
1069 		/* Hit end of the array */
1070 		return (NULL);
1071 	}
1072 
1073 	/* Determine element size */
1074 	*len = bhnd_nvram_value_size(itype, next, ilen - offset, 1);
1075 	if (ilen - offset < *len)
1076 		BHND_NV_PANIC("short element -- misaligned representation");
1077 
1078 	return (next);
1079 }
1080 
1081 /**
1082  * Initialize the representation of @p value with @p ptr.
1083  *
1084  * If @p value is an externally allocated instance and the representation
1085  * cannot be represented inline, the given data will not be copied, and @p ptr
1086  * must remain valid for the lifetime of @p value.
1087  *
1088  * Otherwise, @p value will be initialized with a copy of the @p ptr.
1089  *
1090  * @param	value	The value to be initialized.
1091  * @param	inp	The external representation.
1092  * @param	ilen	The external representation length, in bytes.
1093  * @param	itype	The external representation's data type.
1094  * @param	flags	Value flags.
1095  *
1096  * @retval 0		success.
1097  * @retval ENOMEM	if allocation fails
1098  * @retval EFTYPE	if @p itype is not an array type, and @p ilen is not
1099  *			equal to the size of a single element of @p itype.
1100  * @retval EFAULT	if @p ilen is not correctly aligned for elements of
1101  *			@p itype.
1102  */
1103 static int
1104 bhnd_nvram_val_set(bhnd_nvram_val_t *value, const void *inp, size_t ilen,
1105     bhnd_nvram_type itype, uint32_t flags)
1106 {
1107 	void	*bytes;
1108 
1109 	BHND_NVRAM_VAL_ASSERT_EMPTY(value);
1110 
1111 	/* Reference the external data */
1112 	if ((flags & BHND_NVRAM_VAL_BORROW_DATA) ||
1113 	    (flags & BHND_NVRAM_VAL_STATIC_DATA))
1114 	{
1115 		if (flags & BHND_NVRAM_VAL_BORROW_DATA)
1116 			value->data_storage = BHND_NVRAM_VAL_DATA_EXT_WEAK;
1117 		else
1118 			value->data_storage = BHND_NVRAM_VAL_DATA_EXT_STATIC;
1119 
1120 		value->data.ptr = inp;
1121 		value->data_type = itype;
1122 		value->data_len = ilen;
1123 		return (0);
1124 	}
1125 
1126 	/* Fetch reference to (or allocate) an appropriately sized buffer */
1127 	bytes = bhnd_nvram_val_alloc_bytes(value, ilen, itype, flags);
1128 	if (bytes == NULL)
1129 		return (ENOMEM);
1130 
1131 	/* Copy data */
1132 	memcpy(bytes, inp, ilen);
1133 
1134 	return (0);
1135 }
1136 
1137 /**
1138  * Initialize the internal inline representation of @p value with a copy of
1139  * the data referenced by @p inp of @p itype.
1140  *
1141  * If @p inp is NULL, @p itype and @p ilen will be validated, but no data will
1142  * be copied.
1143  *
1144  * @param	value	The value to be initialized.
1145  * @param	inp	The input data to be copied, or NULL to verify
1146  *			that data of @p ilen and @p itype can be represented
1147  *			inline.
1148  * @param	ilen	The size of the external buffer to be allocated.
1149  * @param	itype	The type of the external buffer to be allocated.
1150  *
1151  * @retval 0		success
1152  * @retval ENOMEM	if @p ilen is too large to be represented inline.
1153  * @retval EFAULT	if @p ilen is not correctly aligned for elements of
1154  *			@p itype.
1155  */
1156 static int
1157 bhnd_nvram_val_set_inline(bhnd_nvram_val_t *value, const void *inp, size_t ilen,
1158     bhnd_nvram_type itype)
1159 {
1160 	BHND_NVRAM_VAL_ASSERT_EMPTY(value);
1161 
1162 #define	NV_STORE_INIT_INLINE()	do {					\
1163 	value->data_len = ilen;						\
1164 } while(0)
1165 
1166 #define	NV_STORE_INLINE(_type, _dest)	do {				\
1167 	if (ilen != sizeof(_type))					\
1168 		return (EFAULT);					\
1169 									\
1170 	if (inp != NULL) {						\
1171 		value->data._dest[0] = *(const _type *)inp;		\
1172 		NV_STORE_INIT_INLINE();					\
1173 	}								\
1174 } while (0)
1175 
1176 #define	NV_COPY_ARRRAY_INLINE(_type, _dest)	do {		\
1177 	if (ilen % sizeof(_type) != 0)				\
1178 		return (EFAULT);				\
1179 								\
1180 	if (ilen > nitems(value->data. _dest))			\
1181 		return (ENOMEM);				\
1182 								\
1183 	if (inp == NULL)					\
1184 		return (0);					\
1185 								\
1186 	memcpy(&value->data._dest, inp, ilen);			\
1187 	if (inp != NULL) {					\
1188 		memcpy(&value->data._dest, inp, ilen);		\
1189 		NV_STORE_INIT_INLINE();				\
1190 	}							\
1191 } while (0)
1192 
1193 	/* Attempt to copy to inline storage */
1194 	switch (itype) {
1195 	case BHND_NVRAM_TYPE_CHAR:
1196 		NV_STORE_INLINE(uint8_t, ch);
1197 		return (0);
1198 
1199 	case BHND_NVRAM_TYPE_UINT8:
1200 	case BHND_NVRAM_TYPE_INT8:
1201 		NV_STORE_INLINE(uint8_t, u8);
1202 		return (0);
1203 
1204 	case BHND_NVRAM_TYPE_UINT16:
1205 	case BHND_NVRAM_TYPE_INT16:
1206 		NV_STORE_INLINE(uint16_t, u16);
1207 		return (0);
1208 
1209 	case BHND_NVRAM_TYPE_UINT32:
1210 	case BHND_NVRAM_TYPE_INT32:
1211 		NV_STORE_INLINE(uint32_t, u32);
1212 		return (0);
1213 
1214 	case BHND_NVRAM_TYPE_UINT64:
1215 	case BHND_NVRAM_TYPE_INT64:
1216 		NV_STORE_INLINE(uint32_t, u32);
1217 		return (0);
1218 
1219 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
1220 		NV_COPY_ARRRAY_INLINE(uint8_t, ch);
1221 		return (0);
1222 
1223 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
1224 	case BHND_NVRAM_TYPE_INT8_ARRAY:
1225 		NV_COPY_ARRRAY_INLINE(uint8_t, u8);
1226 		return (0);
1227 
1228 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
1229 	case BHND_NVRAM_TYPE_INT16_ARRAY:
1230 		NV_COPY_ARRRAY_INLINE(uint16_t, u16);
1231 		return (0);
1232 
1233 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
1234 	case BHND_NVRAM_TYPE_INT32_ARRAY:
1235 		NV_COPY_ARRRAY_INLINE(uint32_t, u32);
1236 		return (0);
1237 
1238 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
1239 	case BHND_NVRAM_TYPE_INT64_ARRAY:
1240 		NV_COPY_ARRRAY_INLINE(uint64_t, u64);
1241 		return (0);
1242 
1243 	case BHND_NVRAM_TYPE_STRING:
1244 	case BHND_NVRAM_TYPE_STRING_ARRAY:
1245 		if (ilen > sizeof(value->data.ch))
1246 			return (ENOMEM);
1247 
1248 		if (inp != NULL) {
1249 			memcpy(&value->data.ch, inp, ilen);
1250 			NV_STORE_INIT_INLINE();
1251 		}
1252 
1253 		return (0);
1254 	}
1255 
1256 #undef	NV_STORE_INIT_INLINE
1257 #undef	NV_STORE_INLINE
1258 #undef	NV_COPY_ARRRAY_INLINE
1259 
1260 	BHND_NV_PANIC("unknown data type %d", itype);
1261 }
1262 
1263 /**
1264  * Initialize the internal representation of @p value with a buffer allocation
1265  * of @p len and @p itype, returning a pointer to the allocated buffer.
1266  *
1267  * If a buffer of @p len and @p itype can be represented inline, no
1268  * external buffer will be allocated, and instead a pointer to the inline
1269  * data representation will be returned.
1270  *
1271  * @param	value	The value to be initialized.
1272  * @param	ilen	The size of the external buffer to be allocated.
1273  * @param	itype	The type of the external buffer to be allocated.
1274  * @param	flags	Value flags.
1275  *
1276  * @retval non-null	The newly allocated buffer.
1277  * @retval NULL		If allocation failed.
1278  * @retval NULL		If @p value is an externally allocated instance.
1279  */
1280 static void *
1281 bhnd_nvram_val_alloc_bytes(bhnd_nvram_val_t *value, size_t ilen,
1282     bhnd_nvram_type itype, uint32_t flags)
1283 {
1284 	void *ptr;
1285 
1286 	BHND_NVRAM_VAL_ASSERT_EMPTY(value);
1287 
1288 	/* Can we use inline storage? */
1289 	if (bhnd_nvram_val_set_inline(value, NULL, ilen, itype) == 0) {
1290 		BHND_NV_ASSERT(sizeof(value->data) >= ilen,
1291 		    ("ilen exceeds inline storage"));
1292 
1293 		value->data_type = itype;
1294 		value->data_len = ilen;
1295 		value->data_storage = BHND_NVRAM_VAL_DATA_INLINE;
1296 		return (&value->data);
1297 	}
1298 
1299 	/* Is allocation permitted? */
1300 	if (!(flags & BHND_NVRAM_VAL_DYNAMIC))
1301 		return (NULL);
1302 
1303 	/* Allocate external storage */
1304 	if ((ptr = bhnd_nv_malloc(ilen)) == NULL)
1305 		return (NULL);
1306 
1307 	value->data.ptr = ptr;
1308 	value->data_len = ilen;
1309 	value->data_type = itype;
1310 	value->data_storage = BHND_NVRAM_VAL_DATA_EXT_ALLOC;
1311 
1312 	return (ptr);
1313 }
1314