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 #ifdef _KERNEL 34 #include <sys/param.h> 35 #include <sys/ctype.h> 36 #include <sys/malloc.h> 37 #include <sys/systm.h> 38 #else /* !_KERNEL */ 39 #include <ctype.h> 40 #include <errno.h> 41 #include <stdint.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #endif /* _KERNEL */ 46 47 #include "bhnd_nvram_private.h" 48 49 #include "bhnd_nvram_datavar.h" 50 51 #include "bhnd_nvram_data_tlvreg.h" 52 53 /* 54 * CFE TLV NVRAM data class. 55 * 56 * The CFE-defined TLV NVRAM format is used on the WGT634U. 57 */ 58 59 struct bhnd_nvram_tlv { 60 struct bhnd_nvram_data nv; /**< common instance state */ 61 struct bhnd_nvram_io *data; /**< backing buffer */ 62 size_t count; /**< variable count */ 63 }; 64 65 BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", BHND_NVRAM_DATA_CAP_DEVPATHS, 66 sizeof(struct bhnd_nvram_tlv)) 67 68 /** Minimal TLV_ENV record header */ 69 struct bhnd_nvram_tlv_env_hdr { 70 uint8_t tag; 71 uint8_t size; 72 } __packed; 73 74 /** Minimal TLV_ENV record */ 75 struct bhnd_nvram_tlv_env { 76 struct bhnd_nvram_tlv_env_hdr hdr; 77 uint8_t flags; 78 char envp[]; 79 } __packed; 80 81 /* Return the length in bytes of an TLV_ENV's envp data */ 82 #define NVRAM_TLV_ENVP_DATA_LEN(_env) \ 83 (((_env)->hdr.size < sizeof((_env)->flags)) ? 0 : \ 84 ((_env)->hdr.size - sizeof((_env)->flags))) 85 86 /* Maximum supported length of the envp data field, in bytes */ 87 #define NVRAM_TLV_ENVP_DATA_MAX_LEN \ 88 (UINT8_MAX - sizeof(uint8_t) /* flags */) 89 90 91 static int bhnd_nvram_tlv_parse_size( 92 struct bhnd_nvram_io *io, 93 size_t *size); 94 95 static int bhnd_nvram_tlv_next_record( 96 struct bhnd_nvram_io *io, 97 size_t *next, size_t *offset, 98 uint8_t *tag); 99 100 static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_next_env( 101 struct bhnd_nvram_tlv *tlv, 102 size_t *next, void **cookiep); 103 104 static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_get_env( 105 struct bhnd_nvram_tlv *tlv, 106 void *cookiep); 107 108 static void *bhnd_nvram_tlv_to_cookie( 109 struct bhnd_nvram_tlv *tlv, 110 size_t io_offset); 111 static size_t bhnd_nvram_tlv_to_offset( 112 struct bhnd_nvram_tlv *tlv, 113 void *cookiep); 114 115 static int 116 bhnd_nvram_tlv_probe(struct bhnd_nvram_io *io) 117 { 118 struct bhnd_nvram_tlv_env ident; 119 size_t nbytes; 120 int error; 121 122 nbytes = bhnd_nvram_io_getsize(io); 123 124 /* Handle what might be an empty TLV image */ 125 if (nbytes < sizeof(ident)) { 126 uint8_t tag; 127 128 /* Fetch just the first tag */ 129 error = bhnd_nvram_io_read(io, 0x0, &tag, sizeof(tag)); 130 if (error) 131 return (error); 132 133 /* This *could* be an empty TLV image, but all we're 134 * testing for here is a single 0x0 byte followed by EOF */ 135 if (tag == NVRAM_TLV_TYPE_END) 136 return (BHND_NVRAM_DATA_PROBE_MAYBE); 137 138 return (ENXIO); 139 } 140 141 /* Otherwise, look at the initial header for a valid TLV ENV tag, 142 * plus one byte of the entry data */ 143 error = bhnd_nvram_io_read(io, 0x0, &ident, 144 sizeof(ident) + sizeof(ident.envp[0])); 145 if (error) 146 return (error); 147 148 /* First entry should be a variable record (which we statically 149 * assert as being defined to use a single byte size field) */ 150 if (ident.hdr.tag != NVRAM_TLV_TYPE_ENV) 151 return (ENXIO); 152 153 _Static_assert(NVRAM_TLV_TYPE_ENV & NVRAM_TLV_TF_U8_LEN, 154 "TYPE_ENV is not a U8-sized field"); 155 156 /* The entry must be at least 3 characters ('x=\0') in length */ 157 if (ident.hdr.size < 3) 158 return (ENXIO); 159 160 /* The first character should be a valid key char (alpha) */ 161 if (!bhnd_nv_isalpha(ident.envp[0])) 162 return (ENXIO); 163 164 return (BHND_NVRAM_DATA_PROBE_DEFAULT); 165 } 166 167 static int 168 bhnd_nvram_tlv_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props, 169 bhnd_nvram_plist *options, void *outp, size_t *olen) 170 { 171 bhnd_nvram_prop *prop; 172 size_t limit, nbytes; 173 int error; 174 175 /* Determine output byte limit */ 176 if (outp != NULL) 177 limit = *olen; 178 else 179 limit = 0; 180 181 nbytes = 0; 182 183 /* Write all properties */ 184 prop = NULL; 185 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) { 186 struct bhnd_nvram_tlv_env env; 187 const char *name; 188 uint8_t *p; 189 size_t name_len, value_len; 190 size_t rec_size; 191 192 env.hdr.tag = NVRAM_TLV_TYPE_ENV; 193 env.hdr.size = sizeof(env.flags); 194 env.flags = 0x0; 195 196 /* Fetch name value and add to record length */ 197 name = bhnd_nvram_prop_name(prop); 198 name_len = strlen(name) + 1 /* '=' */; 199 200 if (UINT8_MAX - env.hdr.size < name_len) { 201 BHND_NV_LOG("%s name exceeds maximum TLV record " 202 "length\n", name); 203 return (EFTYPE); /* would overflow TLV size */ 204 } 205 206 env.hdr.size += name_len; 207 208 /* Add string value to record length */ 209 error = bhnd_nvram_prop_encode(prop, NULL, &value_len, 210 BHND_NVRAM_TYPE_STRING); 211 if (error) { 212 BHND_NV_LOG("error serializing %s to required type " 213 "%s: %d\n", name, 214 bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING), 215 error); 216 return (error); 217 } 218 219 if (UINT8_MAX - env.hdr.size < value_len) { 220 BHND_NV_LOG("%s value exceeds maximum TLV record " 221 "length\n", name); 222 return (EFTYPE); /* would overflow TLV size */ 223 } 224 225 env.hdr.size += value_len; 226 227 /* Calculate total record size */ 228 rec_size = sizeof(env.hdr) + env.hdr.size; 229 if (SIZE_MAX - nbytes < rec_size) 230 return (EFTYPE); /* would overflow size_t */ 231 232 /* Calculate our output pointer */ 233 if (nbytes > limit || limit - nbytes < rec_size) { 234 /* buffer is full; cannot write */ 235 p = NULL; 236 } else { 237 p = (uint8_t *)outp + nbytes; 238 } 239 240 /* Write to output */ 241 if (p != NULL) { 242 memcpy(p, &env, sizeof(env)); 243 p += sizeof(env); 244 245 memcpy(p, name, name_len - 1); 246 p[name_len - 1] = '='; 247 p += name_len; 248 249 error = bhnd_nvram_prop_encode(prop, p, &value_len, 250 BHND_NVRAM_TYPE_STRING); 251 if (error) { 252 BHND_NV_LOG("error serializing %s to required " 253 "type %s: %d\n", name, 254 bhnd_nvram_type_name( 255 BHND_NVRAM_TYPE_STRING), 256 error); 257 return (error); 258 } 259 } 260 261 nbytes += rec_size; 262 } 263 264 /* Write terminating END record */ 265 if (limit > nbytes) 266 *((uint8_t *)outp + nbytes) = NVRAM_TLV_TYPE_END; 267 268 if (nbytes == SIZE_MAX) 269 return (EFTYPE); /* would overflow size_t */ 270 nbytes++; 271 272 /* Provide required length */ 273 *olen = nbytes; 274 if (limit < *olen) { 275 if (outp == NULL) 276 return (0); 277 278 return (ENOMEM); 279 } 280 281 return (0); 282 } 283 284 /** 285 * Initialize @p tlv with the provided NVRAM TLV data mapped by @p src. 286 * 287 * @param tlv A newly allocated data instance. 288 */ 289 static int 290 bhnd_nvram_tlv_init(struct bhnd_nvram_tlv *tlv, struct bhnd_nvram_io *src) 291 { 292 struct bhnd_nvram_tlv_env *env; 293 size_t size; 294 size_t next; 295 int error; 296 297 BHND_NV_ASSERT(tlv->data == NULL, ("tlv data already initialized")); 298 299 /* Determine the actual size of the TLV source data */ 300 if ((error = bhnd_nvram_tlv_parse_size(src, &size))) 301 return (error); 302 303 /* Copy to our own internal buffer */ 304 if ((tlv->data = bhnd_nvram_iobuf_copy_range(src, 0x0, size)) == NULL) 305 return (ENOMEM); 306 307 /* Initialize our backing buffer */ 308 tlv->count = 0; 309 next = 0; 310 while ((env = bhnd_nvram_tlv_next_env(tlv, &next, NULL)) != NULL) { 311 size_t env_len; 312 size_t name_len; 313 314 /* TLV_ENV data must not be empty */ 315 env_len = NVRAM_TLV_ENVP_DATA_LEN(env); 316 if (env_len == 0) { 317 BHND_NV_LOG("cannot parse zero-length TLV_ENV record " 318 "data\n"); 319 return (EINVAL); 320 } 321 322 /* Parse the key=value string, and then replace the '=' 323 * delimiter with '\0' to allow us to provide direct 324 * name pointers from our backing buffer */ 325 error = bhnd_nvram_parse_env(env->envp, env_len, '=', NULL, 326 &name_len, NULL, NULL); 327 if (error) { 328 BHND_NV_LOG("error parsing TLV_ENV data: %d\n", error); 329 return (error); 330 } 331 332 /* Replace '=' with '\0' */ 333 *(env->envp + name_len) = '\0'; 334 335 /* Add to variable count */ 336 tlv->count++; 337 }; 338 339 return (0); 340 } 341 342 static int 343 bhnd_nvram_tlv_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) 344 { 345 346 struct bhnd_nvram_tlv *tlv; 347 int error; 348 349 /* Allocate and initialize the TLV data instance */ 350 tlv = (struct bhnd_nvram_tlv *)nv; 351 352 /* Parse the TLV input data and initialize our backing 353 * data representation */ 354 if ((error = bhnd_nvram_tlv_init(tlv, io))) { 355 bhnd_nvram_tlv_free(nv); 356 return (error); 357 } 358 359 return (0); 360 } 361 362 static void 363 bhnd_nvram_tlv_free(struct bhnd_nvram_data *nv) 364 { 365 struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv; 366 if (tlv->data != NULL) 367 bhnd_nvram_io_free(tlv->data); 368 } 369 370 size_t 371 bhnd_nvram_tlv_count(struct bhnd_nvram_data *nv) 372 { 373 struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv; 374 return (tlv->count); 375 } 376 377 378 static bhnd_nvram_plist * 379 bhnd_nvram_tlv_options(struct bhnd_nvram_data *nv) 380 { 381 return (NULL); 382 } 383 384 static uint32_t 385 bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv) 386 { 387 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); 388 } 389 390 static const char * 391 bhnd_nvram_tlv_next(struct bhnd_nvram_data *nv, void **cookiep) 392 { 393 struct bhnd_nvram_tlv *tlv; 394 struct bhnd_nvram_tlv_env *env; 395 size_t io_offset; 396 397 tlv = (struct bhnd_nvram_tlv *)nv; 398 399 /* Find next readable TLV record */ 400 if (*cookiep == NULL) { 401 /* Start search at offset 0x0 */ 402 io_offset = 0x0; 403 env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep); 404 } else { 405 /* Seek past the previous env record */ 406 io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep); 407 env = bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL); 408 if (env == NULL) 409 BHND_NV_PANIC("invalid cookiep; record missing"); 410 411 /* Advance to next env record, update the caller's cookiep */ 412 env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep); 413 } 414 415 /* Check for EOF */ 416 if (env == NULL) 417 return (NULL); 418 419 /* Return the NUL terminated name */ 420 return (env->envp); 421 } 422 423 static void * 424 bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name) 425 { 426 return (bhnd_nvram_data_generic_find(nv, name)); 427 } 428 429 static int 430 bhnd_nvram_tlv_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, 431 void *cookiep2) 432 { 433 if (cookiep1 < cookiep2) 434 return (-1); 435 436 if (cookiep1 > cookiep2) 437 return (1); 438 439 return (0); 440 } 441 442 static int 443 bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, 444 size_t *len, bhnd_nvram_type type) 445 { 446 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); 447 } 448 449 static int 450 bhnd_nvram_tlv_copy_val(struct bhnd_nvram_data *nv, void *cookiep, 451 bhnd_nvram_val **value) 452 { 453 return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value)); 454 } 455 456 static const void * 457 bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, 458 size_t *len, bhnd_nvram_type *type) 459 { 460 struct bhnd_nvram_tlv *tlv; 461 struct bhnd_nvram_tlv_env *env; 462 const char *val; 463 int error; 464 465 tlv = (struct bhnd_nvram_tlv *)nv; 466 467 /* Fetch pointer to the TLV_ENV record */ 468 if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL) 469 BHND_NV_PANIC("invalid cookiep: %p", cookiep); 470 471 /* Parse value pointer and length from key\0value data */ 472 error = bhnd_nvram_parse_env(env->envp, NVRAM_TLV_ENVP_DATA_LEN(env), 473 '\0', NULL, NULL, &val, len); 474 if (error) 475 BHND_NV_PANIC("unexpected error parsing '%s'", env->envp); 476 477 /* Type is always CSTR */ 478 *type = BHND_NVRAM_TYPE_STRING; 479 480 return (val); 481 } 482 483 static const char * 484 bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) 485 { 486 struct bhnd_nvram_tlv *tlv; 487 const struct bhnd_nvram_tlv_env *env; 488 489 tlv = (struct bhnd_nvram_tlv *)nv; 490 491 /* Fetch pointer to the TLV_ENV record */ 492 if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL) 493 BHND_NV_PANIC("invalid cookiep: %p", cookiep); 494 495 /* Return name pointer */ 496 return (&env->envp[0]); 497 } 498 499 static int 500 bhnd_nvram_tlv_filter_setvar(struct bhnd_nvram_data *nv, const char *name, 501 bhnd_nvram_val *value, bhnd_nvram_val **result) 502 { 503 bhnd_nvram_val *str; 504 const char *inp; 505 bhnd_nvram_type itype; 506 size_t ilen; 507 size_t name_len, tlv_nremain; 508 int error; 509 510 tlv_nremain = NVRAM_TLV_ENVP_DATA_MAX_LEN; 511 512 /* Name (trimmed of any path prefix) must be valid */ 513 if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name))) 514 return (EINVAL); 515 516 /* 'name=' must fit within the maximum TLV_ENV record length */ 517 name_len = strlen(name) + 1; /* '=' */ 518 if (tlv_nremain < name_len) { 519 BHND_NV_LOG("'%s=' exceeds maximum TLV_ENV record length\n", 520 name); 521 return (EINVAL); 522 } 523 tlv_nremain -= name_len; 524 525 /* Convert value to a (bcm-formatted) string */ 526 error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt, 527 value, BHND_NVRAM_VAL_DYNAMIC); 528 if (error) 529 return (error); 530 531 /* The string value must fit within remaining TLV_ENV record length */ 532 inp = bhnd_nvram_val_bytes(str, &ilen, &itype); 533 if (tlv_nremain < ilen) { 534 BHND_NV_LOG("'%.*s\\0' exceeds maximum TLV_ENV record length\n", 535 BHND_NV_PRINT_WIDTH(ilen), inp); 536 537 bhnd_nvram_val_release(str); 538 return (EINVAL); 539 } 540 tlv_nremain -= name_len; 541 542 /* Success. Transfer result ownership to the caller. */ 543 *result = str; 544 return (0); 545 } 546 547 static int 548 bhnd_nvram_tlv_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) 549 { 550 /* We permit deletion of any variable */ 551 return (0); 552 } 553 554 /** 555 * Iterate over the records starting at @p next, returning the parsed 556 * record's @p tag, @p size, and @p offset. 557 * 558 * @param io The I/O context to parse. 559 * @param[in,out] next The next offset to be parsed, or 0x0 560 * to begin parsing. Upon successful 561 * return, will be set to the offset of the 562 * next record (or EOF, if 563 * NVRAM_TLV_TYPE_END was parsed). 564 * @param[out] offset The record's value offset. 565 * @param[out] tag The record's tag. 566 * 567 * @retval 0 success 568 * @retval EINVAL if parsing @p io as TLV fails. 569 * @retval non-zero if reading @p io otherwise fails, a regular unix error 570 * code will be returned. 571 */ 572 static int 573 bhnd_nvram_tlv_next_record(struct bhnd_nvram_io *io, size_t *next, size_t 574 *offset, uint8_t *tag) 575 { 576 size_t io_offset, io_size; 577 uint16_t parsed_len; 578 uint8_t len_hdr[2]; 579 int error; 580 581 io_offset = *next; 582 io_size = bhnd_nvram_io_getsize(io); 583 584 /* Save the record offset */ 585 if (offset != NULL) 586 *offset = io_offset; 587 588 /* Fetch initial tag */ 589 error = bhnd_nvram_io_read(io, io_offset, tag, sizeof(*tag)); 590 if (error) 591 return (error); 592 io_offset++; 593 594 /* EOF */ 595 if (*tag == NVRAM_TLV_TYPE_END) { 596 *next = io_offset; 597 return (0); 598 } 599 600 /* Read length field */ 601 if (*tag & NVRAM_TLV_TF_U8_LEN) { 602 error = bhnd_nvram_io_read(io, io_offset, &len_hdr, 603 sizeof(len_hdr[0])); 604 if (error) { 605 BHND_NV_LOG("error reading TLV record size: %d\n", 606 error); 607 return (error); 608 } 609 610 parsed_len = len_hdr[0]; 611 io_offset++; 612 } else { 613 error = bhnd_nvram_io_read(io, io_offset, &len_hdr, 614 sizeof(len_hdr)); 615 if (error) { 616 BHND_NV_LOG("error reading 16-bit TLV record " 617 "size: %d\n", error); 618 return (error); 619 } 620 621 parsed_len = (len_hdr[0] << 8) | len_hdr[1]; 622 io_offset += 2; 623 } 624 625 /* Advance to next record */ 626 if (parsed_len > io_size || io_size - parsed_len < io_offset) { 627 /* Hit early EOF */ 628 BHND_NV_LOG("TLV record length %hu truncated by input " 629 "size of %zu\n", parsed_len, io_size); 630 return (EINVAL); 631 } 632 633 *next = io_offset + parsed_len; 634 635 /* Valid record found */ 636 return (0); 637 } 638 639 /** 640 * Parse the TLV data in @p io to determine the total size of the TLV 641 * data mapped by @p io (which may be less than the size of @p io). 642 */ 643 static int 644 bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io *io, size_t *size) 645 { 646 size_t next; 647 uint8_t tag; 648 int error; 649 650 /* We have to perform a minimal parse to determine the actual length */ 651 next = 0x0; 652 *size = 0x0; 653 654 /* Iterate over the input until we hit END tag or the read fails */ 655 do { 656 error = bhnd_nvram_tlv_next_record(io, &next, NULL, &tag); 657 if (error) 658 return (error); 659 } while (tag != NVRAM_TLV_TYPE_END); 660 661 /* Offset should now point to EOF */ 662 BHND_NV_ASSERT(next <= bhnd_nvram_io_getsize(io), 663 ("parse returned invalid EOF offset")); 664 665 *size = next; 666 return (0); 667 } 668 669 /** 670 * Iterate over the records in @p tlv, returning a pointer to the next 671 * NVRAM_TLV_TYPE_ENV record, or NULL if EOF is reached. 672 * 673 * @param tlv The TLV instance. 674 * @param[in,out] next The next offset to be parsed, or 0x0 675 * to begin parsing. Upon successful 676 * return, will be set to the offset of the 677 * next record. 678 */ 679 static struct bhnd_nvram_tlv_env * 680 bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv *tlv, size_t *next, 681 void **cookiep) 682 { 683 uint8_t tag; 684 int error; 685 686 /* Find the next TLV_ENV record, starting at @p next */ 687 do { 688 void *c; 689 size_t offset; 690 691 /* Fetch the next TLV record */ 692 error = bhnd_nvram_tlv_next_record(tlv->data, next, &offset, 693 &tag); 694 if (error) { 695 BHND_NV_LOG("unexpected error in next_record(): %d\n", 696 error); 697 return (NULL); 698 } 699 700 /* Only interested in ENV records */ 701 if (tag != NVRAM_TLV_TYPE_ENV) 702 continue; 703 704 /* Map and return TLV_ENV record pointer */ 705 c = bhnd_nvram_tlv_to_cookie(tlv, offset); 706 707 /* Provide the cookiep value for the returned record */ 708 if (cookiep != NULL) 709 *cookiep = c; 710 711 return (bhnd_nvram_tlv_get_env(tlv, c)); 712 } while (tag != NVRAM_TLV_TYPE_END); 713 714 /* No remaining ENV records */ 715 return (NULL); 716 } 717 718 /** 719 * Return a pointer to the TLV_ENV record for @p cookiep, or NULL 720 * if none vailable. 721 */ 722 static struct bhnd_nvram_tlv_env * 723 bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep) 724 { 725 struct bhnd_nvram_tlv_env *env; 726 void *ptr; 727 size_t navail; 728 size_t io_offset, io_size; 729 int error; 730 731 io_size = bhnd_nvram_io_getsize(tlv->data); 732 io_offset = bhnd_nvram_tlv_to_offset(tlv, cookiep); 733 734 /* At EOF? */ 735 if (io_offset == io_size) 736 return (NULL); 737 738 /* Fetch non-const pointer to the record entry */ 739 error = bhnd_nvram_io_write_ptr(tlv->data, io_offset, &ptr, 740 sizeof(env->hdr), &navail); 741 if (error) { 742 /* Should never occur with a valid cookiep */ 743 BHND_NV_LOG("error mapping record for cookiep: %d\n", error); 744 return (NULL); 745 } 746 747 /* Validate the record pointer */ 748 env = ptr; 749 if (env->hdr.tag != NVRAM_TLV_TYPE_ENV) { 750 /* Should never occur with a valid cookiep */ 751 BHND_NV_LOG("non-ENV record mapped for %p\n", cookiep); 752 return (NULL); 753 } 754 755 /* Is the required variable name data is mapped? */ 756 if (navail < sizeof(struct bhnd_nvram_tlv_env_hdr) + env->hdr.size || 757 env->hdr.size == sizeof(env->flags)) 758 { 759 /* Should never occur with a valid cookiep */ 760 BHND_NV_LOG("TLV_ENV variable data not mapped for %p\n", 761 cookiep); 762 return (NULL); 763 } 764 765 return (env); 766 } 767 768 /** 769 * Return a cookiep for the given I/O offset. 770 */ 771 static void * 772 bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset) 773 { 774 const void *ptr; 775 int error; 776 777 BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data), 778 ("io_offset %zu out-of-range", io_offset)); 779 BHND_NV_ASSERT(io_offset < UINTPTR_MAX, 780 ("io_offset %#zx exceeds UINTPTR_MAX", io_offset)); 781 782 error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_offset, NULL); 783 if (error) 784 BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error); 785 786 ptr = (const uint8_t *)ptr + io_offset; 787 return (__DECONST(void *, ptr)); 788 } 789 790 /* Convert a cookiep back to an I/O offset */ 791 static size_t 792 bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep) 793 { 794 const void *ptr; 795 intptr_t offset; 796 size_t io_size; 797 int error; 798 799 BHND_NV_ASSERT(cookiep != NULL, ("null cookiep")); 800 801 io_size = bhnd_nvram_io_getsize(tlv->data); 802 803 error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_size, NULL); 804 if (error) 805 BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error); 806 807 offset = (const uint8_t *)cookiep - (const uint8_t *)ptr; 808 BHND_NV_ASSERT(offset >= 0, ("invalid cookiep")); 809 BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)")); 810 BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)")); 811 812 return ((size_t)offset); 813 } 814