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