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