xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.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 #include <net/ethernet.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 <errno.h>
50 #include <inttypes.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 bool		 bhnd_nvram_ident_octet_string(const char *inp,
61 			     size_t ilen, char *delim, size_t *nelem);
62 static bool		 bhnd_nvram_ident_num_string(const char *inp,
63 			     size_t ilen, u_int base, u_int *obase);
64 
65 static int		 bhnd_nvram_val_bcm_macaddr_filter(
66 			     const bhnd_nvram_val_fmt_t **fmt, const void *inp,
67 			     size_t ilen, bhnd_nvram_type itype);
68 static int		 bhnd_nvram_val_bcm_macaddr_encode(
69 			     bhnd_nvram_val_t *value, void *outp, size_t *olen,
70 			     bhnd_nvram_type otype);
71 
72 static int		 bhnd_nvram_val_bcm_macaddr_string_filter(
73 			     const bhnd_nvram_val_fmt_t **fmt, const void *inp,
74 			     size_t ilen, bhnd_nvram_type itype);
75 static int		 bhnd_nvram_val_bcm_macaddr_string_encode_elem(
76 			     bhnd_nvram_val_t *value, const void *inp,
77 			     size_t ilen, void *outp, size_t *olen,
78 			     bhnd_nvram_type otype);
79 static const void 	*bhnd_nvram_val_bcm_macaddr_string_next(
80 			     bhnd_nvram_val_t *value, const void *prev,
81 			     size_t *len);
82 
83 
84 static int		 bhnd_nvram_val_bcm_int_filter(
85 			     const bhnd_nvram_val_fmt_t **fmt, const void *inp,
86 			     size_t ilen, bhnd_nvram_type itype);
87 static int		 bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val_t *value,
88 			     void *outp, size_t *olen, bhnd_nvram_type otype);
89 
90 static int		 bhnd_nvram_val_bcm_decimal_encode_elem(
91 			     bhnd_nvram_val_t *value, const void *inp,
92 			     size_t ilen, void *outp, size_t *olen,
93 			     bhnd_nvram_type otype);
94 static int		 bhnd_nvram_val_bcm_hex_encode_elem(
95 			     bhnd_nvram_val_t *value, const void *inp,
96 			     size_t ilen, void *outp, size_t *olen,
97 			     bhnd_nvram_type otype);
98 
99 static int		 bhnd_nvram_val_bcm_leddc_filter(
100 			     const bhnd_nvram_val_fmt_t **fmt, const void *inp,
101 			     size_t ilen, bhnd_nvram_type itype);
102 static int		 bhnd_nvram_val_bcm_leddc_encode_elem(
103 			     bhnd_nvram_val_t *value, const void *inp,
104 			     size_t ilen, void *outp, size_t *olen,
105 			     bhnd_nvram_type otype);
106 
107 
108 static int		 bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val_t *value,
109 			     void *outp, size_t *olen, bhnd_nvram_type otype);
110 
111 static int		 bhnd_nvram_val_bcmstr_csv_filter(
112 			     const bhnd_nvram_val_fmt_t **fmt, const void *inp,
113 			     size_t ilen, bhnd_nvram_type itype);
114 static const void	*bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val_t *value,
115 			     const void *prev, size_t *len);
116 
117 /**
118  * Broadcom NVRAM MAC address format.
119  */
120 const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_macaddr_fmt = {
121 	.name		= "bcm-macaddr",
122 	.native_type	= BHND_NVRAM_TYPE_UINT8_ARRAY,
123 	.op_filter	= bhnd_nvram_val_bcm_macaddr_filter,
124 	.op_encode	= bhnd_nvram_val_bcm_macaddr_encode,
125 };
126 
127 /** Broadcom NVRAM MAC address string format. */
128 static const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_macaddr_string_fmt = {
129 	.name		= "bcm-macaddr-string",
130 	.native_type	= BHND_NVRAM_TYPE_STRING,
131 	.op_filter	= bhnd_nvram_val_bcm_macaddr_string_filter,
132 	.op_encode_elem	= bhnd_nvram_val_bcm_macaddr_string_encode_elem,
133 	.op_next	= bhnd_nvram_val_bcm_macaddr_string_next,
134 };
135 
136 /**
137  * Broadcom NVRAM LED duty-cycle format.
138  */
139 const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_leddc_fmt = {
140 	.name		= "bcm-leddc",
141 	.native_type	= BHND_NVRAM_TYPE_UINT32,
142 	.op_filter	= bhnd_nvram_val_bcm_leddc_filter,
143 	.op_encode_elem	= bhnd_nvram_val_bcm_leddc_encode_elem,
144 };
145 
146 
147 /**
148  * Broadcom NVRAM decimal integer format.
149  *
150  * Extends standard integer handling, encoding the string representation of
151  * the integer value as a decimal string:
152  * - Positive values will be string-encoded without a prefix.
153  * - Negative values will be string-encoded with a leading '-' sign.
154  */
155 const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_decimal_fmt = {
156 	.name		= "bcm-decimal",
157 	.native_type	= BHND_NVRAM_TYPE_UINT64,
158 	.op_filter	= bhnd_nvram_val_bcm_int_filter,
159 	.op_encode	= bhnd_nvram_val_bcm_int_encode,
160 	.op_encode_elem	= bhnd_nvram_val_bcm_decimal_encode_elem,
161 };
162 
163 /**
164  * Broadcom NVRAM decimal integer format.
165  *
166  * Extends standard integer handling, encoding the string representation of
167  * unsigned and positive signed integer values as an 0x-prefixed hexadecimal
168  * string.
169  *
170  * For compatibility with standard Broadcom NVRAM parsing, if the integer is
171  * both signed and negative, it will be string encoded as a negative decimal
172  * value, not as a twos-complement hexadecimal value.
173  */
174 const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_hex_fmt = {
175 	.name		= "bcm-hex",
176 	.native_type	= BHND_NVRAM_TYPE_UINT64,
177 	.op_filter	= bhnd_nvram_val_bcm_int_filter,
178 	.op_encode	= bhnd_nvram_val_bcm_int_encode,
179 	.op_encode_elem	= bhnd_nvram_val_bcm_hex_encode_elem,
180 };
181 
182 /**
183  * Broadcom NVRAM string format.
184  *
185  * Handles standard, comma-delimited, and octet-string values as used in
186  * Broadcom NVRAM data.
187  */
188 const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_string_fmt = {
189 	.name		= "bcm-string",
190 	.native_type	= BHND_NVRAM_TYPE_STRING,
191 	.op_encode	= bhnd_nvram_val_bcmstr_encode,
192 };
193 
194 /** Broadcom comma-delimited string. */
195 static const bhnd_nvram_val_fmt_t bhnd_nvram_val_bcm_string_csv_fmt = {
196 	.name		= "bcm-string[]",
197 	.native_type	= BHND_NVRAM_TYPE_STRING,
198 	.op_filter	= bhnd_nvram_val_bcmstr_csv_filter,
199 	.op_next	= bhnd_nvram_val_bcmstr_csv_next,
200 };
201 
202 /**
203  * Common hex/decimal integer filter implementation.
204  */
205 static int
206 bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt_t **fmt, const void *inp,
207     size_t ilen, bhnd_nvram_type itype)
208 {
209 	bhnd_nvram_type	itype_base;
210 
211 	itype_base = bhnd_nvram_base_type(itype);
212 
213 	switch (itype_base) {
214 	case BHND_NVRAM_TYPE_STRING:
215 		/*
216 		 * If the input is a string, delegate to the Broadcom
217 		 * string format -- preserving the original string value
218 		 * takes priority over enforcing hexadecimal/integer string
219 		 * formatting.
220 		 */
221 		*fmt = &bhnd_nvram_val_bcm_string_fmt;
222 		return (0);
223 
224 	default:
225 		if (bhnd_nvram_is_int_type(itype_base))
226 			return (0);
227 
228 		return (EFTYPE);
229 	}
230 }
231 
232 /**
233  * Broadcom hex/decimal integer encode implementation.
234  */
235 static int
236 bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val_t *value, void *outp, size_t *olen,
237     bhnd_nvram_type otype)
238 {
239 	/* If encoding to a string, format multiple elements (if any) with a
240 	 * comma delimiter. */
241 	if (otype == BHND_NVRAM_TYPE_STRING)
242 		return (bhnd_nvram_val_printf(value, "%[]s", outp, olen, ","));
243 
244 	return (bhnd_nvram_val_generic_encode(value, outp, olen, otype));
245 }
246 
247 /**
248  * Broadcom hex integer encode_elem implementation.
249  */
250 static int
251 bhnd_nvram_val_bcm_hex_encode_elem(bhnd_nvram_val_t *value, const void *inp,
252     size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
253 {
254 	bhnd_nvram_type	itype;
255 	ssize_t		width;
256 	int		error;
257 
258 	itype = bhnd_nvram_val_elem_type(value);
259 	BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));
260 
261 	/* If not encoding as a string, perform generic value encoding */
262 	if (otype != BHND_NVRAM_TYPE_STRING)
263 		return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,
264 		    outp, olen, otype));
265 
266 	/* If the value is a signed, negative value, encode as a decimal
267 	 * string */
268 	if (bhnd_nvram_is_signed_type(itype)) {
269 		int64_t		sval;
270 		size_t		slen;
271 		bhnd_nvram_type	stype;
272 
273 		stype = BHND_NVRAM_TYPE_INT64;
274 		slen = sizeof(sval);
275 
276 		/* Fetch 64-bit signed representation */
277 		error = bhnd_nvram_value_coerce(inp, ilen, itype, &sval, &slen,
278 		    stype);
279 		if (error)
280 			return (error);
281 
282 		/* Decimal encoding required? */
283 		if (sval < 0)
284 			return (bhnd_nvram_value_printf("%I64d", &sval, slen,
285 			    stype, outp, olen, otype));
286 	}
287 
288 	/*
289 	 * Encode the value as a hex string.
290 	 *
291 	 * Most producers of Broadcom NVRAM values zero-pad hex values out to
292 	 * their native width (width * two hex characters), and we do the same
293 	 * for compatibility
294 	 */
295 
296 	width = bhnd_nvram_value_size(itype, NULL, 0, 1) * 2;
297 
298 	return (bhnd_nvram_value_printf("0x%0*I64X", inp, ilen, itype,
299 	    outp, olen, width));
300 }
301 
302 /**
303  * Broadcom decimal integer encode_elem implementation.
304  */
305 static int
306 bhnd_nvram_val_bcm_decimal_encode_elem(bhnd_nvram_val_t *value, const void *inp,
307     size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
308 {
309 	const char	*sfmt;
310 	bhnd_nvram_type	 itype;
311 
312 	itype = bhnd_nvram_val_elem_type(value);
313 	BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));
314 
315 	/* If not encoding as a string, perform generic value encoding */
316 	if (otype != BHND_NVRAM_TYPE_STRING)
317 		return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,
318 		    outp, olen, otype));
319 
320 	sfmt = bhnd_nvram_is_signed_type(itype) ? "%I64d" : "%I64u";
321 	return (bhnd_nvram_value_printf(sfmt, inp, ilen, itype, outp, olen));
322 }
323 
324 /**
325  * Broadcom LED duty-cycle filter.
326  */
327 static int
328 bhnd_nvram_val_bcm_leddc_filter(const bhnd_nvram_val_fmt_t **fmt,
329     const void *inp, size_t ilen, bhnd_nvram_type itype)
330 {
331 	const char	*p;
332 	size_t		 plen;
333 
334 	switch (itype) {
335 	case BHND_NVRAM_TYPE_UINT16:
336 	case BHND_NVRAM_TYPE_UINT32:
337 		return (0);
338 
339 	case BHND_NVRAM_TYPE_STRING:
340 		/* Trim any whitespace */
341 		p = inp;
342 		plen = bhnd_nvram_trim_field(&p, ilen, '\0');
343 
344 		/* If the value is not a valid integer string, delegate to the
345 		 * Broadcom string format */
346 		if (!bhnd_nvram_ident_num_string(p, plen, 0, NULL))
347 			*fmt = &bhnd_nvram_val_bcm_string_fmt;
348 
349 		return (0);
350 	default:
351 		return (EFTYPE);
352 	}
353 }
354 
355 /**
356  * Broadcom LED duty-cycle encode.
357  */
358 static int
359 bhnd_nvram_val_bcm_leddc_encode_elem(bhnd_nvram_val_t *value, const void *inp,
360     size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
361 {
362 	bhnd_nvram_type		itype;
363 	size_t			limit, nbytes;
364 	int			error;
365 	uint16_t		led16;
366 	uint32_t		led32;
367 	bool			led16_lossy;
368 	union {
369 		uint16_t	u16;
370 		uint32_t	u32;
371 	} strval;
372 
373 	/*
374 	 * LED duty-cycle values represent the on/off periods as a 32-bit
375 	 * integer, with the top 16 bits representing on cycles, and the
376 	 * bottom 16 representing off cycles.
377 	 *
378 	 * LED duty cycle values have three different formats:
379 	 *
380 	 * - SPROM:	A 16-bit unsigned integer, with on/off cycles encoded
381 	 *		as 8-bit values.
382 	 * - NVRAM:	A 16-bit decimal or hexadecimal string, with on/off
383 	 *		cycles encoded as 8-bit values as per the SPROM format.
384 	 * - NVRAM:	A 32-bit decimal or hexadecimal string, with on/off
385 	 *		cycles encoded as 16-bit values.
386 	 *
387 	 * To convert from a 16-bit representation to a 32-bit representation:
388 	 *     ((value & 0xFF00) << 16) | ((value & 0x00FF) << 8)
389 	 *
390 	 * To convert from a 32-bit representation to a 16-bit representation,
391 	 * perform the same operation in reverse, discarding the lower 8-bits
392 	 * of each half of the 32-bit representation:
393 	 *     ((value >> 16) & 0xFF00) | ((value >> 8) & 0x00FF)
394 	 */
395 
396 	itype = bhnd_nvram_val_elem_type(value);
397 	nbytes = 0;
398 	led16_lossy = false;
399 
400 	/* Determine output byte limit */
401 	if (outp != NULL)
402 		limit = *olen;
403 	else
404 		limit = 0;
405 
406 	/* If the input/output types match, just delegate to standard value
407 	 * encoding support */
408 	if (otype == itype) {
409 		return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
410 		    otype));
411 	}
412 
413 	/* If our value is a string, it may either be a 16-bit or a 32-bit
414 	 * representation of the duty cycle */
415 	if (itype == BHND_NVRAM_TYPE_STRING) {
416 		const char	*p;
417 		uint32_t	 ival;
418 		size_t		 nlen, parsed;
419 
420 		/* Parse integer value */
421 		p = inp;
422 		nlen = sizeof(ival);
423 		error = bhnd_nvram_parse_int(p, ilen, 0, &parsed, &ival, &nlen,
424 		    BHND_NVRAM_TYPE_UINT32);
425 		if (error)
426 			return (error);
427 
428 		/* Trailing garbage? */
429 		if (parsed < ilen && *(p+parsed) != '\0')
430 			return (EFTYPE);
431 
432 		/* Point inp and itype to either our parsed 32-bit or 16-bit
433 		 * value */
434 		inp = &strval;
435 		if (ival & 0xFFFF0000) {
436 			strval.u32 = ival;
437 			itype = BHND_NVRAM_TYPE_UINT32;
438 		} else {
439 			strval.u16 = ival;
440 			itype = BHND_NVRAM_TYPE_UINT16;
441 		}
442 	}
443 
444 	/* Populate both u32 and (possibly lossy) u16 LEDDC representations */
445 	switch (itype) {
446 	case BHND_NVRAM_TYPE_UINT16: {
447 		led16 = *(const uint16_t *)inp;
448 		led32 = ((led16 & 0xFF00) << 16) | ((led16 & 0x00FF) << 8);
449 
450 		/* If all bits are set in the 16-bit value (indicating that
451 		 * the value is 'unset' in SPROM), we must update the 32-bit
452 		 * representation to match. */
453 		if (led16 == UINT16_MAX)
454 			led32 = UINT32_MAX;
455 
456 		break;
457 	}
458 
459 	case BHND_NVRAM_TYPE_UINT32:
460 		led32 = *(const uint32_t *)inp;
461 		led16 = ((led32 >> 16) & 0xFF00) | ((led32 >> 8) & 0x00FF);
462 
463 		/*
464 		 * Determine whether the led16 conversion is lossy:
465 		 *
466 		 * - If the lower 8 bits of each half of the 32-bit value
467 		 *   aren't set, we can safely use the 16-bit representation
468 		 *   without losing data.
469 		 * - If all bits in the 32-bit value are set, the variable is
470 		 *   treated as unset in  SPROM. We can safely use the 16-bit
471 		 *   representation without losing data.
472 		 */
473 		if ((led32 & 0x00FF00FF) != 0 && led32 != UINT32_MAX)
474 			led16_lossy = true;
475 
476 		break;
477 	default:
478 		BHND_NV_PANIC("unsupported backing data type: %s",
479 		    bhnd_nvram_type_name(itype));
480 	}
481 
482 	/*
483 	 * Encode as requested output type.
484 	 */
485 	switch (otype) {
486 	case BHND_NVRAM_TYPE_STRING:
487 		/*
488 		 * Prefer 16-bit format.
489 		 */
490 		if (!led16_lossy) {
491 			return (bhnd_nvram_value_printf("0x%04hX", &led16,
492 			    sizeof(led16), BHND_NVRAM_TYPE_UINT16, outp, olen));
493 		} else {
494 			return (bhnd_nvram_value_printf("0x%04X", &led32,
495 			    sizeof(led32), BHND_NVRAM_TYPE_UINT32, outp, olen));
496 		}
497 
498 		break;
499 
500 	case BHND_NVRAM_TYPE_UINT16: {
501 		/* Can we encode as uint16 without losing data? */
502 		if (led16_lossy)
503 			return (ERANGE);
504 
505 		/* Write led16 format */
506 		nbytes += sizeof(uint16_t);
507 		if (limit >= nbytes)
508 			*(uint16_t *)outp = led16;
509 
510 		break;
511 	}
512 
513 	case BHND_NVRAM_TYPE_UINT32:
514 		/* Write led32 format */
515 		nbytes += sizeof(uint32_t);
516 		if (limit >= nbytes)
517 			*(uint32_t *)outp = led32;
518 		break;
519 
520 	default:
521 		/* No other output formats are supported */
522 		return (EFTYPE);
523 	}
524 
525 	/* Provide the actual length */
526 	*olen = nbytes;
527 
528 	/* Report insufficient space (if output was requested) */
529 	if (limit < nbytes && outp != NULL)
530 		return (ENOMEM);
531 
532 	return (0);
533 }
534 
535 /**
536  * Broadcom NVRAM string encoding.
537  */
538 static int
539 bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val_t *value, void *outp,
540     size_t *olen, bhnd_nvram_type otype)
541 {
542 	bhnd_nvram_val_t		 array;
543 	const bhnd_nvram_val_fmt_t	*array_fmt;
544 	const void			*inp;
545 	bhnd_nvram_type			itype;
546 	size_t				ilen;
547 	int				error;
548 
549 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
550 
551 	/* If the output is not an array type (or if it's a character array),
552 	 * we can fall back on standard string encoding */
553 	if (!bhnd_nvram_is_array_type(otype) ||
554 	    otype == BHND_NVRAM_TYPE_CHAR_ARRAY)
555 	{
556 		return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
557 		    otype));
558 	}
559 
560 	/* Otherwise, we need to interpret our value as either a macaddr
561 	 * string, or a comma-delimited string. */
562 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
563 	if (bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))
564 		array_fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;
565 	else
566 		array_fmt = &bhnd_nvram_val_bcm_string_csv_fmt;
567 
568 	/* Wrap in array-typed representation */
569 	error = bhnd_nvram_val_init(&array, array_fmt, inp, ilen, itype,
570 	    BHND_NVRAM_VAL_BORROW_DATA);
571 	if (error) {
572 		BHND_NV_LOG("error initializing array representation: %d\n",
573 		    error);
574 		return (error);
575 	}
576 
577 	/* Ask the array-typed value to perform the encode */
578 	error = bhnd_nvram_val_encode(&array, outp, olen, otype);
579 	if (error)
580 		BHND_NV_LOG("error encoding array representation: %d\n", error);
581 
582 	bhnd_nvram_val_release(&array);
583 
584 	return (error);
585 }
586 
587 /**
588  * Broadcom NVRAM comma-delimited string filter.
589  */
590 static int
591 bhnd_nvram_val_bcmstr_csv_filter(const bhnd_nvram_val_fmt_t **fmt,
592     const void *inp, size_t ilen, bhnd_nvram_type itype)
593 {
594 	switch (itype) {
595 	case BHND_NVRAM_TYPE_STRING:
596 	case BHND_NVRAM_TYPE_STRING_ARRAY:
597 		return (0);
598 	default:
599 		return (EFTYPE);
600 	}
601 }
602 
603 /**
604  * Broadcom NVRAM comma-delimited string iteration.
605  */
606 static const void *
607 bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val_t *value, const void *prev,
608     size_t *len)
609 {
610 	const char	*next;
611 	const char	*inp;
612 	bhnd_nvram_type	 itype;
613 	size_t		 ilen, remain;
614 	char		 delim;
615 
616 	/* Fetch backing representation */
617 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
618 
619 	/* Fetch next value */
620 	switch (itype) {
621 	case BHND_NVRAM_TYPE_STRING:
622 		/* Zero-length array? */
623 		if (ilen == 0)
624 			return (NULL);
625 
626 		if (prev == NULL) {
627 			/* First element */
628 			next = inp;
629 			remain = ilen;
630 			delim = ',';
631 		} else {
632 			/* Advance to the previous element's delimiter */
633 			next = (const char *)prev + *len;
634 
635 			/* Did we hit the end of the string? */
636 			if ((size_t)(next - inp) >= ilen)
637 				return (NULL);
638 
639 			/* Fetch (and skip past) the delimiter */
640 			delim = *next;
641 			next++;
642 			remain = ilen - (size_t)(next - inp);
643 
644 			/* Was the delimiter the final character? */
645 			if (remain == 0)
646 				return (NULL);
647 		}
648 
649 		/* Parse the field value, up to the next delimiter */
650 		*len = bhnd_nvram_parse_field(&next, remain, delim);
651 
652 		return (next);
653 
654 	case BHND_NVRAM_TYPE_STRING_ARRAY:
655 		next = bhnd_nvram_string_array_next(inp, ilen, prev);
656 		if (next != NULL) {
657 			*len = strlen(next);
658 
659 			/* Account for trailing NUL */
660 			if (*len + (size_t)(next - inp) < ilen)
661 				(*len)++;
662 		}
663 
664 		return (next);
665 	default:
666 		BHND_NV_PANIC("unsupported type: %d", itype);
667 	}
668 }
669 
670 /**
671  * MAC address filter.
672  */
673 static int
674 bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt_t **fmt,
675     const void *inp, size_t ilen, bhnd_nvram_type itype)
676 {
677 	switch (itype) {
678 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
679 		return (0);
680 	case BHND_NVRAM_TYPE_STRING:
681 		/* Let bcm_macaddr_string format handle it */
682 		*fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;
683 		return (0);
684 	default:
685 		return (EFTYPE);
686 	}
687 }
688 
689 /**
690  * MAC address encoding.
691  */
692 static int
693 bhnd_nvram_val_bcm_macaddr_encode(bhnd_nvram_val_t *value, void *outp,
694     size_t *olen, bhnd_nvram_type otype)
695 {
696 	const void	*inp;
697 	bhnd_nvram_type	 itype;
698 	size_t		 ilen;
699 
700 	/*
701 	 * If converting to a string (or a single-element string array),
702 	 * produce an octet string (00:00:...).
703 	 */
704 	if (bhnd_nvram_base_type(otype) == BHND_NVRAM_TYPE_STRING) {
705 		return (bhnd_nvram_val_printf(value, "%[]02hhX", outp, olen,
706 		    ":"));
707 	}
708 
709 	/* Otherwise, use standard encoding support */
710 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
711 	return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype));}
712 
713 /**
714  * MAC address string filter.
715  */
716 static int
717 bhnd_nvram_val_bcm_macaddr_string_filter(const bhnd_nvram_val_fmt_t **fmt,
718     const void *inp, size_t ilen, bhnd_nvram_type itype)
719 {
720 	switch (itype) {
721 	case BHND_NVRAM_TYPE_STRING:
722 		/* Use the standard Broadcom string format implementation if
723 		 * the input is not an octet string. */
724 		if (!bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))
725 			*fmt = &bhnd_nvram_val_bcm_string_fmt;
726 
727 		return (0);
728 	default:
729 		return (EFTYPE);
730 	}
731 }
732 
733 
734 /**
735  * MAC address string octet encoding.
736  */
737 static int
738 bhnd_nvram_val_bcm_macaddr_string_encode_elem(bhnd_nvram_val_t *value,
739     const void *inp, size_t ilen, void *outp, size_t *olen,
740     bhnd_nvram_type otype)
741 {
742 	size_t	nparsed;
743 	int	error;
744 
745 	/* If integer encoding is requested, explicitly parse our
746 	 * non-0x-prefixed as a base 16 integer value */
747 	if (bhnd_nvram_is_int_type(otype)) {
748 		error = bhnd_nvram_parse_int(inp, ilen, 16, &nparsed, outp,
749 		    olen, otype);
750 		if (error)
751 			return (error);
752 
753 		if (nparsed != ilen)
754 			return (EFTYPE);
755 
756 		return (0);
757 	}
758 
759 	/* Otherwise, use standard encoding support */
760 	return (bhnd_nvram_value_coerce(inp, ilen,
761 	    bhnd_nvram_val_elem_type(value), outp, olen, otype));
762 }
763 
764 /**
765  * MAC address string octet iteration.
766  */
767 static const void *
768 bhnd_nvram_val_bcm_macaddr_string_next(bhnd_nvram_val_t *value, const void *prev,
769     size_t *len)
770 {
771 	const char	*next;
772 	const char	*str;
773 	bhnd_nvram_type	 stype;
774 	size_t		 slen, remain;
775 	char		 delim;
776 
777 	/* Fetch backing string */
778 	str = bhnd_nvram_val_bytes(value, &slen, &stype);
779 	BHND_NV_ASSERT(stype == BHND_NVRAM_TYPE_STRING,
780 	    ("unsupported type: %d", stype));
781 
782 	/* Zero-length array? */
783 	if (slen == 0)
784 		return (NULL);
785 
786 	if (prev == NULL) {
787 		/* First element */
788 
789 		/* Determine delimiter */
790 		if (!bhnd_nvram_ident_octet_string(str, slen, &delim, NULL)) {
791 			/* Default to comma-delimited parsing */
792 			delim = ',';
793 		}
794 
795 		/* Parsing will start at the base string pointer */
796 		next = str;
797 		remain = slen;
798 	} else {
799 		/* Advance to the previous element's delimiter */
800 		next = (const char *)prev + *len;
801 
802 		/* Did we hit the end of the string? */
803 		if ((size_t)(next - str) >= slen)
804 			return (NULL);
805 
806 		/* Fetch (and skip past) the delimiter */
807 		delim = *next;
808 		next++;
809 		remain = slen - (size_t)(next - str);
810 
811 		/* Was the delimiter the final character? */
812 		if (remain == 0)
813 			return (NULL);
814 	}
815 
816 	/* Parse the field value, up to the next delimiter */
817 	*len = bhnd_nvram_parse_field(&next, remain, delim);
818 
819 	return (next);
820 }
821 
822 
823 /**
824  * Determine whether @p inp is in octet string format, consisting of a
825  * fields of two hex characters, separated with ':' or '-' delimiters.
826  *
827  * This may be used to identify MAC address octet strings
828  * (BHND_NVRAM_SFMT_MACADDR).
829  *
830  * @param		inp	The string to be parsed.
831  * @param		ilen	The length of @p inp, in bytes.
832  * @param[out]		delim	On success, the delimiter used by this octet
833  * 				string. May be set to NULL if the field
834  *				delimiter is not desired.
835  * @param[out]		nelem	On success, the number of fields in this
836  *				octet string. May be set to NULL if the field
837  *				count is not desired.
838  *
839  *
840  * @retval true		if @p inp is a valid octet string
841  * @retval false	if @p inp is not a valid octet string.
842  */
843 static bool
844 bhnd_nvram_ident_octet_string(const char *inp, size_t ilen, char *delim,
845     size_t *nelem)
846 {
847 	size_t	elem_count;
848 	size_t	max_elem_count, min_elem_count;
849 	size_t	field_count;
850 	char	idelim;
851 
852 	field_count = 0;
853 
854 	/* Require exactly two digits. If we relax this, there is room
855 	 * for ambiguity with signed integers and the '-' delimiter */
856 	min_elem_count = 2;
857 	max_elem_count = 2;
858 
859 	/* Identify the delimiter used. The standard delimiter for MAC
860 	 * addresses is ':', but some earlier NVRAM formats may use '-' */
861 	for (const char *d = ":-";; d++) {
862 		const char *loc;
863 
864 		/* No delimiter found, not an octet string */
865 		if (*d == '\0')
866 			return (false);
867 
868 		/* Look for the delimiter */
869 		if ((loc = memchr(inp, *d, ilen)) == NULL)
870 			continue;
871 
872 		/* Delimiter found */
873 		idelim = *loc;
874 		break;
875 	}
876 
877 	/* To disambiguate from signed integers, if the delimiter is "-",
878 	 * the octets must be exactly 2 chars each */
879 	if (idelim == '-')
880 		min_elem_count = 2;
881 
882 	/* String must be composed of individual octets (zero or more hex
883 	 * digits) separated by our delimiter. */
884 	elem_count = 0;
885 	for (const char *p = inp; (size_t)(p - inp) < ilen; p++) {
886 		switch (*p) {
887 		case ':':
888 		case '-':
889 		case '\0':
890 			/* Hit a delim character; all delims must match
891 			 * the first delimiter used */
892 			if (*p != '\0' && *p != idelim)
893 				return (false);
894 
895 			/* Must have parsed at least min_elem_count digits */
896 			if (elem_count < min_elem_count)
897 				return (false);
898 
899 			/* Reset element count */
900 			elem_count = 0;
901 
902 			/* Bump field count */
903 			field_count++;
904 			break;
905 		default:
906 			/* More than maximum number of hex digits? */
907 			if (elem_count >= max_elem_count)
908 				return (false);
909 
910 			/* Octet values must be hex digits */
911 			if (!bhnd_nv_isxdigit(*p))
912 				return (false);
913 
914 			elem_count++;
915 			break;
916 		}
917 	}
918 
919 	if (delim != NULL)
920 		*delim = idelim;
921 
922 	if (nelem != NULL)
923 		*nelem = field_count;
924 
925 	return (true);
926 }
927 
928 
929 /**
930  * Determine whether @p inp is in hexadecimal, octal, or decimal string
931  * format.
932  *
933  * - A @p str may be prefixed with a single optional '+' or '-' sign denoting
934  *   signedness.
935  * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a
936  *   base 16 integer follows.
937  * - An octal @p str may include a '0' prefix, denoting that an octal integer
938  *   follows.
939  *
940  * @param	inp	The string to be parsed.
941  * @param	ilen	The length of @p inp, in bytes.
942  * @param	base	The input string's base (2-36), or 0.
943  * @param[out]	obase	On success, will be set to the base of the parsed
944  *			integer. May be set to NULL if the base is not
945  *			desired.
946  *
947  * @retval true		if @p inp is a valid number string
948  * @retval false	if @p inp is not a valid number string.
949  * @retval false	if @p base is invalid.
950  */
951 static bool
952 bhnd_nvram_ident_num_string(const char *inp, size_t ilen, u_int base,
953     u_int *obase)
954 {
955 	size_t	nbytes, ndigits;
956 
957 	nbytes = 0;
958 	ndigits = 0;
959 
960 	/* Parse and skip sign */
961 	if (nbytes >= ilen)
962 		return (false);
963 
964 	if (inp[nbytes] == '-' || inp[nbytes] == '+')
965 		nbytes++;
966 
967 	/* Truncated after sign character? */
968 	if (nbytes == ilen)
969 		return (false);
970 
971 	/* Identify (or validate) hex base, skipping 0x/0X prefix */
972 	if (base == 16 || base == 0) {
973 		/* Check for (and skip) 0x/0X prefix */
974 		if (ilen - nbytes >= 2 && inp[nbytes] == '0' &&
975 		    (inp[nbytes+1] == 'x' || inp[nbytes+1] == 'X'))
976 		{
977 			base = 16;
978 			nbytes += 2;
979 		}
980 	}
981 
982 	/* Truncated after hex prefix? */
983 	if (nbytes == ilen)
984 		return (false);
985 
986 	/* Differentiate decimal/octal by looking for a leading 0 */
987 	if (base == 0) {
988 		if (inp[nbytes] == '0') {
989 			base = 8;
990 		} else {
991 			base = 10;
992 		}
993 	}
994 
995 	/* Consume and validate all remaining digit characters */
996 	for (; nbytes < ilen; nbytes++) {
997 		u_int	carry;
998 		char	c;
999 
1000 		/* Parse carry value */
1001 		c = inp[nbytes];
1002 		if (bhnd_nv_isdigit(c)) {
1003 			carry = c - '0';
1004 		} else if (bhnd_nv_isxdigit(c)) {
1005 			if (bhnd_nv_isupper(c))
1006 				carry = (c - 'A') + 10;
1007 			else
1008 				carry = (c - 'a') + 10;
1009 		} else {
1010 			/* Hit a non-digit character */
1011 			return (false);
1012 		}
1013 
1014 		/* If carry is outside the base, it's not a valid digit
1015 		 * in the current parse context; consider it a non-digit
1016 		 * character */
1017 		if (carry >= base)
1018 			return (false);
1019 
1020 		/* Increment parsed digit count */
1021 		ndigits++;
1022 	}
1023 
1024 	/* Empty integer string? */
1025 	if (ndigits == 0)
1026 		return (false);
1027 
1028 	/* Valid integer -- provide the base and return */
1029 	if (obase != NULL)
1030 		*obase = base;
1031 	return (true);
1032 }
1033