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