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