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