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