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 125 /** BCM header values */ 126 struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)]; 127 128 size_t count; /**< total variable count */ 129 }; 130 131 BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", sizeof(struct bhnd_nvram_bcm)) 132 133 static int 134 bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io) 135 { 136 struct bhnd_nvram_bcmhdr hdr; 137 int error; 138 139 if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr)))) 140 return (error); 141 142 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) 143 return (ENXIO); 144 145 return (BHND_NVRAM_DATA_PROBE_DEFAULT); 146 } 147 148 /** 149 * Initialize @p bcm with the provided NVRAM data mapped by @p src. 150 * 151 * @param bcm A newly allocated data instance. 152 */ 153 static int 154 bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src) 155 { 156 struct bhnd_nvram_bcmhdr hdr; 157 uint8_t *p; 158 void *ptr; 159 size_t io_offset, io_size; 160 uint8_t crc, valid; 161 int error; 162 163 if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr)))) 164 return (error); 165 166 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) 167 return (ENXIO); 168 169 /* Fetch the actual NVRAM image size */ 170 io_size = le32toh(hdr.size); 171 if (io_size < sizeof(hdr)) { 172 /* The header size must include the header itself */ 173 BHND_NV_LOG("corrupt header size: %zu\n", io_size); 174 return (EINVAL); 175 } 176 177 if (io_size > bhnd_nvram_io_getsize(src)) { 178 BHND_NV_LOG("header size %zu exceeds input size %zu\n", 179 io_size, bhnd_nvram_io_getsize(src)); 180 return (EINVAL); 181 } 182 183 /* Allocate a buffer large enough to hold the NVRAM image, and 184 * an extra EOF-signaling NUL (on the chance it's missing from the 185 * source data) */ 186 if (io_size == SIZE_MAX) 187 return (ENOMEM); 188 189 bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1); 190 if (bcm->data == NULL) 191 return (ENOMEM); 192 193 /* Fetch a pointer into our backing buffer and copy in the 194 * NVRAM image. */ 195 error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL); 196 if (error) 197 return (error); 198 199 p = ptr; 200 if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size))) 201 return (error); 202 203 /* Verify the CRC */ 204 valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC); 205 crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP, 206 io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL); 207 208 if (crc != valid) { 209 BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, " 210 "expected=%hhx)\n", crc, valid); 211 } 212 213 /* Populate header variable definitions */ 214 #define BCM_READ_HDR_VAR(_name, _dest, _swap) do { \ 215 struct bhnd_nvram_bcm_hvar *data; \ 216 data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR); \ 217 BHND_NV_ASSERT(data != NULL, \ 218 ("no such header variable: " __STRING(_name))); \ 219 \ 220 \ 221 data->value. _dest = _swap(BCM_NVRAM_GET_BITS( \ 222 hdr. _name ## _FIELD, _name)); \ 223 } while(0) 224 225 BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT, u16, le16toh); 226 BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG, u16, le16toh); 227 BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH, u16, le16toh); 228 BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL, u32, le32toh); 229 230 _Static_assert(nitems(bcm->hvars) == 4, "missing initialization for" 231 "NVRAM header variable(s)"); 232 233 #undef BCM_READ_HDR_VAR 234 235 /* Process the buffer */ 236 bcm->count = 0; 237 io_offset = sizeof(hdr); 238 while (io_offset < io_size) { 239 char *envp; 240 const char *name, *value; 241 size_t envp_len; 242 size_t name_len, value_len; 243 244 /* Parse the key=value string */ 245 envp = (char *) (p + io_offset); 246 envp_len = strnlen(envp, io_size - io_offset); 247 error = bhnd_nvram_parse_env(envp, envp_len, '=', &name, 248 &name_len, &value, &value_len); 249 if (error) { 250 BHND_NV_LOG("error parsing envp at offset %#zx: %d\n", 251 io_offset, error); 252 return (error); 253 } 254 255 /* Insert a '\0' character, replacing the '=' delimiter and 256 * allowing us to vend references directly to the variable 257 * name */ 258 *(envp + name_len) = '\0'; 259 260 /* Record any NVRAM variables that mirror our header variables. 261 * This is a brute-force search -- for the amount of data we're 262 * operating on, it shouldn't be an issue. */ 263 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 264 struct bhnd_nvram_bcm_hvar *hvar; 265 union bhnd_nvram_bcm_hvar_value hval; 266 size_t hval_len; 267 268 hvar = &bcm->hvars[i]; 269 270 /* Already matched? */ 271 if (hvar->envp != NULL) 272 continue; 273 274 /* Name matches? */ 275 if ((strcmp(name, hvar->name)) != 0) 276 continue; 277 278 /* Save pointer to mirrored envp */ 279 hvar->envp = envp; 280 281 /* Check for stale value */ 282 hval_len = sizeof(hval); 283 error = bhnd_nvram_value_coerce(value, value_len, 284 BHND_NVRAM_TYPE_STRING, &hval, &hval_len, 285 hvar->type); 286 if (error) { 287 /* If parsing fails, we can likely only make 288 * things worse by trying to synchronize the 289 * variables */ 290 BHND_NV_LOG("error parsing header variable " 291 "'%s=%s': %d\n", name, value, error); 292 } else if (hval_len != hvar->len) { 293 hvar->stale = true; 294 } else if (memcmp(&hval, &hvar->value, hval_len) != 0) { 295 hvar->stale = true; 296 } 297 } 298 299 /* Seek past the value's terminating '\0' */ 300 io_offset += envp_len; 301 if (io_offset == io_size) { 302 BHND_NV_LOG("missing terminating NUL at offset %#zx\n", 303 io_offset); 304 return (EINVAL); 305 } 306 307 if (*(p + io_offset) != '\0') { 308 BHND_NV_LOG("invalid terminator '%#hhx' at offset " 309 "%#zx\n", *(p + io_offset), io_offset); 310 return (EINVAL); 311 } 312 313 /* Update variable count */ 314 bcm->count++; 315 316 /* Seek to the next record */ 317 if (++io_offset == io_size) { 318 char ch; 319 320 /* Hit EOF without finding a terminating NUL 321 * byte; we need to grow our buffer and append 322 * it */ 323 io_size++; 324 if ((error = bhnd_nvram_io_setsize(bcm->data, io_size))) 325 return (error); 326 327 /* Write NUL byte */ 328 ch = '\0'; 329 error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch, 330 sizeof(ch)); 331 if (error) 332 return (error); 333 } 334 335 /* Check for explicit EOF (encoded as a single empty NUL 336 * terminated string) */ 337 if (*(p + io_offset) == '\0') 338 break; 339 } 340 341 /* Add non-mirrored header variables to total count variable */ 342 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 343 if (bcm->hvars[i].envp == NULL) 344 bcm->count++; 345 } 346 347 return (0); 348 } 349 350 static int 351 bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) 352 { 353 struct bhnd_nvram_bcm *bcm; 354 int error; 355 356 bcm = (struct bhnd_nvram_bcm *)nv; 357 358 /* Populate default BCM mirrored header variable set */ 359 _Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars), 360 "hvar declarations must match bhnd_nvram_bcm_hvars template"); 361 memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars)); 362 363 /* Parse the BCM input data and initialize our backing 364 * data representation */ 365 if ((error = bhnd_nvram_bcm_init(bcm, io))) { 366 bhnd_nvram_bcm_free(nv); 367 return (error); 368 } 369 370 return (0); 371 } 372 373 static void 374 bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv) 375 { 376 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; 377 378 if (bcm->data != NULL) 379 bhnd_nvram_io_free(bcm->data); 380 } 381 382 size_t 383 bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv) 384 { 385 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv; 386 return (bcm->count); 387 } 388 389 static int 390 bhnd_nvram_bcm_size(struct bhnd_nvram_data *nv, size_t *size) 391 { 392 return (bhnd_nvram_bcm_serialize(nv, NULL, size)); 393 } 394 395 static int 396 bhnd_nvram_bcm_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) 397 { 398 struct bhnd_nvram_bcm *bcm; 399 struct bhnd_nvram_bcmhdr hdr; 400 void *cookiep; 401 const char *name; 402 size_t nbytes, limit; 403 uint8_t crc; 404 int error; 405 406 bcm = (struct bhnd_nvram_bcm *)nv; 407 nbytes = 0; 408 409 /* Save the output buffer limit */ 410 if (buf == NULL) 411 limit = 0; 412 else 413 limit = *len; 414 415 /* Reserve space for the NVRAM header */ 416 nbytes += sizeof(struct bhnd_nvram_bcmhdr); 417 418 /* Write all variables to the output buffer */ 419 cookiep = NULL; 420 while ((name = bhnd_nvram_data_next(nv, &cookiep))) { 421 uint8_t *outp; 422 size_t olen; 423 size_t name_len, val_len; 424 425 if (limit > nbytes) { 426 outp = (uint8_t *)buf + nbytes; 427 olen = limit - nbytes; 428 } else { 429 outp = NULL; 430 olen = 0; 431 } 432 433 /* Determine length of variable name */ 434 name_len = strlen(name) + 1; 435 436 /* Write the variable name and '=' delimiter */ 437 if (olen >= name_len) { 438 /* Copy name */ 439 memcpy(outp, name, name_len - 1); 440 441 /* Append '=' */ 442 *(outp + name_len - 1) = '='; 443 } 444 445 /* Adjust byte counts */ 446 if (SIZE_MAX - name_len < nbytes) 447 return (ERANGE); 448 449 nbytes += name_len; 450 451 /* Reposition output */ 452 if (limit > nbytes) { 453 outp = (uint8_t *)buf + nbytes; 454 olen = limit - nbytes; 455 } else { 456 outp = NULL; 457 olen = 0; 458 } 459 460 /* Coerce to NUL-terminated C string, writing to the output 461 * buffer (or just calculating the length if outp is NULL) */ 462 val_len = olen; 463 error = bhnd_nvram_data_getvar(nv, cookiep, outp, &val_len, 464 BHND_NVRAM_TYPE_STRING); 465 466 if (error && error != ENOMEM) 467 return (error); 468 469 /* Adjust byte counts */ 470 if (SIZE_MAX - val_len < nbytes) 471 return (ERANGE); 472 473 nbytes += val_len; 474 } 475 476 /* Write terminating NUL */ 477 if (nbytes < limit) 478 *((uint8_t *)buf + nbytes) = '\0'; 479 nbytes++; 480 481 /* Provide actual size */ 482 *len = nbytes; 483 if (buf == NULL || nbytes > limit) { 484 if (buf != NULL) 485 return (ENOMEM); 486 487 return (0); 488 } 489 490 /* Fetch current NVRAM header */ 491 if ((error = bhnd_nvram_io_read(bcm->data, 0x0, &hdr, sizeof(hdr)))) 492 return (error); 493 494 /* Update values covered by CRC and write to output buffer */ 495 hdr.size = htole32(*len); 496 memcpy(buf, &hdr, sizeof(hdr)); 497 498 /* Calculate new CRC */ 499 crc = bhnd_nvram_crc8((uint8_t *)buf + BCM_NVRAM_CRC_SKIP, 500 *len - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL); 501 502 /* Update header with valid CRC */ 503 hdr.cfg0 &= ~BCM_NVRAM_CFG0_CRC_MASK; 504 hdr.cfg0 |= (crc << BCM_NVRAM_CFG0_CRC_SHIFT); 505 memcpy(buf, &hdr, sizeof(hdr)); 506 507 return (0); 508 } 509 510 static uint32_t 511 bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv) 512 { 513 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); 514 } 515 516 static const char * 517 bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep) 518 { 519 struct bhnd_nvram_bcm *bcm; 520 struct bhnd_nvram_bcm_hvar *hvar, *hvar_next; 521 const void *ptr; 522 const char *envp, *basep; 523 size_t io_size, io_offset; 524 int error; 525 526 bcm = (struct bhnd_nvram_bcm *)nv; 527 528 io_offset = sizeof(struct bhnd_nvram_bcmhdr); 529 io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset; 530 531 /* Map backing buffer */ 532 error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size, 533 NULL); 534 if (error) { 535 BHND_NV_LOG("error mapping backing buffer: %d\n", error); 536 return (NULL); 537 } 538 539 basep = ptr; 540 541 /* If cookiep pointers into our header variable array, handle as header 542 * variable iteration. */ 543 hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep); 544 if (hvar != NULL) { 545 size_t idx; 546 547 /* Advance to next entry, if any */ 548 idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1; 549 550 /* Find the next header-defined variable that isn't defined in 551 * the NVRAM data, start iteration there */ 552 for (size_t i = idx; i < nitems(bcm->hvars); i++) { 553 hvar_next = &bcm->hvars[i]; 554 if (hvar_next->envp != NULL && !hvar_next->stale) 555 continue; 556 557 *cookiep = hvar_next; 558 return (hvar_next->name); 559 } 560 561 /* No further header-defined variables; iteration 562 * complete */ 563 return (NULL); 564 } 565 566 /* Handle standard NVRAM data iteration */ 567 if (*cookiep == NULL) { 568 /* Start at the first NVRAM data record */ 569 envp = basep; 570 } else { 571 /* Seek to next record */ 572 envp = *cookiep; 573 envp += strlen(envp) + 1; /* key + '\0' */ 574 envp += strlen(envp) + 1; /* value + '\0' */ 575 } 576 577 /* 578 * Skip entries that have an existing header variable entry that takes 579 * precedence over the NVRAM data value. 580 * 581 * The header's value will be provided when performing header variable 582 * iteration 583 */ 584 while ((size_t)(envp - basep) < io_size && *envp != '\0') { 585 /* Locate corresponding header variable */ 586 hvar = NULL; 587 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 588 if (bcm->hvars[i].envp != envp) 589 continue; 590 591 hvar = &bcm->hvars[i]; 592 break; 593 } 594 595 /* If no corresponding hvar entry, or the entry does not take 596 * precedence over this NVRAM value, we can safely return this 597 * value as-is. */ 598 if (hvar == NULL || !hvar->stale) 599 break; 600 601 /* Seek to next record */ 602 envp += strlen(envp) + 1; /* key + '\0' */ 603 envp += strlen(envp) + 1; /* value + '\0' */ 604 } 605 606 /* On NVRAM data EOF, try switching to header variables */ 607 if ((size_t)(envp - basep) == io_size || *envp == '\0') { 608 /* Find first valid header variable */ 609 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 610 if (bcm->hvars[i].envp != NULL) 611 continue; 612 613 *cookiep = &bcm->hvars[i]; 614 return (bcm->hvars[i].name); 615 } 616 617 /* No header variables */ 618 return (NULL); 619 } 620 621 *cookiep = (void *)(uintptr_t)envp; 622 return (envp); 623 } 624 625 static void * 626 bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name) 627 { 628 return (bhnd_nvram_data_generic_find(nv, name)); 629 } 630 631 static int 632 bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, 633 size_t *len, bhnd_nvram_type type) 634 { 635 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); 636 } 637 638 static const void * 639 bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, 640 size_t *len, bhnd_nvram_type *type) 641 { 642 struct bhnd_nvram_bcm *bcm; 643 struct bhnd_nvram_bcm_hvar *hvar; 644 const char *envp; 645 646 bcm = (struct bhnd_nvram_bcm *)nv; 647 648 /* Handle header variables */ 649 if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) { 650 BHND_NV_ASSERT( 651 hvar->len % bhnd_nvram_value_size(hvar->type, NULL, 0, 652 hvar->nelem) == 0, 653 ("length is not aligned to type width")); 654 655 *type = hvar->type; 656 *len = hvar->len; 657 return (&hvar->value); 658 } 659 660 /* Cookie points to key\0value\0 -- get the value address */ 661 BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep")); 662 663 envp = cookiep; 664 envp += strlen(envp) + 1; /* key + '\0' */ 665 *len = strlen(envp) + 1; /* value + '\0' */ 666 *type = BHND_NVRAM_TYPE_STRING; 667 668 return (envp); 669 } 670 671 static const char * 672 bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) 673 { 674 struct bhnd_nvram_bcm *bcm; 675 struct bhnd_nvram_bcm_hvar *hvar; 676 677 bcm = (struct bhnd_nvram_bcm *)nv; 678 679 /* Handle header variables */ 680 if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) { 681 return (hvar->name); 682 } 683 684 /* Cookie points to key\0value\0 */ 685 return (cookiep); 686 } 687 688 /** 689 * Return the internal BCM data reference for a header-defined variable 690 * with @p name, or NULL if none exists. 691 */ 692 static struct bhnd_nvram_bcm_hvar * 693 bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name) 694 { 695 for (size_t i = 0; i < nitems(bcm->hvars); i++) { 696 if (strcmp(bcm->hvars[i].name, name) == 0) 697 return (&bcm->hvars[i]); 698 } 699 700 /* Not found */ 701 return (NULL); 702 } 703 704 /** 705 * If @p cookiep references a header-defined variable, return the 706 * internal BCM data reference. Otherwise, returns NULL. 707 */ 708 static struct bhnd_nvram_bcm_hvar * 709 bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep) 710 { 711 #ifdef BHND_NVRAM_INVARIANTS 712 uintptr_t base, ptr; 713 #endif 714 715 /* If the cookie falls within the hvar array, it's a 716 * header variable cookie */ 717 if (nitems(bcm->hvars) == 0) 718 return (NULL); 719 720 if (cookiep < (void *)&bcm->hvars[0]) 721 return (NULL); 722 723 if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1]) 724 return (NULL); 725 726 #ifdef BHND_NVRAM_INVARIANTS 727 base = (uintptr_t)bcm->hvars; 728 ptr = (uintptr_t)cookiep; 729 730 BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0, 731 ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars)); 732 #endif /* INVARIANTS */ 733 734 return ((struct bhnd_nvram_bcm_hvar *)cookiep); 735 } 736 737 /** 738 * Return the index of @p hdrvar within @p bcm's backing hvars array. 739 */ 740 static size_t 741 bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm, 742 struct bhnd_nvram_bcm_hvar *hdrvar) 743 { 744 BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL, 745 ("%p is not a valid hdrvar reference", hdrvar)); 746 747 return (hdrvar - &bcm->hvars[0]); 748 } 749