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