1 /*- 2 * Copyright (c) 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 #include <sys/endian.h> 32 33 #ifdef _KERNEL 34 35 #include <sys/bus.h> 36 #include <sys/ctype.h> 37 #include <sys/malloc.h> 38 #include <sys/systm.h> 39 40 #else /* !_KERNEL */ 41 42 #include <ctype.h> 43 #include <stdint.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 48 #endif /* _KERNEL */ 49 50 #include "bhnd_nvram_private.h" 51 52 #include "bhnd_nvram_datavar.h" 53 54 #include "bhnd_nvram_data_bcmreg.h" 55 #include "bhnd_nvram_data_bcmvar.h" 56 57 /* 58 * Broadcom NVRAM data class. 59 * 60 * The Broadcom NVRAM NUL-delimited ASCII format is used by most 61 * Broadcom SoCs. 62 * 63 * The NVRAM data is encoded as a standard header, followed by series of 64 * NUL-terminated 'key=value' strings; the end of the stream is denoted 65 * by a single extra NUL character. 66 */ 67 68 struct bhnd_nvram_bcm; 69 70 static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_gethdrvar( 71 struct bhnd_nvram_bcm *bcm, 72 const char *name); 73 static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_to_hdrvar( 74 struct bhnd_nvram_bcm *bcm, 75 void *cookiep); 76 static size_t bhnd_nvram_bcm_hdrvar_index( 77 struct bhnd_nvram_bcm *bcm, 78 struct bhnd_nvram_bcm_hvar *hvar); 79 /* 80 * Set of BCM NVRAM header values that are required to be mirrored in the 81 * NVRAM data itself. 82 * 83 * If they're not included in the parsed NVRAM data, we need to vend the 84 * header-parsed values with their appropriate keys, and add them in any 85 * updates to the NVRAM data. 86 * 87 * If they're modified in NVRAM, we need to sync the changes with the 88 * the NVRAM header values. 89 */ 90 static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = { 91 { 92 .name = BCM_NVRAM_CFG0_SDRAM_INIT_VAR, 93 .type = BHND_NVRAM_TYPE_UINT16, 94 .len = sizeof(uint16_t), 95 .nelem = 1, 96 }, 97 { 98 .name = BCM_NVRAM_CFG1_SDRAM_CFG_VAR, 99 .type = BHND_NVRAM_TYPE_UINT16, 100 .len = sizeof(uint16_t), 101 .nelem = 1, 102 }, 103 { 104 .name = BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR, 105 .type = BHND_NVRAM_TYPE_UINT16, 106 .len = sizeof(uint16_t), 107 .nelem = 1, 108 }, 109 { 110 .name = BCM_NVRAM_SDRAM_NCDL_VAR, 111 .type = BHND_NVRAM_TYPE_UINT32, 112 .len = sizeof(uint32_t), 113 .nelem = 1, 114 }, 115 }; 116 117 /** BCM NVRAM data class instance */ 118 struct bhnd_nvram_bcm { 119 struct bhnd_nvram_data nv; /**< common instance state */ 120 struct bhnd_nvram_io *data; /**< backing buffer */ 121 bhnd_nvram_plist *opts; /**< serialization options */ 122 123 /** BCM header values */ 124 struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)]; 125 126 size_t count; /**< total variable count */ 127 }; 128 129 BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", BHND_NVRAM_DATA_CAP_DEVPATHS, 130 sizeof(struct bhnd_nvram_bcm)) 131 132 static int 133 bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io) 134 { 135 struct bhnd_nvram_bcmhdr hdr; 136 int error; 137 138 if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr)))) 139 return (error); 140 141 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) 142 return (ENXIO); 143 144 if (le32toh(hdr.size) > bhnd_nvram_io_getsize(io)) 145 return (ENXIO); 146 147 return (BHND_NVRAM_DATA_PROBE_DEFAULT); 148 } 149 150 /** 151 * Parser states for bhnd_nvram_bcm_getvar_direct_common(). 152 */ 153 typedef enum { 154 BCM_PARSE_KEY_START, 155 BCM_PARSE_KEY_CONT, 156 BCM_PARSE_KEY, 157 BCM_PARSE_NEXT_KEY, 158 BCM_PARSE_VALUE_START, 159 BCM_PARSE_VALUE 160 } bcm_parse_state; 161 162 static int 163 bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io *io, const char *name, 164 void *outp, size_t *olen, bhnd_nvram_type otype) 165 { 166 return (bhnd_nvram_bcm_getvar_direct_common(io, name, outp, olen, otype, 167 true)); 168 } 169 170 /** 171 * Common BCM/BCMRAW implementation of bhnd_nvram_getvar_direct(). 172 */ 173 int 174 bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io *io, const char *name, 175 void *outp, size_t *olen, bhnd_nvram_type otype, bool have_header) 176 { 177 struct bhnd_nvram_bcmhdr hdr; 178 char buf[512]; 179 bcm_parse_state pstate; 180 size_t limit, offset; 181 size_t buflen, bufpos; 182 size_t namelen, namepos; 183 size_t vlen; 184 int error; 185 186 limit = bhnd_nvram_io_getsize(io); 187 offset = 0; 188 189 /* Fetch and validate the header */ 190 if (have_header) { 191 if ((error = bhnd_nvram_io_read(io, offset, &hdr, sizeof(hdr)))) 192 return (error); 193 194 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) 195 return (ENXIO); 196 197 offset += sizeof(hdr); 198 limit = bhnd_nv_ummin(le32toh(hdr.size), limit); 199 } 200 201 /* Loop our parser until we find the requested variable, or hit EOF */ 202 pstate = BCM_PARSE_KEY_START; 203 buflen = 0; 204 bufpos = 0; 205 namelen = strlen(name); 206 namepos = 0; 207 vlen = 0; 208 209 while ((offset - bufpos) < limit) { 210 BHND_NV_ASSERT(bufpos <= buflen, 211 ("buf position invalid (%zu > %zu)", bufpos, buflen)); 212 BHND_NV_ASSERT(buflen <= sizeof(buf), 213 ("buf length invalid (%zu > %zu", buflen, sizeof(buf))); 214 215 /* Repopulate our parse buffer? */ 216 if (buflen - bufpos == 0) { 217 BHND_NV_ASSERT(offset < limit, ("offset overrun")); 218 219 buflen = bhnd_nv_ummin(sizeof(buf), limit - offset); 220 bufpos = 0; 221 222 error = bhnd_nvram_io_read(io, offset, buf, buflen); 223 if (error) 224 return (error); 225 226 offset += buflen; 227 } 228 229 switch (pstate) { 230 case BCM_PARSE_KEY_START: 231 BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!")); 232 233 /* An extra '\0' denotes NVRAM EOF */ 234 if (buf[bufpos] == '\0') 235 return (ENOENT); 236 237 /* Reset name matching position */ 238 namepos = 0; 239 240 /* Start name matching */ 241 pstate = BCM_PARSE_KEY_CONT; 242 break; 243 244 case BCM_PARSE_KEY_CONT: { 245 size_t navail, nleft; 246 247 nleft = namelen - namepos; 248 navail = bhnd_nv_ummin(buflen - bufpos, nleft); 249 250 if (strncmp(name+namepos, buf+bufpos, navail) == 0) { 251 /* Matched */ 252 namepos += navail; 253 bufpos += navail; 254 255 /* If we've matched the full variable name, 256 * look for its trailing delimiter */ 257 if (namepos == namelen) 258 pstate = BCM_PARSE_KEY; 259 } else { 260 /* No match; advance to next entry and restart 261 * name matching */ 262 pstate = BCM_PARSE_NEXT_KEY; 263 } 264 265 break; 266 } 267 268 case BCM_PARSE_KEY: 269 BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!")); 270 271 if (buf[bufpos] == '=') { 272 /* Key fully matched; advance past '=' and 273 * parse the value */ 274 bufpos++; 275 pstate = BCM_PARSE_VALUE_START; 276 } else { 277 /* No match; advance to next entry and restart 278 * name matching */ 279 pstate = BCM_PARSE_NEXT_KEY; 280 } 281 282 break; 283 284 case BCM_PARSE_NEXT_KEY: { 285 const char *p; 286 287 /* Scan for a '\0' terminator */ 288 p = memchr(buf+bufpos, '\0', buflen - bufpos); 289 290 if (p != NULL) { 291 /* Found entry terminator; restart name 292 * matching at next entry */ 293 pstate = BCM_PARSE_KEY_START; 294 bufpos = (p - buf) + 1 /* skip '\0' */; 295 } else { 296 /* Consumed full buffer looking for '\0'; 297 * force repopulation of the buffer and 298 * retry */ 299 bufpos = buflen; 300 } 301 302 break; 303 } 304 305 case BCM_PARSE_VALUE_START: { 306 const char *p; 307 308 /* Scan for a '\0' terminator */ 309 p = memchr(buf+bufpos, '\0', buflen - bufpos); 310 311 if (p != NULL) { 312 /* Found entry terminator; parse the value */ 313 vlen = p - &buf[bufpos]; 314 pstate = BCM_PARSE_VALUE; 315 316 } else if (p == NULL && offset == limit) { 317 /* Hit EOF without a terminating '\0'; 318 * treat the entry as implicitly terminated */ 319 vlen = buflen - bufpos; 320 pstate = BCM_PARSE_VALUE; 321 322 } else if (p == NULL && bufpos > 0) { 323 size_t nread; 324 325 /* Move existing value data to start of 326 * buffer */ 327 memmove(buf, buf+bufpos, buflen - bufpos); 328 buflen = bufpos; 329 bufpos = 0; 330 331 /* Populate full buffer to allow retry of 332 * value parsing */ 333 nread = bhnd_nv_ummin(sizeof(buf) - buflen, 334 limit - offset); 335 336 error = bhnd_nvram_io_read(io, offset, 337 buf+buflen, nread); 338 if (error) 339 return (error); 340 341 offset += nread; 342 buflen += nread; 343 } else { 344 /* Value exceeds our buffer capacity */ 345 BHND_NV_LOG("cannot parse value for '%s' " 346 "(exceeds %zu byte limit)\n", name, 347 sizeof(buf)); 348 349 return (ENXIO); 350 } 351 352 break; 353 } 354 355 case BCM_PARSE_VALUE: 356 BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun")); 357 358 return (bhnd_nvram_value_coerce(buf+bufpos, vlen, 359 BHND_NVRAM_TYPE_STRING, outp, olen, otype)); 360 } 361 } 362 363 /* Variable not found */ 364 return (ENOENT); 365 } 366 367 static int 368 bhnd_nvram_bcm_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props, 369 bhnd_nvram_plist *options, void *outp, size_t *olen) 370 { 371 struct bhnd_nvram_bcmhdr hdr; 372 bhnd_nvram_prop *prop; 373 size_t limit, nbytes; 374 uint32_t sdram_ncdl; 375 uint16_t sdram_init, sdram_cfg, sdram_refresh; 376 uint8_t bcm_ver, crc8; 377 int error; 378 379 /* Determine output byte limit */ 380 if (outp != NULL) 381 limit = *olen; 382 else 383 limit = 0; 384 385 /* Fetch required header variables */ 386 #define PROPS_GET_HDRVAR(_name, _dest, _type) do { \ 387 const char *name = BCM_NVRAM_ ## _name ## _VAR; \ 388 if (!bhnd_nvram_plist_contains(props, name)) { \ 389 BHND_NV_LOG("missing required property: %s\n", \ 390 name); \ 391 return (EFTYPE); \ 392 } \ 393 \ 394 error = bhnd_nvram_plist_get_encoded(props, name, \ 395 (_dest), sizeof(*(_dest)), \ 396 BHND_NVRAM_TYPE_ ##_type); \ 397 if (error) { \ 398 BHND_NV_LOG("error reading required header " \ 399 "%s property: %d\n", name, error); \ 400 return (EFTYPE); \ 401 } \ 402 } while (0) 403 404 PROPS_GET_HDRVAR(SDRAM_NCDL, &sdram_ncdl, UINT32); 405 PROPS_GET_HDRVAR(CFG0_SDRAM_INIT, &sdram_init, UINT16); 406 PROPS_GET_HDRVAR(CFG1_SDRAM_CFG, &sdram_cfg, UINT16); 407 PROPS_GET_HDRVAR(CFG1_SDRAM_REFRESH, &sdram_refresh, UINT16); 408 409 #undef PROPS_GET_HDRVAR 410 411 /* Fetch BCM nvram version from options */ 412 if (options != NULL && 413 bhnd_nvram_plist_contains(options, BCM_NVRAM_ENCODE_OPT_VERSION)) 414 { 415 error = bhnd_nvram_plist_get_uint8(options, 416 BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver); 417 if (error) { 418 BHND_NV_LOG("error reading %s uint8 option value: %d\n", 419 BCM_NVRAM_ENCODE_OPT_VERSION, error); 420 return (EINVAL); 421 } 422 } else { 423 bcm_ver = BCM_NVRAM_CFG0_VER_DEFAULT; 424 } 425 426 /* Construct our header */ 427 hdr = (struct bhnd_nvram_bcmhdr) { 428 .magic = htole32(BCM_NVRAM_MAGIC), 429 .size = 0, 430 .cfg0 = 0, 431 .cfg1 = 0, 432 .sdram_ncdl = htole32(sdram_ncdl) 433 }; 434 435 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, 0x0); 436 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER, bcm_ver); 437 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_SDRAM_INIT, 438 htole16(sdram_init)); 439 440 hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_CFG, 441 htole16(sdram_cfg)); 442 hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_REFRESH, 443 htole16(sdram_refresh)); 444 445 /* Write the header */ 446 nbytes = sizeof(hdr); 447 if (limit >= nbytes) 448 memcpy(outp, &hdr, sizeof(hdr)); 449 450 /* Write all properties */ 451 prop = NULL; 452 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) { 453 const char *name; 454 char *p; 455 size_t prop_limit; 456 size_t name_len, value_len; 457 458 if (outp == NULL || limit < nbytes) { 459 p = NULL; 460 prop_limit = 0; 461 } else { 462 p = ((char *)outp) + nbytes; 463 prop_limit = limit - nbytes; 464 } 465 466 /* Fetch and write name + '=' to output */ 467 name = bhnd_nvram_prop_name(prop); 468 name_len = strlen(name) + 1; 469 470 if (prop_limit > name_len) { 471 memcpy(p, name, name_len - 1); 472 p[name_len - 1] = '='; 473 474 prop_limit -= name_len; 475 p += name_len; 476 } else { 477 prop_limit = 0; 478 p = NULL; 479 } 480 481 /* Advance byte count */ 482 if (SIZE_MAX - nbytes < name_len) 483 return (EFTYPE); /* would overflow size_t */ 484 485 nbytes += name_len; 486 487 /* Attempt to write NUL-terminated value to output */ 488 value_len = prop_limit; 489 error = bhnd_nvram_prop_encode(prop, p, &value_len, 490 BHND_NVRAM_TYPE_STRING); 491 492 /* If encoding failed for any reason other than ENOMEM (which 493 * we'll detect and report after encoding all properties), 494 * return immediately */ 495 if (error && error != ENOMEM) { 496 BHND_NV_LOG("error serializing %s to required type " 497 "%s: %d\n", name, 498 bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING), 499 error); 500 return (error); 501 } 502 503 /* Advance byte count */ 504 if (SIZE_MAX - nbytes < value_len) 505 return (EFTYPE); /* would overflow size_t */ 506 507 nbytes += value_len; 508 } 509 510 /* Write terminating '\0' */ 511 if (limit > nbytes) 512 *((char *)outp + nbytes) = '\0'; 513 514 if (nbytes == SIZE_MAX) 515 return (EFTYPE); /* would overflow size_t */ 516 else 517 nbytes++; 518 519 /* Update header length; this must fit within the header's 32-bit size 520 * field */ 521 if (nbytes <= UINT32_MAX) { 522 hdr.size = (uint32_t)nbytes; 523 } else { 524 BHND_NV_LOG("size %zu exceeds maximum supported size of %u " 525 "bytes\n", nbytes, UINT32_MAX); 526 return (EFTYPE); 527 } 528 529 /* Provide required length */ 530 *olen = nbytes; 531 if (limit < *olen) { 532 if (outp == NULL) 533 return (0); 534 535 return (ENOMEM); 536 } 537 538 /* Calculate the CRC value */ 539 BHND_NV_ASSERT(nbytes >= BCM_NVRAM_CRC_SKIP, ("invalid output size")); 540 crc8 = bhnd_nvram_crc8((uint8_t *)outp + BCM_NVRAM_CRC_SKIP, 541 nbytes - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL); 542 543 /* Update CRC and write the finalized header */ 544 BHND_NV_ASSERT(nbytes >= sizeof(hdr), ("invalid output size")); 545 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, crc8); 546 memcpy(outp, &hdr, sizeof(hdr)); 547 548 return (0); 549 } 550 551 /** 552 * Initialize @p bcm with the provided NVRAM data mapped by @p src. 553 * 554 * @param bcm A newly allocated data instance. 555 */ 556 static int 557 bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src) 558 { 559 struct bhnd_nvram_bcmhdr hdr; 560 uint8_t *p; 561 void *ptr; 562 size_t io_offset, io_size; 563 uint8_t crc, valid, bcm_ver; 564 int error; 565 566 if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr)))) 567 return (error); 568 569 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) 570 return (ENXIO); 571 572 /* Fetch the actual NVRAM image size */ 573 io_size = le32toh(hdr.size); 574 if (io_size < sizeof(hdr)) { 575 /* The header size must include the header itself */ 576 BHND_NV_LOG("corrupt header size: %zu\n", io_size); 577 return (EINVAL); 578 } 579 580 if (io_size > bhnd_nvram_io_getsize(src)) { 581 BHND_NV_LOG("header size %zu exceeds input size %zu\n", 582 io_size, bhnd_nvram_io_getsize(src)); 583 return (EINVAL); 584 } 585 586 /* Allocate a buffer large enough to hold the NVRAM image, and 587 * an extra EOF-signaling NUL (on the chance it's missing from the 588 * source data) */ 589 if (io_size == SIZE_MAX) 590 return (ENOMEM); 591 592 bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1); 593 if (bcm->data == NULL) 594 return (ENOMEM); 595 596 /* Fetch a pointer into our backing buffer and copy in the 597 * NVRAM image. */ 598 error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL); 599 if (error) 600 return (error); 601 602 p = ptr; 603 if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size))) 604 return (error); 605 606 /* Verify the CRC */ 607 valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC); 608 crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP, 609 io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL); 610 611 if (crc != valid) { 612 BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, " 613 "expected=%hhx)\n", crc, valid); 614 } 615 616 /* Populate header variable definitions */ 617 #define BCM_READ_HDR_VAR(_name, _dest, _swap) do { \ 618 struct bhnd_nvram_bcm_hvar *data; \ 619 data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR); \ 620 BHND_NV_ASSERT(data != NULL, \ 621 ("no such header variable: " __STRING(_name))); \ 622 \ 623 \ 624 data->value. _dest = _swap(BCM_NVRAM_GET_BITS( \ 625 hdr. _name ## _FIELD, _name)); \ 626 } while(0) 627 628 BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT, u16, le16toh); 629 BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG, u16, le16toh); 630 BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH, u16, le16toh); 631 BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL, u32, le32toh); 632 633 _Static_assert(nitems(bcm->hvars) == 4, "missing initialization for" 634 "NVRAM header variable(s)"); 635 636 #undef BCM_READ_HDR_VAR 637 638 /* Process the buffer */ 639 bcm->count = 0; 640 io_offset = sizeof(hdr); 641 while (io_offset < io_size) { 642 char *envp; 643 const char *name, *value; 644 size_t envp_len; 645 size_t name_len, value_len; 646 647 /* Parse the key=value string */ 648 envp = (char *) (p + io_offset); 649 envp_len = strnlen(envp, io_size - io_offset); 650 error = bhnd_nvram_parse_env(envp, envp_len, '=', &name, 651 &name_len, &value, &value_len); 652 if (error) { 653 BHND_NV_LOG("error parsing envp at offset %#zx: %d\n", 654 io_offset, error); 655 return (error); 656 } 657 658 /* Insert a '\0' character, replacing the '=' delimiter and 659 * allowing us to vend references directly to the variable 660 * name */ 661 *(envp + name_len) = '\0'; 662 663 /* Record any NVRAM variables that mirror our header variables. 664 * This is a brute-force search -- for the amount of data we're 665 * operating on, it shouldn't be an issue. */ 666 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 667 struct bhnd_nvram_bcm_hvar *hvar; 668 union bhnd_nvram_bcm_hvar_value hval; 669 size_t hval_len; 670 671 hvar = &bcm->hvars[i]; 672 673 /* Already matched? */ 674 if (hvar->envp != NULL) 675 continue; 676 677 /* Name matches? */ 678 if ((strcmp(name, hvar->name)) != 0) 679 continue; 680 681 /* Save pointer to mirrored envp */ 682 hvar->envp = envp; 683 684 /* Check for stale value */ 685 hval_len = sizeof(hval); 686 error = bhnd_nvram_value_coerce(value, value_len, 687 BHND_NVRAM_TYPE_STRING, &hval, &hval_len, 688 hvar->type); 689 if (error) { 690 /* If parsing fails, we can likely only make 691 * things worse by trying to synchronize the 692 * variables */ 693 BHND_NV_LOG("error parsing header variable " 694 "'%s=%s': %d\n", name, value, error); 695 } else if (hval_len != hvar->len) { 696 hvar->stale = true; 697 } else if (memcmp(&hval, &hvar->value, hval_len) != 0) { 698 hvar->stale = true; 699 } 700 } 701 702 /* Seek past the value's terminating '\0' */ 703 io_offset += envp_len; 704 if (io_offset == io_size) { 705 BHND_NV_LOG("missing terminating NUL at offset %#zx\n", 706 io_offset); 707 return (EINVAL); 708 } 709 710 if (*(p + io_offset) != '\0') { 711 BHND_NV_LOG("invalid terminator '%#hhx' at offset " 712 "%#zx\n", *(p + io_offset), io_offset); 713 return (EINVAL); 714 } 715 716 /* Update variable count */ 717 bcm->count++; 718 719 /* Seek to the next record */ 720 if (++io_offset == io_size) { 721 char ch; 722 723 /* Hit EOF without finding a terminating NUL 724 * byte; we need to grow our buffer and append 725 * it */ 726 io_size++; 727 if ((error = bhnd_nvram_io_setsize(bcm->data, io_size))) 728 return (error); 729 730 /* Write NUL byte */ 731 ch = '\0'; 732 error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch, 733 sizeof(ch)); 734 if (error) 735 return (error); 736 } 737 738 /* Check for explicit EOF (encoded as a single empty NUL 739 * terminated string) */ 740 if (*(p + io_offset) == '\0') 741 break; 742 } 743 744 /* Add non-mirrored header variables to total count variable */ 745 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 746 if (bcm->hvars[i].envp == NULL) 747 bcm->count++; 748 } 749 750 /* Populate serialization options from our header */ 751 bcm_ver = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER); 752 error = bhnd_nvram_plist_append_bytes(bcm->opts, 753 BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver, sizeof(bcm_ver), 754 BHND_NVRAM_TYPE_UINT8); 755 if (error) 756 return (error); 757 758 return (0); 759 } 760 761 static int 762 bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) 763 { 764 struct bhnd_nvram_bcm *bcm; 765 int error; 766 767 bcm = (struct bhnd_nvram_bcm *)nv; 768 769 /* Populate default BCM mirrored header variable set */ 770 _Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars), 771 "hvar declarations must match bhnd_nvram_bcm_hvars template"); 772 memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars)); 773 774 /* Allocate (empty) option list, to be populated by 775 * bhnd_nvram_bcm_init() */ 776 bcm->opts = bhnd_nvram_plist_new(); 777 if (bcm->opts == NULL) 778 return (ENOMEM); 779 780 /* Parse the BCM input data and initialize our backing 781 * data representation */ 782 if ((error = bhnd_nvram_bcm_init(bcm, io))) { 783 bhnd_nvram_bcm_free(nv); 784 return (error); 785 } 786 787 return (0); 788 } 789 790 static void 791 bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv) 792 { 793 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; 794 795 if (bcm->data != NULL) 796 bhnd_nvram_io_free(bcm->data); 797 798 if (bcm->opts != NULL) 799 bhnd_nvram_plist_release(bcm->opts); 800 } 801 802 size_t 803 bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv) 804 { 805 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; 806 return (bcm->count); 807 } 808 809 static bhnd_nvram_plist * 810 bhnd_nvram_bcm_options(struct bhnd_nvram_data *nv) 811 { 812 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; 813 return (bcm->opts); 814 } 815 816 static uint32_t 817 bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv) 818 { 819 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); 820 } 821 822 static const char * 823 bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep) 824 { 825 struct bhnd_nvram_bcm *bcm; 826 struct bhnd_nvram_bcm_hvar *hvar, *hvar_next; 827 const void *ptr; 828 const char *envp, *basep; 829 size_t io_size, io_offset; 830 int error; 831 832 bcm = (struct bhnd_nvram_bcm *)nv; 833 834 io_offset = sizeof(struct bhnd_nvram_bcmhdr); 835 io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset; 836 837 /* Map backing buffer */ 838 error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size, 839 NULL); 840 if (error) { 841 BHND_NV_LOG("error mapping backing buffer: %d\n", error); 842 return (NULL); 843 } 844 845 basep = ptr; 846 847 /* If cookiep pointers into our header variable array, handle as header 848 * variable iteration. */ 849 hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep); 850 if (hvar != NULL) { 851 size_t idx; 852 853 /* Advance to next entry, if any */ 854 idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1; 855 856 /* Find the next header-defined variable that isn't defined in 857 * the NVRAM data, start iteration there */ 858 for (size_t i = idx; i < nitems(bcm->hvars); i++) { 859 hvar_next = &bcm->hvars[i]; 860 if (hvar_next->envp != NULL && !hvar_next->stale) 861 continue; 862 863 *cookiep = hvar_next; 864 return (hvar_next->name); 865 } 866 867 /* No further header-defined variables; iteration 868 * complete */ 869 return (NULL); 870 } 871 872 /* Handle standard NVRAM data iteration */ 873 if (*cookiep == NULL) { 874 /* Start at the first NVRAM data record */ 875 envp = basep; 876 } else { 877 /* Seek to next record */ 878 envp = *cookiep; 879 envp += strlen(envp) + 1; /* key + '\0' */ 880 envp += strlen(envp) + 1; /* value + '\0' */ 881 } 882 883 /* 884 * Skip entries that have an existing header variable entry that takes 885 * precedence over the NVRAM data value. 886 * 887 * The header's value will be provided when performing header variable 888 * iteration 889 */ 890 while ((size_t)(envp - basep) < io_size && *envp != '\0') { 891 /* Locate corresponding header variable */ 892 hvar = NULL; 893 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 894 if (bcm->hvars[i].envp != envp) 895 continue; 896 897 hvar = &bcm->hvars[i]; 898 break; 899 } 900 901 /* If no corresponding hvar entry, or the entry does not take 902 * precedence over this NVRAM value, we can safely return this 903 * value as-is. */ 904 if (hvar == NULL || !hvar->stale) 905 break; 906 907 /* Seek to next record */ 908 envp += strlen(envp) + 1; /* key + '\0' */ 909 envp += strlen(envp) + 1; /* value + '\0' */ 910 } 911 912 /* On NVRAM data EOF, try switching to header variables */ 913 if ((size_t)(envp - basep) == io_size || *envp == '\0') { 914 /* Find first valid header variable */ 915 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 916 if (bcm->hvars[i].envp != NULL) 917 continue; 918 919 *cookiep = &bcm->hvars[i]; 920 return (bcm->hvars[i].name); 921 } 922 923 /* No header variables */ 924 return (NULL); 925 } 926 927 *cookiep = __DECONST(void *, envp); 928 return (envp); 929 } 930 931 static void * 932 bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name) 933 { 934 return (bhnd_nvram_data_generic_find(nv, name)); 935 } 936 937 static int 938 bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, 939 void *cookiep2) 940 { 941 struct bhnd_nvram_bcm *bcm; 942 struct bhnd_nvram_bcm_hvar *hvar1, *hvar2; 943 944 bcm = (struct bhnd_nvram_bcm *)nv; 945 946 hvar1 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep1); 947 hvar2 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep2); 948 949 /* Header variables are always ordered below any variables defined 950 * in the BCM data */ 951 if (hvar1 != NULL && hvar2 == NULL) { 952 return (1); /* hvar follows non-hvar */ 953 } else if (hvar1 == NULL && hvar2 != NULL) { 954 return (-1); /* non-hvar precedes hvar */ 955 } 956 957 /* Otherwise, both cookies are either hvars or non-hvars. We can 958 * safely fall back on pointer order, which will provide a correct 959 * ordering matching the behavior of bhnd_nvram_data_next() for 960 * both cases */ 961 if (cookiep1 < cookiep2) 962 return (-1); 963 964 if (cookiep1 > cookiep2) 965 return (1); 966 967 return (0); 968 } 969 970 static int 971 bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, 972 size_t *len, bhnd_nvram_type type) 973 { 974 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); 975 } 976 977 static int 978 bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data *nv, void *cookiep, 979 bhnd_nvram_val **value) 980 { 981 return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value)); 982 } 983 984 static const void * 985 bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, 986 size_t *len, bhnd_nvram_type *type) 987 { 988 struct bhnd_nvram_bcm *bcm; 989 struct bhnd_nvram_bcm_hvar *hvar; 990 const char *envp; 991 992 bcm = (struct bhnd_nvram_bcm *)nv; 993 994 /* Handle header variables */ 995 if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) { 996 BHND_NV_ASSERT(bhnd_nvram_value_check_aligned(&hvar->value, 997 hvar->len, hvar->type) == 0, ("value misaligned")); 998 999 *type = hvar->type; 1000 *len = hvar->len; 1001 return (&hvar->value); 1002 } 1003 1004 /* Cookie points to key\0value\0 -- get the value address */ 1005 BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep")); 1006 1007 envp = cookiep; 1008 envp += strlen(envp) + 1; /* key + '\0' */ 1009 *len = strlen(envp) + 1; /* value + '\0' */ 1010 *type = BHND_NVRAM_TYPE_STRING; 1011 1012 return (envp); 1013 } 1014 1015 static const char * 1016 bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) 1017 { 1018 struct bhnd_nvram_bcm *bcm; 1019 struct bhnd_nvram_bcm_hvar *hvar; 1020 1021 bcm = (struct bhnd_nvram_bcm *)nv; 1022 1023 /* Handle header variables */ 1024 if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) { 1025 return (hvar->name); 1026 } 1027 1028 /* Cookie points to key\0value\0 */ 1029 return (cookiep); 1030 } 1031 1032 static int 1033 bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data *nv, const char *name, 1034 bhnd_nvram_val *value, bhnd_nvram_val **result) 1035 { 1036 bhnd_nvram_val *str; 1037 int error; 1038 1039 /* Name (trimmed of any path prefix) must be valid */ 1040 if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name))) 1041 return (EINVAL); 1042 1043 /* Value must be bcm-formatted string */ 1044 error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt, 1045 value, BHND_NVRAM_VAL_DYNAMIC); 1046 if (error) 1047 return (error); 1048 1049 /* Success. Transfer result ownership to the caller. */ 1050 *result = str; 1051 return (0); 1052 } 1053 1054 static int 1055 bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) 1056 { 1057 /* We permit deletion of any variable */ 1058 return (0); 1059 } 1060 1061 /** 1062 * Return the internal BCM data reference for a header-defined variable 1063 * with @p name, or NULL if none exists. 1064 */ 1065 static struct bhnd_nvram_bcm_hvar * 1066 bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name) 1067 { 1068 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 1069 if (strcmp(bcm->hvars[i].name, name) == 0) 1070 return (&bcm->hvars[i]); 1071 } 1072 1073 /* Not found */ 1074 return (NULL); 1075 } 1076 1077 /** 1078 * If @p cookiep references a header-defined variable, return the 1079 * internal BCM data reference. Otherwise, returns NULL. 1080 */ 1081 static struct bhnd_nvram_bcm_hvar * 1082 bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep) 1083 { 1084 #ifdef BHND_NVRAM_INVARIANTS 1085 uintptr_t base, ptr; 1086 #endif 1087 1088 /* If the cookie falls within the hvar array, it's a 1089 * header variable cookie */ 1090 if (nitems(bcm->hvars) == 0) 1091 return (NULL); 1092 1093 if (cookiep < (void *)&bcm->hvars[0]) 1094 return (NULL); 1095 1096 if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1]) 1097 return (NULL); 1098 1099 #ifdef BHND_NVRAM_INVARIANTS 1100 base = (uintptr_t)bcm->hvars; 1101 ptr = (uintptr_t)cookiep; 1102 1103 BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0, 1104 ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars)); 1105 #endif /* INVARIANTS */ 1106 1107 return ((struct bhnd_nvram_bcm_hvar *)cookiep); 1108 } 1109 1110 /** 1111 * Return the index of @p hdrvar within @p bcm's backing hvars array. 1112 */ 1113 static size_t 1114 bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm, 1115 struct bhnd_nvram_bcm_hvar *hdrvar) 1116 { 1117 BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL, 1118 ("%p is not a valid hdrvar reference", hdrvar)); 1119 1120 return (hdrvar - &bcm->hvars[0]); 1121 } 1122