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