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