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