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