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