]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c
Upgrade Unbound to 1.6.0. More to follow.
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / nvram / bhnd_nvram_value_fmts.c
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 }