1 /*-
2 * Copyright (c) 2015-2016 Landon Fuller <[email protected]>
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
bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)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
bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val * value,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_bcm_hex_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_bcm_decimal_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_bcm_leddc_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)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
bhnd_nvram_val_bcm_leddc_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val * value,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_bcmstr_csv_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)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 *
bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val * value,const void * prev,size_t * len)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
bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)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
bhnd_nvram_val_bcm_macaddr_encode(bhnd_nvram_val * value,void * outp,size_t * olen,bhnd_nvram_type otype)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
bhnd_nvram_val_bcm_macaddr_string_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)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
bhnd_nvram_val_bcm_macaddr_string_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)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 *
bhnd_nvram_val_bcm_macaddr_string_next(bhnd_nvram_val * value,const void * prev,size_t * len)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
bhnd_nvram_ident_octet_string(const char * inp,size_t ilen,char * delim,size_t * nelem)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
bhnd_nvram_ident_num_string(const char * inp,size_t ilen,u_int base,u_int * obase)976 bhnd_nvram_ident_num_string(const char *inp, size_t ilen, u_int base,
977 u_int *obase)
978 {
979 size_t nbytes, ndigits;
980
981 nbytes = 0;
982 ndigits = 0;
983
984 /* Parse and skip sign */
985 if (nbytes >= ilen)
986 return (false);
987
988 if (inp[nbytes] == '-' || inp[nbytes] == '+')
989 nbytes++;
990
991 /* Truncated after sign character? */
992 if (nbytes == ilen)
993 return (false);
994
995 /* Identify (or validate) hex base, skipping 0x/0X prefix */
996 if (base == 16 || base == 0) {
997 /* Check for (and skip) 0x/0X prefix */
998 if (ilen - nbytes >= 2 && inp[nbytes] == '0' &&
999 (inp[nbytes+1] == 'x' || inp[nbytes+1] == 'X'))
1000 {
1001 base = 16;
1002 nbytes += 2;
1003 }
1004 }
1005
1006 /* Truncated after hex prefix? */
1007 if (nbytes == ilen)
1008 return (false);
1009
1010 /* Differentiate decimal/octal by looking for a leading 0 */
1011 if (base == 0) {
1012 if (inp[nbytes] == '0') {
1013 base = 8;
1014 } else {
1015 base = 10;
1016 }
1017 }
1018
1019 /* Consume and validate all remaining digit characters */
1020 for (; nbytes < ilen; nbytes++) {
1021 u_int carry;
1022 char c;
1023
1024 /* Parse carry value */
1025 c = inp[nbytes];
1026 if (bhnd_nv_isdigit(c)) {
1027 carry = c - '0';
1028 } else if (bhnd_nv_isxdigit(c)) {
1029 if (bhnd_nv_isupper(c))
1030 carry = (c - 'A') + 10;
1031 else
1032 carry = (c - 'a') + 10;
1033 } else {
1034 /* Hit a non-digit character */
1035 return (false);
1036 }
1037
1038 /* If carry is outside the base, it's not a valid digit
1039 * in the current parse context; consider it a non-digit
1040 * character */
1041 if (carry >= base)
1042 return (false);
1043
1044 /* Increment parsed digit count */
1045 ndigits++;
1046 }
1047
1048 /* Empty integer string? */
1049 if (ndigits == 0)
1050 return (false);
1051
1052 /* Valid integer -- provide the base and return */
1053 if (obase != NULL)
1054 *obase = base;
1055 return (true);
1056 }
1057