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