xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_subr.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 
35 #ifdef _KERNEL
36 
37 #include <sys/ctype.h>
38 #include <sys/kernel.h>
39 #include <sys/limits.h>
40 #include <sys/malloc.h>
41 #include <sys/systm.h>
42 
43 #include <machine/_inttypes.h>
44 
45 #else /* !_KERNEL */
46 
47 #include <ctype.h>
48 #include <errno.h>
49 #include <inttypes.h>
50 #include <limits.h>
51 #include <stdbool.h>
52 #include <stdio.h>
53 #include <stdint.h>
54 #include <stdlib.h>
55 #include <string.h>
56 
57 #endif /* _KERNEL */
58 
59 #include "bhnd_nvram_io.h"
60 #include "bhnd_nvram_private.h"
61 #include "bhnd_nvram_value.h"
62 
63 #include "bhnd_nvram_map_data.h"
64 
65 /*
66  * Common NVRAM/SPROM support, including NVRAM variable map
67  * lookup.
68  */
69 
70 #ifdef _KERNEL
71 MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data");
72 #endif
73 
74 /** signed/unsigned 32-bit integer value storage */
75 union bhnd_nvram_int_storage {
76 	uint32_t	u32;
77 	int32_t		s32;
78 };
79 
80 /*
81  * CRC-8 lookup table used to checksum SPROM and NVRAM data via
82  * bhnd_nvram_crc8().
83  *
84  * Generated with following parameters:
85  * 	polynomial:	CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1)
86  * 	reflected bits:	false
87  * 	reversed:	true
88  */
89 const uint8_t bhnd_nvram_crc8_tab[] = {
90 	0x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3,
91 	0x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46,
92 	0x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f,
93 	0x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b,
94 	0x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77,
95 	0x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09,
96 	0x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91,
97 	0x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01,
98 	0x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38,
99 	0xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19,
100 	0x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f,
101 	0xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5,
102 	0x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d,
103 	0x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82,
104 	0xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1,
105 	0x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5,
106 	0x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3,
107 	0x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87,
108 	0xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1,
109 	0x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5,
110 	0xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6,
111 	0x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49,
112 	0x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1,
113 	0x26, 0x68, 0x9f
114 };
115 
116 /**
117  * Return a human readable name for @p type.
118  *
119  * @param type The type to query.
120  */
121 const char *
122 bhnd_nvram_type_name(bhnd_nvram_type type)
123 {
124 	switch (type) {
125 	case BHND_NVRAM_TYPE_UINT8:
126 		return ("uint8");
127 	case BHND_NVRAM_TYPE_UINT16:
128 		return ("uint16");
129 	case BHND_NVRAM_TYPE_UINT32:
130 		return ("uint32");
131 	case BHND_NVRAM_TYPE_UINT64:
132 		return ("uint64");
133 	case BHND_NVRAM_TYPE_CHAR:
134 		return ("char");
135 	case BHND_NVRAM_TYPE_INT8:
136 		return ("int8");
137 	case BHND_NVRAM_TYPE_INT16:
138 		return ("int16");
139 	case BHND_NVRAM_TYPE_INT32:
140 		return ("int32");
141 	case BHND_NVRAM_TYPE_INT64:
142 		return ("int64");
143 	case BHND_NVRAM_TYPE_STRING:
144 		return ("string");
145 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
146 		return ("uint8[]");
147 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
148 		return ("uint16[]");
149 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
150 		return ("uint32[]");
151 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
152 		return ("uint64[]");
153 	case BHND_NVRAM_TYPE_INT8_ARRAY:
154 		return ("int8[]");
155 	case BHND_NVRAM_TYPE_INT16_ARRAY:
156 		return ("int16[]");
157 	case BHND_NVRAM_TYPE_INT32_ARRAY:
158 		return ("int32[]");
159 	case BHND_NVRAM_TYPE_INT64_ARRAY:
160 		return ("int64[]");
161 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
162 		return ("char[]");
163 	case BHND_NVRAM_TYPE_STRING_ARRAY:
164 		return ("string[]");
165 	}
166 
167 	/* Quiesce gcc4.2 */
168 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
169 }
170 
171 /**
172  * Return true if @p type is a signed integer type, false otherwise.
173  *
174  * Will return false for all array types.
175  *
176  * @param type The type to query.
177  */
178 bool
179 bhnd_nvram_is_signed_type(bhnd_nvram_type type)
180 {
181 	switch (type) {
182 	case BHND_NVRAM_TYPE_INT8:
183 	case BHND_NVRAM_TYPE_INT16:
184 	case BHND_NVRAM_TYPE_INT32:
185 	case BHND_NVRAM_TYPE_INT64:
186 		BHND_NV_ASSERT(bhnd_nvram_is_int_type(type), ("non-int type?"));
187 		return (true);
188 
189 	case BHND_NVRAM_TYPE_CHAR:
190 	case BHND_NVRAM_TYPE_UINT8:
191 	case BHND_NVRAM_TYPE_UINT16:
192 	case BHND_NVRAM_TYPE_UINT32:
193 	case BHND_NVRAM_TYPE_UINT64:
194 	case BHND_NVRAM_TYPE_STRING:
195 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
196 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
197 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
198 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
199 	case BHND_NVRAM_TYPE_INT8_ARRAY:
200 	case BHND_NVRAM_TYPE_INT16_ARRAY:
201 	case BHND_NVRAM_TYPE_INT32_ARRAY:
202 	case BHND_NVRAM_TYPE_INT64_ARRAY:
203 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
204 	case BHND_NVRAM_TYPE_STRING_ARRAY:
205 		return (false);
206 	}
207 
208 	/* Quiesce gcc4.2 */
209 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
210 }
211 
212 /**
213  * Return true if @p type is an unsigned integer type, false otherwise.
214  *
215  * @param type The type to query.
216  *
217  * @return Will return false for all array types.
218  * @return Will return true for BHND_NVRAM_TYPE_CHAR.
219  */
220 bool
221 bhnd_nvram_is_unsigned_type(bhnd_nvram_type type)
222 {
223 	/* If an integer type, must be either signed or unsigned */
224 	if (!bhnd_nvram_is_int_type(type))
225 		return (false);
226 
227 	return (!bhnd_nvram_is_signed_type(type));
228 }
229 
230 /**
231  * Return true if bhnd_nvram_is_signed_type() or bhnd_nvram_is_unsigned_type()
232  * returns true for @p type.
233  *
234  * @param type The type to query.
235  */
236 bool
237 bhnd_nvram_is_int_type(bhnd_nvram_type type)
238 {
239 	switch (type) {
240 	case BHND_NVRAM_TYPE_UINT8:
241 	case BHND_NVRAM_TYPE_UINT16:
242 	case BHND_NVRAM_TYPE_UINT32:
243 	case BHND_NVRAM_TYPE_UINT64:
244 	case BHND_NVRAM_TYPE_INT8:
245 	case BHND_NVRAM_TYPE_INT16:
246 	case BHND_NVRAM_TYPE_INT32:
247 	case BHND_NVRAM_TYPE_INT64:
248 		return (true);
249 
250 	case BHND_NVRAM_TYPE_CHAR:
251 	case BHND_NVRAM_TYPE_STRING:
252 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
253 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
254 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
255 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
256 	case BHND_NVRAM_TYPE_INT8_ARRAY:
257 	case BHND_NVRAM_TYPE_INT16_ARRAY:
258 	case BHND_NVRAM_TYPE_INT32_ARRAY:
259 	case BHND_NVRAM_TYPE_INT64_ARRAY:
260 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
261 	case BHND_NVRAM_TYPE_STRING_ARRAY:
262 		return (false);
263 	}
264 
265 	/* Quiesce gcc4.2 */
266 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
267 }
268 
269 /**
270  * Return true if @p type is an array type, false otherwise.
271  *
272  * @param type The type to query.
273  */
274 bool
275 bhnd_nvram_is_array_type(bhnd_nvram_type type)
276 {
277 	switch (type) {
278 	case BHND_NVRAM_TYPE_UINT8:
279 	case BHND_NVRAM_TYPE_UINT16:
280 	case BHND_NVRAM_TYPE_UINT32:
281 	case BHND_NVRAM_TYPE_UINT64:
282 	case BHND_NVRAM_TYPE_INT8:
283 	case BHND_NVRAM_TYPE_INT16:
284 	case BHND_NVRAM_TYPE_INT32:
285 	case BHND_NVRAM_TYPE_INT64:
286 	case BHND_NVRAM_TYPE_CHAR:
287 	case BHND_NVRAM_TYPE_STRING:
288 		return (false);
289 
290 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
291 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
292 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
293 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
294 	case BHND_NVRAM_TYPE_INT8_ARRAY:
295 	case BHND_NVRAM_TYPE_INT16_ARRAY:
296 	case BHND_NVRAM_TYPE_INT32_ARRAY:
297 	case BHND_NVRAM_TYPE_INT64_ARRAY:
298 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
299 	case BHND_NVRAM_TYPE_STRING_ARRAY:
300 		return (true);
301 	}
302 
303 	/* Quiesce gcc4.2 */
304 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
305 }
306 
307 /**
308  * If @p type is an array type, return the base element type. Otherwise,
309  * returns @p type.
310  *
311  * @param type The type to query.
312  */
313 bhnd_nvram_type
314 bhnd_nvram_base_type(bhnd_nvram_type type)
315 {
316 	switch (type) {
317 	case BHND_NVRAM_TYPE_UINT8:
318 	case BHND_NVRAM_TYPE_UINT16:
319 	case BHND_NVRAM_TYPE_UINT32:
320 	case BHND_NVRAM_TYPE_UINT64:
321 	case BHND_NVRAM_TYPE_INT8:
322 	case BHND_NVRAM_TYPE_INT16:
323 	case BHND_NVRAM_TYPE_INT32:
324 	case BHND_NVRAM_TYPE_INT64:
325 	case BHND_NVRAM_TYPE_CHAR:
326 	case BHND_NVRAM_TYPE_STRING:
327 		return (type);
328 
329 	case BHND_NVRAM_TYPE_UINT8_ARRAY:	return (BHND_NVRAM_TYPE_UINT8);
330 	case BHND_NVRAM_TYPE_UINT16_ARRAY:	return (BHND_NVRAM_TYPE_UINT16);
331 	case BHND_NVRAM_TYPE_UINT32_ARRAY:	return (BHND_NVRAM_TYPE_UINT32);
332 	case BHND_NVRAM_TYPE_UINT64_ARRAY:	return (BHND_NVRAM_TYPE_UINT64);
333 	case BHND_NVRAM_TYPE_INT8_ARRAY:	return (BHND_NVRAM_TYPE_INT8);
334 	case BHND_NVRAM_TYPE_INT16_ARRAY:	return (BHND_NVRAM_TYPE_INT16);
335 	case BHND_NVRAM_TYPE_INT32_ARRAY:	return (BHND_NVRAM_TYPE_INT32);
336 	case BHND_NVRAM_TYPE_INT64_ARRAY:	return (BHND_NVRAM_TYPE_INT64);
337 	case BHND_NVRAM_TYPE_CHAR_ARRAY:	return (BHND_NVRAM_TYPE_CHAR);
338 	case BHND_NVRAM_TYPE_STRING_ARRAY:	return (BHND_NVRAM_TYPE_STRING);
339 	}
340 
341 	/* Quiesce gcc4.2 */
342 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
343 }
344 
345 /**
346  * Calculate the number of elements represented by a value of @p len bytes
347  * with @p type.
348  *
349  * @param	type	The value type.
350  * @param	data	The actual data to be queried, or NULL if unknown.
351  * @param	len	The length in bytes of @p data, or if @p data is NULL,
352  *			the expected length in bytes.
353  * @param[out]	nelem	On success, the number of elements. If @p type is not
354  *			a fixed width type (e.g. BHND_NVRAM_TYPE_STRING_ARRAY),
355  *			and @p data is NULL, an @p nelem value of 0 will be
356  *			returned.
357  *
358  * @retval 0		success
359  * @retval EFTYPE	if @p type is not an array type, and @p len is not
360  *			equal to the size of a single element of @p type.
361  * @retval EFAULT	if @p len is not correctly aligned for elements of
362  *			@p type.
363  */
364 int
365 bhnd_nvram_value_nelem(bhnd_nvram_type type, const void *data, size_t len,
366     size_t *nelem)
367 {
368 	bhnd_nvram_type	base_type;
369 	size_t		base_size;
370 
371 	/* Length must be aligned to the element size */
372 	base_type = bhnd_nvram_base_type(type);
373 	base_size = bhnd_nvram_value_size(base_type, NULL, 0, 1);
374 	if (base_size != 0 && len % base_size != 0)
375 		return (EFAULT);
376 
377 	switch (type) {
378 	case BHND_NVRAM_TYPE_STRING:
379 	case BHND_NVRAM_TYPE_STRING_ARRAY: {
380 		const char	*p;
381 		size_t		 nleft;
382 
383 		/* Cannot determine the element count without parsing
384 		 * the actual data */
385 		if (data == NULL) {
386 			*nelem = 0;
387 			return (0);
388 		}
389 
390 		/* Iterate over the NUL-terminated strings to calculate
391 		 * total element count */
392 		p = data;
393 		nleft = len;
394 		*nelem = 0;
395 		while (nleft > 0) {
396 			size_t slen;
397 
398 			/* Increment element count */
399 			(*nelem)++;
400 
401 			/* If not a string array, data must not contain more
402 			 * than one entry. */
403 			if (!bhnd_nvram_is_array_type(type) && *nelem > 1)
404 				return (EFTYPE);
405 
406 			/* Determine string length */
407 			slen = strnlen(p, nleft);
408 			nleft -= slen;
409 
410 			/* Advance input */
411 			p += slen;
412 
413 			/* Account for trailing NUL, if we haven't hit the end
414 			 * of the input */
415 			if (nleft > 0) {
416 				nleft--;
417 				p++;
418 			}
419 		}
420 
421 		return (0);
422 	}
423 	case BHND_NVRAM_TYPE_INT8:
424 	case BHND_NVRAM_TYPE_UINT8:
425 	case BHND_NVRAM_TYPE_CHAR:
426 	case BHND_NVRAM_TYPE_INT16:
427 	case BHND_NVRAM_TYPE_UINT16:
428 	case BHND_NVRAM_TYPE_INT32:
429 	case BHND_NVRAM_TYPE_UINT32:
430 	case BHND_NVRAM_TYPE_INT64:
431 	case BHND_NVRAM_TYPE_UINT64:
432 		/* Length must be equal to the size of exactly one
433 		 * element (arrays can represent zero elements -- non-array
434 		 * types cannot) */
435 		if (len != base_size)
436 			return (EFTYPE);
437 		*nelem = 1;
438 		return (0);
439 
440 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
441 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
442 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
443 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
444 	case BHND_NVRAM_TYPE_INT8_ARRAY:
445 	case BHND_NVRAM_TYPE_INT16_ARRAY:
446 	case BHND_NVRAM_TYPE_INT32_ARRAY:
447 	case BHND_NVRAM_TYPE_INT64_ARRAY:
448 	case BHND_NVRAM_TYPE_CHAR_ARRAY:
449 		BHND_NV_ASSERT(base_size != 0, ("invalid base size"));
450 		*nelem = len / base_size;
451 		return (0);
452 	}
453 
454 	/* Quiesce gcc4.2 */
455 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
456 }
457 
458 /**
459  * Return the size, in bytes, of a value of @p type with @p nelem elements.
460  *
461  * @param	type	The value type.
462  * @param	data	The actual data to be queried, or NULL if unknown. If
463  *			NULL and the base type is not a fixed width type
464  *			(e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned.
465  * @param	nbytes	The size of @p data, in bytes, or 0 if @p data is NULL.
466  * @param	nelem	The number of elements. If @p type is not an array type,
467  *			this value must be 1.
468  *
469  * @retval 0		If @p type has a variable width, and @p data is NULL.
470  * @retval 0		If a @p nelem value greater than 1 is provided for a
471  *			non-array @p type.
472  * @retval 0		If a @p nelem value of 0 is provided.
473  * @retval 0		If the result would exceed the maximum value
474  *			representable by size_t.
475  * @retval non-zero	The size, in bytes, of @p type with @p nelem elements.
476  */
477 size_t
478 bhnd_nvram_value_size(bhnd_nvram_type type, const void *data, size_t nbytes,
479     size_t nelem)
480 {
481 	/* If nelem 0, nothing to do */
482 	if (nelem == 0)
483 		return (0);
484 
485 	/* Non-array types must have an nelem value of 1 */
486 	if (!bhnd_nvram_is_array_type(type) && nelem != 1)
487 		return (0);
488 
489 	switch (type) {
490 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
491 	case BHND_NVRAM_TYPE_UINT16_ARRAY:
492 	case BHND_NVRAM_TYPE_UINT32_ARRAY:
493 	case BHND_NVRAM_TYPE_UINT64_ARRAY:
494 	case BHND_NVRAM_TYPE_INT8_ARRAY:
495 	case BHND_NVRAM_TYPE_INT16_ARRAY:
496 	case BHND_NVRAM_TYPE_INT32_ARRAY:
497 	case BHND_NVRAM_TYPE_INT64_ARRAY:
498 	case BHND_NVRAM_TYPE_CHAR_ARRAY: {
499 		bhnd_nvram_type	base_type;
500 		size_t		base_size;
501 
502 		base_type = bhnd_nvram_base_type(type);
503 		base_size = bhnd_nvram_value_size(base_type, NULL, 0, 1);
504 
505 		/* Would nelem * base_size overflow? */
506 		if (SIZE_MAX / nelem < base_size) {
507 			BHND_NV_LOG("cannot represent size %s * %zu\n",
508 			    bhnd_nvram_type_name(base_type), nelem);
509 			return (0);
510 		}
511 
512 		return (nelem * base_size);
513 	}
514 
515 	case BHND_NVRAM_TYPE_STRING_ARRAY: {
516 		const char	*p;
517 		size_t		 total_size;
518 
519 		if (data == NULL)
520 			return (0);
521 
522 		/* Iterate over the NUL-terminated strings to calculate
523 		 * total byte length */
524 		p = data;
525 		total_size = 0;
526 		for (size_t i = 0; i < nelem; i++) {
527 			size_t	elem_size;
528 
529 			elem_size = strnlen(p, nbytes - total_size);
530 			p += elem_size;
531 
532 			/* Check for (and skip) terminating NUL */
533 			if (total_size < nbytes && *p == '\0') {
534 				elem_size++;
535 				p++;
536 			}
537 
538 			/* Would total_size + elem_size overflow?
539 			 *
540 			 * A memory range larger than SIZE_MAX shouldn't be,
541 			 * possible, but include the check for completeness */
542 			if (SIZE_MAX - total_size < elem_size)
543 				return (0);
544 
545 			total_size += elem_size;
546 		}
547 
548 		return (total_size);
549 	}
550 
551 	case BHND_NVRAM_TYPE_STRING: {
552 		size_t size;
553 
554 		if (data == NULL)
555 			return (0);
556 
557 		/* Find length */
558 		size = strnlen(data, nbytes);
559 
560 		/* Is there a terminating NUL, or did we just hit the
561 		 * end of the string input */
562 		if (size < nbytes)
563 			size++;
564 
565 		return (size);
566 	}
567 	case BHND_NVRAM_TYPE_INT8:
568 	case BHND_NVRAM_TYPE_UINT8:
569 	case BHND_NVRAM_TYPE_CHAR:
570 		return (sizeof(uint8_t));
571 
572 	case BHND_NVRAM_TYPE_INT16:
573 	case BHND_NVRAM_TYPE_UINT16:
574 		return (sizeof(uint16_t));
575 
576 	case BHND_NVRAM_TYPE_INT32:
577 	case BHND_NVRAM_TYPE_UINT32:
578 		return (sizeof(uint32_t));
579 
580 	case BHND_NVRAM_TYPE_UINT64:
581 	case BHND_NVRAM_TYPE_INT64:
582 		return (sizeof(uint64_t));
583 	}
584 
585 	/* Quiesce gcc4.2 */
586 	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
587 }
588 
589 /**
590  * Iterate over all strings in the @p inp string array.
591  *
592  * @param	inp	The string array to be iterated. This must be a buffer
593  *			of one or more NUL-terminated strings --
594  *			@see BHND_NVRAM_TYPE_STRING_ARRAY.
595  * @param	ilen	The size, in bytes, of @p inp, including any
596  *			terminating NUL character(s).
597  * @param	prev	The value previously returned by
598  *			bhnd_nvram_string_array_next(), or NULL to begin
599  *			iteration.
600  *
601  * @retval non-NULL	A reference to the next NUL-terminated string
602  * @retval NULL		If the end of the string array is reached.
603  */
604 const char *
605 bhnd_nvram_string_array_next(const char *inp, size_t ilen, const char *prev)
606 {
607 	size_t nremain, plen;
608 
609 	if (ilen == 0)
610 		return (NULL);
611 
612 	if (prev == NULL)
613 		return (inp);
614 
615 	/* Advance to next value */
616 	BHND_NV_ASSERT(prev >= inp, ("invalid prev pointer"));
617 	BHND_NV_ASSERT(prev < (inp+ilen), ("invalid prev pointer"));
618 
619 	nremain = ilen - (size_t)(prev - inp);
620 	plen = strnlen(prev, nremain);
621 	nremain -= plen;
622 
623 	/* Only a trailing NUL remains? */
624 	if (nremain <= 1)
625 		return (NULL);
626 
627 	return (prev + plen + 1);
628 }
629 
630 /**
631  * Format a string representation of @p inp using @p fmt, with, writing the
632  * result to @p outp.
633  *
634  * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
635  *
636  * @param		fmt	The format string.
637  * @param		inp	The value to be formatted.
638  * @param		ilen	The size of @p inp, in bytes.
639  * @param		itype	The type of @p inp.
640  * @param[out]		outp	On success, the string value will be written to
641  *				this buffer. This argment may be NULL if the
642  *				value is not desired.
643  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
644  *				to the actual size of the formatted string.
645  *
646  * @retval 0		success
647  * @retval EINVAL	If @p fmt contains unrecognized format string
648  *			specifiers.
649  * @retval ENOMEM	If the @p outp is non-NULL, and the provided @p olen
650  *			is too small to hold the encoded value.
651  * @retval EFTYPE	If value coercion from @p inp to a string value via
652  *			@p fmt is unsupported.
653  * @retval ERANGE	If value coercion of @p value would overflow (or
654  *			underflow) the representation defined by @p fmt.
655  */
656 int
657 bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen,
658     bhnd_nvram_type itype, char *outp, size_t *olen, ...)
659 {
660 	va_list	ap;
661 	int	error;
662 
663 	va_start(ap, olen);
664 	error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap);
665 	va_end(ap);
666 
667 	return (error);
668 }
669 
670 /**
671  * Format a string representation of @p inp using @p fmt, with, writing the
672  * result to @p outp.
673  *
674  * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
675  *
676  * @param		fmt	The format string.
677  * @param		inp	The value to be formatted.
678  * @param		ilen	The size of @p inp, in bytes.
679  * @param		itype	The type of @p inp.
680  * @param[out]		outp	On success, the string value will be written to
681  *				this buffer. This argment may be NULL if the
682  *				value is not desired.
683  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
684  *				to the actual size of the formatted string.
685  * @param		ap	Argument list.
686  *
687  * @retval 0		success
688  * @retval EINVAL	If @p fmt contains unrecognized format string
689  *			specifiers.
690  * @retval ENOMEM	If the @p outp is non-NULL, and the provided @p olen
691  *			is too small to hold the encoded value.
692  * @retval EFTYPE	If value coercion from @p inp to a string value via
693  *			@p fmt is unsupported.
694  * @retval ERANGE	If value coercion of @p value would overflow (or
695  *			underflow) the representation defined by @p fmt.
696  */
697 int
698 bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen,
699     bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap)
700 {
701 	bhnd_nvram_val_t	val;
702 	int			error;
703 
704 	/* Map input buffer as a value instance */
705 	error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
706 	    BHND_NVRAM_VAL_BORROW_DATA);
707 	if (error)
708 		return (error);
709 
710 	/* Attempt to format the value */
711 	error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap);
712 
713 	/* Clean up */
714 	bhnd_nvram_val_release(&val);
715 	return (error);
716 }
717 
718 /* used by bhnd_nvram_find_vardefn() */
719 static int
720 bhnd_nvram_find_vardefn_compare(const void *key, const void *rhs)
721 {
722 	const struct bhnd_nvram_vardefn *r = rhs;
723 
724 	return (strcmp((const char *)key, r->name));
725 }
726 
727 /**
728  * Find and return the variable definition for @p varname, if any.
729  *
730  * @param varname variable name
731  *
732  * @retval bhnd_nvram_vardefn If a valid definition for @p varname is found.
733  * @retval NULL If no definition for @p varname is found.
734  */
735 const struct bhnd_nvram_vardefn *
736 bhnd_nvram_find_vardefn(const char *varname)
737 {
738 	return (bsearch(varname, bhnd_nvram_vardefns, bhnd_nvram_num_vardefns,
739 	    sizeof(bhnd_nvram_vardefns[0]), bhnd_nvram_find_vardefn_compare));
740 }
741 
742 /**
743  * Return the variable ID for a variable definition.
744  *
745  * @param defn Variable definition previously returned by
746  * bhnd_nvram_find_vardefn() or bhnd_nvram_get_vardefn().
747  */
748 size_t
749 bhnd_nvram_get_vardefn_id(const struct bhnd_nvram_vardefn *defn)
750 {
751 	BHND_NV_ASSERT(
752 	    defn >= bhnd_nvram_vardefns &&
753 	    defn <= &bhnd_nvram_vardefns[bhnd_nvram_num_vardefns-1],
754 	    ("invalid variable definition pointer %p", defn));
755 
756 	return (defn - bhnd_nvram_vardefns);
757 }
758 
759 /**
760  * Return the variable definition with the given @p id, or NULL
761  * if no such variable ID is defined.
762  *
763  * @param id variable ID.
764  *
765  * @retval bhnd_nvram_vardefn If a valid definition for @p id is found.
766  * @retval NULL If no definition for @p id is found.
767  */
768 const struct bhnd_nvram_vardefn *
769 bhnd_nvram_get_vardefn(size_t id)
770 {
771 	if (id >= bhnd_nvram_num_vardefns)
772 		return (NULL);
773 
774 	return (&bhnd_nvram_vardefns[id]);
775 }
776 
777 /**
778  * Validate an NVRAM variable name.
779  *
780  * Scans for special characters (path delimiters, value delimiters, path
781  * alias prefixes), returning false if the given name cannot be used
782  * as a relative NVRAM key.
783  *
784  * @param name A relative NVRAM variable name to validate.
785  * @param name_len The length of @p name, in bytes.
786  *
787  * @retval true If @p name is a valid relative NVRAM key.
788  * @retval false If @p name should not be used as a relative NVRAM key.
789  */
790 bool
791 bhnd_nvram_validate_name(const char *name, size_t name_len)
792 {
793 	size_t limit;
794 
795 	limit = strnlen(name, name_len);
796 	if (limit == 0)
797 		return (false);
798 
799 	/* Disallow path alias prefixes ([0-9]+:.*) */
800 	if (limit >= 2 && bhnd_nv_isdigit(*name)) {
801 		for (const char *p = name; (size_t)(p - name) < limit; p++) {
802 			if (bhnd_nv_isdigit(*p))
803 				continue;
804 			else if (*p == ':')
805 				return (false);
806 			else
807 				break;
808 		}
809 	}
810 
811 	/* Scan for special characters */
812 	for (const char *p = name; (size_t)(p - name) < limit; p++) {
813 		switch (*p) {
814 		case '/':	/* path delimiter */
815 		case '=':	/* key=value delimiter */
816 			return (false);
817 
818 		default:
819 			if (!isascii(*p) || bhnd_nv_isspace(*p))
820 				return (false);
821 		}
822 	}
823 
824 	return (true);
825 }
826 
827 /**
828  * Coerce value @p inp of type @p itype to @p otype, writing the
829  * result to @p outp.
830  *
831  * @param		inp	The value to be coerced.
832  * @param		ilen	The size of @p inp, in bytes.
833  * @param		itype	The base data type of @p inp.
834  * @param[out]		outp	On success, the value will be written to this
835  *				buffer. This argment may be NULL if the value
836  *				is not desired.
837  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
838  *				to the actual size of the requested value.
839  * @param		otype	The data type to be written to @p outp.
840  *
841  * @retval 0		success
842  * @retval ENOMEM	If @p outp is non-NULL and a buffer of @p olen is too
843  *			small to hold the requested value.
844  * @retval EFTYPE	If the variable data cannot be coerced to @p otype.
845  * @retval ERANGE	If value coercion would overflow @p otype.
846  */
847 int
848 bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype,
849     void *outp, size_t *olen, bhnd_nvram_type otype)
850 {
851 	bhnd_nvram_val_t	val;
852 	int			error;
853 
854 	/* Wrap input buffer in a value instance */
855 	error = bhnd_nvram_val_init(&val, NULL, inp, ilen,
856 	    itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED);
857 	if (error)
858 		return (error);
859 
860 	/* Try to encode as requested type */
861 	error = bhnd_nvram_val_encode(&val, outp, olen, otype);
862 
863 	/* Clean up and return error */
864 	bhnd_nvram_val_release(&val);
865 	return (error);
866 }
867 
868 /**
869  * Parses the string in the optionally NUL-terminated @p str to as an integer
870  * value of @p otype, accepting any integer format supported by the standard
871  * strtoul().
872  *
873  * - Any leading whitespace in @p str -- as defined by the equivalent of
874  *   calling isspace_l() with an ASCII locale -- will be ignored.
875  * - A @p str may be prefixed with a single optional '+' or '-' sign denoting
876  *   signedness.
877  * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a
878  *   base 16 integer follows.
879  * - An octal @p str may include a '0' prefix, denoting that an octal integer
880  *   follows.
881  *
882  * If a @p base of 0 is specified, the base will be determined according
883  * to the string's initial prefix, as per strtoul()'s documented behavior.
884  *
885  * When parsing a base 16 integer to a signed representation, if no explicit
886  * sign prefix is given, the string will be parsed as the raw two's complement
887  * representation of the signed integer value.
888  *
889  * @param		str	The string to be parsed.
890  * @param		maxlen	The maximum number of bytes to be read in
891  *				@p str.
892  * @param		base	The input string's base (2-36), or 0.
893  * @param[out]		nbytes	On success or failure, will be set to the total
894  *				number of parsed bytes. If the total number of
895  *				bytes is not desired, a NULL pointer may be
896  *				provided.
897  * @param[out]		outp	On success, the parsed integer value will be
898  *				written to @p outp. This argment may be NULL if
899  *				the value is not desired.
900  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
901  *				to the actual size of the requested value.
902  * @param		otype	The integer type to be parsed.
903  *
904  * @retval 0		success
905  * @retval EINVAL	if an invalid @p base is specified.
906  * @retval EINVAL	if an unsupported (or non-integer) @p otype is
907  *			specified.
908  * @retval ENOMEM	If @p outp is non-NULL and a buffer of @p olen is too
909  *			small to hold the requested value.
910  * @retval EFTYPE	if @p str cannot be parsed as an integer of @p base.
911  * @retval ERANGE	If the integer parsed from @p str is too large to be
912  *			represented as a value of @p otype.
913  */
914 int
915 bhnd_nvram_parse_int(const char *str, size_t maxlen,  u_int base,
916     size_t *nbytes, void *outp, size_t *olen, bhnd_nvram_type otype)
917 {
918 	uint64_t	value;
919 	uint64_t	carry_max, value_max;
920 	uint64_t	type_max;
921 	size_t		limit, local_nbytes;
922 	size_t		ndigits;
923 	bool		negative, sign, twos_compl;
924 
925 	/* Must be an integer type */
926 	if (!bhnd_nvram_is_int_type(otype))
927 		return (EINVAL);
928 
929 	/* Determine output byte limit */
930 	if (outp != NULL)
931 		limit = *olen;
932 	else
933 		limit = 0;
934 
935 	/* We always need a byte count. If the caller provides a NULL nbytes,
936 	 * track our position in a stack variable */
937 	if (nbytes == NULL)
938 		nbytes = &local_nbytes;
939 
940 	value = 0;
941 	ndigits = 0;
942 	*nbytes = 0;
943 	negative = false;
944 	sign = false;
945 
946 	/* Validate the specified base */
947 	if (base != 0 && !(base >= 2 && base <= 36))
948 		return (EINVAL);
949 
950 	/* Skip any leading whitespace */
951 	for (; *nbytes < maxlen; (*nbytes)++) {
952 		if (!bhnd_nv_isspace(str[*nbytes]))
953 			break;
954 	}
955 
956 	/* Empty string? */
957 	if (*nbytes == maxlen)
958 		return (EFTYPE);
959 
960 	/* Parse and skip sign */
961 	if (str[*nbytes] == '-') {
962 		negative = true;
963 		sign = true;
964 		(*nbytes)++;
965 	} else if (str[*nbytes] == '+') {
966 		sign = true;
967 		(*nbytes)++;
968 	}
969 
970 	/* Truncated after sign character? */
971 	if (*nbytes == maxlen)
972 		return (EFTYPE);
973 
974 	/* Identify (or validate) hex base, skipping 0x/0X prefix */
975 	if (base == 16 || base == 0) {
976 		/* Check for (and skip) 0x/0X prefix */
977 		if (maxlen - *nbytes >= 2 && str[*nbytes] == '0' &&
978 		    (str[*nbytes+1] == 'x' || str[*nbytes+1] == 'X'))
979 		{
980 			base = 16;
981 			(*nbytes) += 2;
982 		}
983 	}
984 
985 	/* Truncated after hex prefix? */
986 	if (*nbytes == maxlen)
987 		return (EFTYPE);
988 
989 	/* Differentiate decimal/octal by looking for a leading 0 */
990 	if (base == 0) {
991 		if (str[*nbytes] == '0') {
992 			base = 8;
993 		} else {
994 			base = 10;
995 		}
996 	}
997 
998 	/* Only enable twos-compliment signed integer parsing enabled if the
999 	 * input is base 16, and no explicit sign prefix was provided */
1000 	if (!sign && base == 16)
1001 		twos_compl = true;
1002 	else
1003 		twos_compl = false;
1004 
1005 	/* Determine the maximum value representable by the requested type */
1006 	switch (otype) {
1007 	case BHND_NVRAM_TYPE_CHAR:
1008 	case BHND_NVRAM_TYPE_UINT8:
1009 		type_max = (uint64_t)UINT8_MAX;
1010 		break;
1011 	case BHND_NVRAM_TYPE_UINT16:
1012 		type_max = (uint64_t)UINT16_MAX;
1013 		break;
1014 	case BHND_NVRAM_TYPE_UINT32:
1015 		type_max = (uint64_t)UINT32_MAX;
1016 		break;
1017 	case BHND_NVRAM_TYPE_UINT64:
1018 		type_max = (uint64_t)UINT64_MAX;
1019 		break;
1020 
1021 	case BHND_NVRAM_TYPE_INT8:
1022 		if (twos_compl)
1023 			type_max = (uint64_t)UINT8_MAX;
1024 		else if (negative)
1025 			type_max = -(uint64_t)INT8_MIN;
1026 		else
1027 			type_max = (uint64_t)INT8_MAX;
1028 		break;
1029 
1030 	case BHND_NVRAM_TYPE_INT16:
1031 		if (twos_compl)
1032 			type_max = (uint64_t)UINT16_MAX;
1033 		else if (negative)
1034 			type_max = -(uint64_t)INT16_MIN;
1035 		else
1036 			type_max = (uint64_t)INT16_MAX;
1037 		break;
1038 
1039 	case BHND_NVRAM_TYPE_INT32:
1040 		if (twos_compl)
1041 			type_max = (uint64_t)UINT32_MAX;
1042 		else if (negative)
1043 			type_max = -(uint64_t)INT32_MIN;
1044 		else
1045 			type_max = (uint64_t)INT32_MAX;
1046 		break;
1047 
1048 	case BHND_NVRAM_TYPE_INT64:
1049 		if (twos_compl)
1050 			type_max = (uint64_t)UINT64_MAX;
1051 		else if (negative)
1052 			type_max = -(uint64_t)INT64_MIN;
1053 		else
1054 			type_max = (uint64_t)INT64_MAX;
1055 		break;
1056 
1057 	default:
1058 		BHND_NV_LOG("unsupported integer type: %d\n", otype);
1059 		return (EINVAL);
1060 	}
1061 
1062 	/* The maximum value after which an additional carry would overflow */
1063 	value_max = type_max / (uint64_t)base;
1064 
1065 	/* The maximum carry value given a value equal to value_max */
1066 	carry_max = type_max % (uint64_t)base;
1067 
1068 	/* Consume input until we hit maxlen or a non-digit character */
1069 	for (; *nbytes < maxlen; (*nbytes)++) {
1070 		u_long	carry;
1071 		char	c;
1072 
1073 		/* Parse carry value */
1074 		c = str[*nbytes];
1075 		if (bhnd_nv_isdigit(c)) {
1076 			carry = c - '0';
1077 		} else if (bhnd_nv_isxdigit(c)) {
1078 			if (bhnd_nv_isupper(c))
1079 				carry = (c - 'A') + 10;
1080 			else
1081 				carry = (c - 'a') + 10;
1082 		} else {
1083 			/* Hit first non-digit character */
1084 			break;
1085 		}
1086 
1087 		/* If carry is outside the base, it's not a valid digit
1088 		 * in the current parse context; consider it a non-digit
1089 		 * character */
1090 		if (carry >= (uint64_t)base)
1091 			break;
1092 
1093 		/* Increment count of parsed digits */
1094 		ndigits++;
1095 
1096 		if (value > value_max) {
1097 			/* -Any- carry value would overflow */
1098 			return (ERANGE);
1099 		} else if (value == value_max && carry > carry_max) {
1100 			/* -This- carry value would overflow */
1101 			return (ERANGE);
1102 		}
1103 
1104 		value *= (uint64_t)base;
1105 		value += carry;
1106 	}
1107 
1108 	/* If we hit a non-digit character before parsing the first digit,
1109 	 * we hit an empty integer string. */
1110 	if (ndigits == 0)
1111 		return (EFTYPE);
1112 
1113 	if (negative)
1114 		value = -value;
1115 
1116 	/* Provide (and verify) required length */
1117 	*olen = bhnd_nvram_value_size(otype, NULL, 0, 1);
1118 	if (outp == NULL)
1119 		return (0);
1120 	else if (limit < *olen)
1121 		return (ENOMEM);
1122 
1123 	/* Provide result */
1124 	switch (otype) {
1125 	case BHND_NVRAM_TYPE_CHAR:
1126 	case BHND_NVRAM_TYPE_UINT8:
1127 		*(uint8_t *)outp = (uint8_t)value;
1128 		break;
1129 	case BHND_NVRAM_TYPE_UINT16:
1130 		*(uint16_t *)outp = (uint16_t)value;
1131 		break;
1132 	case BHND_NVRAM_TYPE_UINT32:
1133 		*(uint32_t *)outp = (uint32_t)value;
1134 		break;
1135 	case BHND_NVRAM_TYPE_UINT64:
1136 		*(uint64_t *)outp = (uint64_t)value;
1137 		break;
1138 
1139 	case BHND_NVRAM_TYPE_INT8:
1140 		*(int8_t *)outp = (int8_t)(int64_t)value;
1141 		break;
1142 	case BHND_NVRAM_TYPE_INT16:
1143 		*(int16_t *)outp = (int16_t)(int64_t)value;
1144 		break;
1145 	case BHND_NVRAM_TYPE_INT32:
1146 		*(int32_t *)outp = (int32_t)(int64_t)value;
1147 		break;
1148 	case BHND_NVRAM_TYPE_INT64:
1149 		*(int64_t *)outp = (int64_t)value;
1150 		break;
1151 	default:
1152 		/* unreachable */
1153 		BHND_NV_PANIC("unhandled type %d\n", otype);
1154 	}
1155 
1156 	return (0);
1157 }
1158 
1159 /**
1160  * Parse a 'name=value' string.
1161  *
1162  * @param env The string to be parsed.
1163  * @param env_len The length of @p envp.
1164  * @param delim The delimiter used in @p envp. This will generally be '='.
1165  * @param[out] name If not NULL, a pointer to the name string. This argument
1166  * may be NULL.
1167  * @param[out] name_len On success, the length of the name substring. This
1168  * argument may be NULL.
1169  * @param[out] value On success, a pointer to the value substring. This argument
1170  * may be NULL.
1171  * @param[out] value_len On success, the length of the value substring. This
1172  * argument may be NULL.
1173  *
1174  * @retval 0 success
1175  * @retval EINVAL if parsing @p envp fails.
1176  */
1177 int
1178 bhnd_nvram_parse_env(const char *env, size_t env_len, char delim,
1179     const char **name, size_t *name_len, const char **value, size_t *value_len)
1180 {
1181 	const char *p;
1182 
1183 	/* Name */
1184 	if ((p = memchr(env, delim, env_len)) == NULL) {
1185 		BHND_NV_LOG("delimiter '%c' not found in '%.*s'\n", delim,
1186 		    BHND_NV_PRINT_WIDTH(env_len), env);
1187 		return (EINVAL);
1188 	}
1189 
1190 	/* Name */
1191 	if (name != NULL)
1192 		*name = env;
1193 	if (name_len != NULL)
1194 		*name_len = p - env;
1195 
1196 	/* Skip delim */
1197 	p++;
1198 
1199 	/* Value */
1200 	if (value != NULL)
1201 		*value = p;
1202 	if (value_len != NULL)
1203 		*value_len = env_len - (p - env);
1204 
1205 	return (0);
1206 }
1207 
1208 
1209 /**
1210  * Parse a field value, returning the actual pointer to the first
1211  * non-whitespace character and the total size of the field.
1212  *
1213  * @param[in,out] inp The field string to parse. Will be updated to point
1214  * at the first non-whitespace character found.
1215  * @param ilen The length of @p inp, in bytes.
1216  * @param delim The field delimiter to search for.
1217  *
1218  * @return Returns the actual size of the field data.
1219  */
1220 size_t
1221 bhnd_nvram_parse_field(const char **inp, size_t ilen, char delim)
1222 {
1223 	const char	*p, *sp;
1224 
1225 	/* Skip any leading whitespace */
1226 	for (sp = *inp; (size_t)(sp-*inp) < ilen && bhnd_nv_isspace(*sp); sp++)
1227 		continue;
1228 
1229 	*inp = sp;
1230 
1231 	/* Find the last field character */
1232 	for (p = *inp; (size_t)(p - *inp) < ilen; p++) {
1233 		if (*p == delim || *p == '\0')
1234 			break;
1235 	}
1236 
1237 	return (p - *inp);
1238 }
1239 
1240 /**
1241  * Parse a field value, returning the actual pointer to the first
1242  * non-whitespace character and the total size of the field, minus
1243  * any trailing whitespace.
1244  *
1245  * @param[in,out] inp The field string to parse. Will be updated to point
1246  * at the first non-whitespace character found.
1247  * @param ilen The length of the parsed field, in bytes, excluding the
1248  * field elimiter and any trailing whitespace.
1249  * @param delim The field delimiter to search for.
1250  *
1251  * @return Returns the actual size of the field data.
1252  */
1253 size_t
1254 bhnd_nvram_trim_field(const char **inp, size_t ilen, char delim)
1255 {
1256 	const char	*sp;
1257 	size_t		 plen;
1258 
1259 	plen = bhnd_nvram_parse_field(inp, ilen, delim);
1260 
1261 	/* Trim trailing whitespace */
1262 	sp = *inp;
1263 	while (plen > 0) {
1264 		if (!bhnd_nv_isspace(*(sp + plen - 1)))
1265 			break;
1266 
1267 		plen--;
1268 	}
1269 
1270 	return (plen);
1271 }
1272