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