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