1 /*- 2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/hash.h> 32 #include <sys/limits.h> 33 #include <sys/queue.h> 34 35 #ifdef _KERNEL 36 37 #include <sys/ctype.h> 38 #include <sys/systm.h> 39 40 #include <machine/_inttypes.h> 41 42 #else /* !_KERNEL */ 43 44 #include <ctype.h> 45 #include <errno.h> 46 #include <inttypes.h> 47 #include <stdbool.h> 48 #include <stdio.h> 49 #include <stdint.h> 50 #include <stdlib.h> 51 #include <string.h> 52 53 #endif /* _KERNEL */ 54 55 #include "bhnd_nvram_private.h" 56 #include "bhnd_nvram_datavar.h" 57 58 #include "bhnd_nvram_storevar.h" 59 60 /* 61 * BHND NVRAM Store 62 * 63 * Manages in-memory and persistent representations of NVRAM data. 64 */ 65 66 static int bhnd_nvstore_parse_data( 67 struct bhnd_nvram_store *sc); 68 69 static int bhnd_nvstore_parse_path_entries( 70 struct bhnd_nvram_store *sc); 71 72 static int bhnd_nvram_store_export_child( 73 struct bhnd_nvram_store *sc, 74 bhnd_nvstore_path *top, 75 bhnd_nvstore_path *child, 76 bhnd_nvram_plist *plist, 77 uint32_t flags); 78 79 static int bhnd_nvstore_export_merge( 80 struct bhnd_nvram_store *sc, 81 bhnd_nvstore_path *path, 82 bhnd_nvram_plist *merged, 83 uint32_t flags); 84 85 static int bhnd_nvstore_export_devpath_alias( 86 struct bhnd_nvram_store *sc, 87 bhnd_nvstore_path *path, 88 const char *devpath, 89 bhnd_nvram_plist *plist, 90 u_long *alias_val); 91 92 /** 93 * Allocate and initialize a new NVRAM data store instance. 94 * 95 * The caller is responsible for deallocating the instance via 96 * bhnd_nvram_store_free(). 97 * 98 * @param[out] store On success, a pointer to the newly allocated NVRAM data 99 * instance. 100 * @param data The NVRAM data to be managed by the returned NVRAM data store 101 * instance. 102 * 103 * @retval 0 success 104 * @retval non-zero if an error occurs during allocation or initialization, a 105 * regular unix error code will be returned. 106 */ 107 int 108 bhnd_nvram_store_new(struct bhnd_nvram_store **store, 109 struct bhnd_nvram_data *data) 110 { 111 struct bhnd_nvram_store *sc; 112 int error; 113 114 /* Allocate new instance */ 115 sc = bhnd_nv_calloc(1, sizeof(*sc)); 116 if (sc == NULL) 117 return (ENOMEM); 118 119 BHND_NVSTORE_LOCK_INIT(sc); 120 BHND_NVSTORE_LOCK(sc); 121 122 /* Initialize path hash table */ 123 sc->num_paths = 0; 124 for (size_t i = 0; i < nitems(sc->paths); i++) 125 LIST_INIT(&sc->paths[i]); 126 127 /* Initialize alias hash table */ 128 sc->num_aliases = 0; 129 for (size_t i = 0; i < nitems(sc->aliases); i++) 130 LIST_INIT(&sc->aliases[i]); 131 132 /* Retain the NVRAM data */ 133 sc->data = bhnd_nvram_data_retain(data); 134 sc->data_caps = bhnd_nvram_data_caps(data); 135 sc->data_opts = bhnd_nvram_data_options(data); 136 if (sc->data_opts != NULL) { 137 bhnd_nvram_plist_retain(sc->data_opts); 138 } else { 139 sc->data_opts = bhnd_nvram_plist_new(); 140 if (sc->data_opts == NULL) { 141 error = ENOMEM; 142 goto cleanup; 143 } 144 } 145 146 /* Register required root path */ 147 error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH, 148 BHND_NVSTORE_ROOT_PATH_LEN); 149 if (error) 150 goto cleanup; 151 152 sc->root_path = bhnd_nvstore_get_path(sc, BHND_NVSTORE_ROOT_PATH, 153 BHND_NVSTORE_ROOT_PATH_LEN); 154 BHND_NV_ASSERT(sc->root_path, ("missing root path")); 155 156 /* Parse all variables vended by our backing NVRAM data instance, 157 * generating all path entries, alias entries, and variable indexes */ 158 if ((error = bhnd_nvstore_parse_data(sc))) 159 goto cleanup; 160 161 *store = sc; 162 163 BHND_NVSTORE_UNLOCK(sc); 164 return (0); 165 166 cleanup: 167 BHND_NVSTORE_UNLOCK(sc); 168 bhnd_nvram_store_free(sc); 169 return (error); 170 } 171 172 /** 173 * Allocate and initialize a new NVRAM data store instance, parsing the 174 * NVRAM data from @p io. 175 * 176 * The caller is responsible for deallocating the instance via 177 * bhnd_nvram_store_free(). 178 * 179 * The NVRAM data mapped by @p io will be copied, and @p io may be safely 180 * deallocated after bhnd_nvram_store_new() returns. 181 * 182 * @param[out] store On success, a pointer to the newly allocated NVRAM data 183 * instance. 184 * @param io An I/O context mapping the NVRAM data to be copied and parsed. 185 * @param cls The NVRAM data class to be used when parsing @p io, or NULL 186 * to perform runtime identification of the appropriate data class. 187 * 188 * @retval 0 success 189 * @retval non-zero if an error occurs during allocation or initialization, a 190 * regular unix error code will be returned. 191 */ 192 int 193 bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store, 194 struct bhnd_nvram_io *io, bhnd_nvram_data_class *cls) 195 { 196 struct bhnd_nvram_data *data; 197 int error; 198 199 /* Try to parse the data */ 200 if ((error = bhnd_nvram_data_new(cls, &data, io))) 201 return (error); 202 203 /* Try to create our new store instance */ 204 error = bhnd_nvram_store_new(store, data); 205 bhnd_nvram_data_release(data); 206 207 return (error); 208 } 209 210 /** 211 * Free an NVRAM store instance, releasing all associated resources. 212 * 213 * @param sc A store instance previously allocated via 214 * bhnd_nvram_store_new(). 215 */ 216 void 217 bhnd_nvram_store_free(struct bhnd_nvram_store *sc) 218 { 219 220 /* Clean up alias hash table */ 221 for (size_t i = 0; i < nitems(sc->aliases); i++) { 222 bhnd_nvstore_alias *alias, *anext; 223 LIST_FOREACH_SAFE(alias, &sc->aliases[i], na_link, anext) 224 bhnd_nv_free(alias); 225 } 226 227 /* Clean up path hash table */ 228 for (size_t i = 0; i < nitems(sc->paths); i++) { 229 bhnd_nvstore_path *path, *pnext; 230 LIST_FOREACH_SAFE(path, &sc->paths[i], np_link, pnext) 231 bhnd_nvstore_path_free(path); 232 } 233 234 if (sc->data != NULL) 235 bhnd_nvram_data_release(sc->data); 236 237 if (sc->data_opts != NULL) 238 bhnd_nvram_plist_release(sc->data_opts); 239 240 BHND_NVSTORE_LOCK_DESTROY(sc); 241 bhnd_nv_free(sc); 242 } 243 244 /** 245 * Parse all variables vended by our backing NVRAM data instance, 246 * generating all path entries, alias entries, and variable indexes. 247 * 248 * @param sc The NVRAM store instance to be initialized with 249 * paths, aliases, and data parsed from its backing 250 * data. 251 * 252 * @retval 0 success 253 * @retval non-zero if an error occurs during parsing, a regular unix error 254 * code will be returned. 255 */ 256 static int 257 bhnd_nvstore_parse_data(struct bhnd_nvram_store *sc) 258 { 259 const char *name; 260 void *cookiep; 261 int error; 262 263 /* Parse and register all device paths and path aliases. This enables 264 * resolution of _forward_ references to device paths aliases when 265 * scanning variable entries below */ 266 if ((error = bhnd_nvstore_parse_path_entries(sc))) 267 return (error); 268 269 /* Calculate the per-path variable counts, and report dangling alias 270 * references as an error. */ 271 cookiep = NULL; 272 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { 273 bhnd_nvstore_path *path; 274 bhnd_nvstore_name_info info; 275 276 /* Parse the name info */ 277 error = bhnd_nvstore_parse_name_info(name, 278 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info); 279 if (error) 280 return (error); 281 282 switch (info.type) { 283 case BHND_NVSTORE_VAR: 284 /* Fetch referenced path */ 285 path = bhnd_nvstore_var_get_path(sc, &info); 286 if (path == NULL) { 287 BHND_NV_LOG("variable '%s' has dangling " 288 "path reference\n", name); 289 return (EFTYPE); 290 } 291 292 /* Increment path variable count */ 293 if (path->num_vars == SIZE_MAX) { 294 BHND_NV_LOG("more than SIZE_MAX variables in " 295 "path %s\n", path->path_str); 296 return (EFTYPE); 297 } 298 path->num_vars++; 299 break; 300 301 case BHND_NVSTORE_ALIAS_DECL: 302 /* Skip -- path alias already parsed and recorded */ 303 break; 304 } 305 } 306 307 /* If the backing NVRAM data instance vends only a single root ("/") 308 * path, we may be able to skip generating an index for the root 309 * path */ 310 if (sc->num_paths == 1) { 311 bhnd_nvstore_path *path; 312 313 /* If the backing instance provides its own name-based lookup 314 * indexing, we can skip generating a duplicate here */ 315 if (sc->data_caps & BHND_NVRAM_DATA_CAP_INDEXED) 316 return (0); 317 318 /* If the sole root path contains fewer variables than the 319 * minimum indexing threshhold, we do not need to generate an 320 * index */ 321 path = bhnd_nvstore_get_root_path(sc); 322 if (path->num_vars < BHND_NV_IDX_VAR_THRESHOLD) 323 return (0); 324 } 325 326 /* Allocate per-path index instances */ 327 for (size_t i = 0; i < nitems(sc->paths); i++) { 328 bhnd_nvstore_path *path; 329 330 LIST_FOREACH(path, &sc->paths[i], np_link) { 331 path->index = bhnd_nvstore_index_new(path->num_vars); 332 if (path->index == NULL) 333 return (ENOMEM); 334 } 335 } 336 337 /* Populate per-path indexes */ 338 cookiep = NULL; 339 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { 340 bhnd_nvstore_name_info info; 341 bhnd_nvstore_path *path; 342 343 /* Parse the name info */ 344 error = bhnd_nvstore_parse_name_info(name, 345 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info); 346 if (error) 347 return (error); 348 349 switch (info.type) { 350 case BHND_NVSTORE_VAR: 351 /* Fetch referenced path */ 352 path = bhnd_nvstore_var_get_path(sc, &info); 353 BHND_NV_ASSERT(path != NULL, 354 ("dangling path reference")); 355 356 /* Append to index */ 357 error = bhnd_nvstore_index_append(sc, path->index, 358 cookiep); 359 if (error) 360 return (error); 361 break; 362 363 case BHND_NVSTORE_ALIAS_DECL: 364 /* Skip */ 365 break; 366 } 367 } 368 369 /* Prepare indexes for querying */ 370 for (size_t i = 0; i < nitems(sc->paths); i++) { 371 bhnd_nvstore_path *path; 372 373 LIST_FOREACH(path, &sc->paths[i], np_link) { 374 error = bhnd_nvstore_index_prepare(sc, path->index); 375 if (error) 376 return (error); 377 } 378 } 379 380 return (0); 381 } 382 383 /** 384 * Parse and register path and path alias entries for all declarations found in 385 * the NVRAM data backing @p nvram. 386 * 387 * @param sc The NVRAM store instance. 388 * 389 * @retval 0 success 390 * @retval non-zero If parsing fails, a regular unix error code will be 391 * returned. 392 */ 393 static int 394 bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc) 395 { 396 const char *name; 397 void *cookiep; 398 int error; 399 400 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 401 402 /* Skip path registration if the data source does not support device 403 * paths. */ 404 if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) { 405 BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path")); 406 return (0); 407 } 408 409 /* Otherwise, parse and register all paths and path aliases */ 410 cookiep = NULL; 411 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { 412 bhnd_nvstore_name_info info; 413 414 /* Parse the name info */ 415 error = bhnd_nvstore_parse_name_info(name, 416 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info); 417 if (error) 418 return (error); 419 420 /* Register the path */ 421 error = bhnd_nvstore_var_register_path(sc, &info, cookiep); 422 if (error) { 423 BHND_NV_LOG("failed to register path for %s: %d\n", 424 name, error); 425 return (error); 426 } 427 } 428 429 return (0); 430 } 431 432 /** 433 * Merge exported per-path variables (uncommitted, committed, or both) into 434 * the empty @p merged property list. 435 * 436 * @param sc The NVRAM store instance. 437 * @param path The NVRAM path to be exported. 438 * @param merged The property list to populate with the merged results. 439 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. 440 * 441 * @retval 0 success 442 * @retval ENOMEM If allocation fails. 443 * @retval non-zero If merging the variables defined in @p path otherwise 444 * fails, a regular unix error code will be returned. 445 */ 446 static int 447 bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc, 448 bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags) 449 { 450 void *cookiep, *idxp; 451 int error; 452 453 /* Populate merged list with all pending variables */ 454 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) { 455 bhnd_nvram_prop *prop; 456 457 prop = NULL; 458 while ((prop = bhnd_nvram_plist_next(path->pending, prop))) { 459 /* Skip variables marked for deletion */ 460 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) { 461 if (bhnd_nvram_prop_is_null(prop)) 462 continue; 463 } 464 465 /* Append to merged list */ 466 error = bhnd_nvram_plist_append(merged, prop); 467 if (error) 468 return (error); 469 } 470 } 471 472 /* Skip merging committed variables? */ 473 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED)) 474 return (0); 475 476 /* Merge in the committed NVRAM variables */ 477 idxp = NULL; 478 while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) { 479 const char *name; 480 bhnd_nvram_val *val; 481 482 /* Fetch the variable name */ 483 name = bhnd_nvram_data_getvar_name(sc->data, cookiep); 484 485 /* Trim device path prefix */ 486 if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) 487 name = bhnd_nvram_trim_path_name(name); 488 489 /* Skip if already defined in pending updates */ 490 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) { 491 if (bhnd_nvram_plist_contains(path->pending, name)) 492 continue; 493 } 494 495 /* Skip if higher precedence value was already defined. This 496 * may occur if the underlying data store contains duplicate 497 * keys; iteration will always return the definition with 498 * the highest precedence first */ 499 if (bhnd_nvram_plist_contains(merged, name)) 500 continue; 501 502 /* Fetch the variable's value representation */ 503 if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val))) 504 return (error); 505 506 /* Add to path variable list */ 507 error = bhnd_nvram_plist_append_val(merged, name, val); 508 bhnd_nvram_val_release(val); 509 if (error) 510 return (error); 511 } 512 513 return (0); 514 } 515 516 /** 517 * Find a free alias value for @p path, and append the devpathXX alias 518 * declaration to @p plist. 519 * 520 * @param sc The NVRAM store instance. 521 * @param path The NVRAM path for which a devpath alias 522 * variable should be produced. 523 * @param devpath The devpathXX path value for @p path. 524 * @param plist The property list to which @p path's devpath 525 * variable will be appended. 526 * @param[out] alias_val On success, will be set to the alias value 527 * allocated for @p path. 528 * 529 * @retval 0 success 530 * @retval ENOMEM If allocation fails. 531 * @retval non-zero If merging the variables defined in @p path otherwise 532 * fails, a regular unix error code will be returned. 533 */ 534 static int 535 bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc, 536 bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist, 537 u_long *alias_val) 538 { 539 bhnd_nvstore_alias *alias; 540 char *pathvar; 541 int error; 542 543 *alias_val = 0; 544 545 /* Prefer alias value already reserved for this path. */ 546 alias = bhnd_nvstore_find_alias(sc, path->path_str); 547 if (alias != NULL) { 548 *alias_val = alias->alias; 549 550 /* Allocate devpathXX variable name */ 551 bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val); 552 if (pathvar == NULL) 553 return (ENOMEM); 554 555 /* Append alias variable to property list */ 556 error = bhnd_nvram_plist_append_string(plist, pathvar, devpath); 557 558 BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use", 559 * alias_val, path->path_str)); 560 561 bhnd_nv_free(pathvar); 562 return (error); 563 } 564 565 /* Find the next free devpathXX alias entry */ 566 while (1) { 567 /* Skip existing reserved alias values */ 568 while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) { 569 if (*alias_val == ULONG_MAX) 570 return (ENOMEM); 571 572 (*alias_val)++; 573 } 574 575 /* Allocate devpathXX variable name */ 576 bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val); 577 if (pathvar == NULL) 578 return (ENOMEM); 579 580 /* If not in-use, we can terminate the search */ 581 if (!bhnd_nvram_plist_contains(plist, pathvar)) 582 break; 583 584 /* Keep searching */ 585 bhnd_nv_free(pathvar); 586 587 if (*alias_val == ULONG_MAX) 588 return (ENOMEM); 589 590 (*alias_val)++; 591 } 592 593 /* Append alias variable to property list */ 594 error = bhnd_nvram_plist_append_string(plist, pathvar, devpath); 595 596 bhnd_nv_free(pathvar); 597 return (error); 598 } 599 600 /** 601 * Export a single @p child path's properties, appending the result to @p plist. 602 * 603 * @param sc The NVRAM store instance. 604 * @param top The root NVRAM path being exported. 605 * @param child The NVRAM path to be exported. 606 * @param plist The property list to which @p child's exported 607 * properties should be appended. 608 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. 609 * 610 * @retval 0 success 611 * @retval ENOMEM If allocation fails. 612 * @retval non-zero If merging the variables defined in @p path otherwise 613 * fails, a regular unix error code will be returned. 614 */ 615 static int 616 bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc, 617 bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist, 618 uint32_t flags) 619 { 620 bhnd_nvram_plist *path_vars; 621 bhnd_nvram_prop *prop; 622 const char *relpath; 623 char *prefix, *namebuf; 624 size_t prefix_len, relpath_len; 625 size_t namebuf_size; 626 bool emit_compact_devpath; 627 int error; 628 629 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 630 631 prefix = NULL; 632 path_vars = NULL; 633 namebuf = NULL; 634 635 /* Determine the path relative to the top-level path */ 636 relpath = bhnd_nvstore_parse_relpath(top->path_str, child->path_str); 637 if (relpath == NULL) { 638 /* Skip -- not a child of the root path */ 639 return (0); 640 } 641 relpath_len = strlen(relpath); 642 643 /* Skip sub-path if export of children was not requested, */ 644 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0) 645 return (0); 646 647 /* Collect all variables to be included in the export */ 648 if ((path_vars = bhnd_nvram_plist_new()) == NULL) 649 return (ENOMEM); 650 651 if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) { 652 bhnd_nvram_plist_release(path_vars); 653 return (error); 654 } 655 656 /* Skip if no children are to be exported */ 657 if (bhnd_nvram_plist_count(path_vars) == 0) { 658 bhnd_nvram_plist_release(path_vars); 659 return (0); 660 } 661 662 /* Determine appropriate device path encoding */ 663 emit_compact_devpath = false; 664 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) { 665 /* Re-encode as compact (if non-empty path) */ 666 if (relpath_len > 0) 667 emit_compact_devpath = true; 668 } else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) { 669 /* Re-encode with fully expanded device path */ 670 emit_compact_devpath = false; 671 } else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) { 672 /* Preserve existing encoding of this path */ 673 if (bhnd_nvstore_find_alias(sc, child->path_str) != NULL) 674 emit_compact_devpath = true; 675 } else { 676 BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags); 677 error = EINVAL; 678 goto finished; 679 } 680 681 /* Allocate variable device path prefix to use for all property names, 682 * and if using compact encoding, emit the devpathXX= variable */ 683 prefix = NULL; 684 prefix_len = 0; 685 if (emit_compact_devpath) { 686 u_long alias_val; 687 int len; 688 689 /* Reserve an alias value and append the devpathXX= variable to 690 * the property list */ 691 error = bhnd_nvstore_export_devpath_alias(sc, child, relpath, 692 plist, &alias_val); 693 if (error) 694 goto finished; 695 696 /* Allocate variable name prefix */ 697 len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val); 698 if (prefix == NULL) { 699 error = ENOMEM; 700 goto finished; 701 } 702 703 prefix_len = len; 704 } else if (relpath_len > 0) { 705 int len; 706 707 /* Allocate the variable name prefix, appending '/' to the 708 * relative path */ 709 len = bhnd_nv_asprintf(&prefix, "%s/", relpath); 710 if (prefix == NULL) { 711 error = ENOMEM; 712 goto finished; 713 } 714 715 prefix_len = len; 716 } 717 718 /* If prefixing of variable names is required, allocate a name 719 * formatting buffer */ 720 namebuf_size = 0; 721 if (prefix != NULL) { 722 size_t maxlen; 723 724 /* Find the maximum name length */ 725 maxlen = 0; 726 prop = NULL; 727 while ((prop = bhnd_nvram_plist_next(path_vars, prop))) { 728 const char *name; 729 730 name = bhnd_nvram_prop_name(prop); 731 maxlen = bhnd_nv_ummax(strlen(name), maxlen); 732 } 733 734 /* Allocate name buffer (path-prefix + name + '\0') */ 735 namebuf_size = prefix_len + maxlen + 1; 736 namebuf = bhnd_nv_malloc(namebuf_size); 737 if (namebuf == NULL) { 738 error = ENOMEM; 739 goto finished; 740 } 741 } 742 743 /* Append all path variables to the export plist, prepending the 744 * device-path prefix to the variable names, if required */ 745 prop = NULL; 746 while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) { 747 const char *name; 748 749 /* Prepend device prefix to the variable name */ 750 name = bhnd_nvram_prop_name(prop); 751 if (prefix != NULL) { 752 int len; 753 754 /* 755 * Write prefixed variable name to our name buffer. 756 * 757 * We precalcuate the size when scanning all names 758 * above, so this should always succeed. 759 */ 760 len = snprintf(namebuf, namebuf_size, "%s%s", prefix, 761 name); 762 if (len < 0 || (size_t)len >= namebuf_size) 763 BHND_NV_PANIC("invalid max_name_len"); 764 765 name = namebuf; 766 } 767 768 /* Add property to export plist */ 769 error = bhnd_nvram_plist_append_val(plist, name, 770 bhnd_nvram_prop_val(prop)); 771 if (error) 772 goto finished; 773 } 774 775 /* Success */ 776 error = 0; 777 778 finished: 779 if (prefix != NULL) 780 bhnd_nv_free(prefix); 781 782 if (namebuf != NULL) 783 bhnd_nv_free(namebuf); 784 785 if (path_vars != NULL) 786 bhnd_nvram_plist_release(path_vars); 787 788 return (error); 789 } 790 791 /** 792 * Export a flat, ordered NVRAM property list representation of all NVRAM 793 * properties at @p path. 794 * 795 * @param sc The NVRAM store instance. 796 * @param path The NVRAM path to export, or NULL to select the root 797 * path. 798 * @param[out] cls On success, will be set to the backing data class 799 * of @p sc. If the data class is are not desired, 800 * a NULL pointer may be provided. 801 * @param[out] props On success, will be set to a caller-owned property 802 * list containing the exported properties. The caller is 803 * responsible for releasing this value via 804 * bhnd_nvram_plist_release(). 805 * @param[out] options On success, will be set to a caller-owned property 806 * list containing the current NVRAM serialization options 807 * for @p sc. The caller is responsible for releasing this 808 * value via bhnd_nvram_plist_release(). 809 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. 810 * 811 * @retval 0 success 812 * @retval EINVAL If @p flags is invalid. 813 * @retval ENOENT The requested path was not found. 814 * @retval ENOMEM If allocation fails. 815 * @retval non-zero If export of @p path otherwise fails, a regular unix 816 * error code will be returned. 817 */ 818 int 819 bhnd_nvram_store_export(struct bhnd_nvram_store *sc, const char *path, 820 bhnd_nvram_data_class **cls, bhnd_nvram_plist **props, 821 bhnd_nvram_plist **options, uint32_t flags) 822 { 823 bhnd_nvram_plist *unordered; 824 bhnd_nvstore_path *top; 825 bhnd_nvram_prop *prop; 826 const char *name; 827 void *cookiep; 828 size_t num_dpath_flags; 829 int error; 830 831 *props = NULL; 832 unordered = NULL; 833 num_dpath_flags = 0; 834 if (options != NULL) 835 *options = NULL; 836 837 /* Default to exporting root path */ 838 if (path == NULL) 839 path = BHND_NVSTORE_ROOT_PATH; 840 841 /* Default to exporting all properties */ 842 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) && 843 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) 844 { 845 flags |= BHND_NVSTORE_EXPORT_ALL_VARS; 846 } 847 848 /* Default to preserving the current device path encoding */ 849 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS) && 850 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) 851 { 852 flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS; 853 } 854 855 /* Exactly one device path encoding flag must be set */ 856 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) 857 num_dpath_flags++; 858 859 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) 860 num_dpath_flags++; 861 862 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) 863 num_dpath_flags++; 864 865 if (num_dpath_flags != 1) 866 return (EINVAL); 867 868 /* If EXPORT_DELETED is set, EXPORT_UNCOMMITTED must be set too */ 869 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED) && 870 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) 871 { 872 return (EINVAL); 873 } 874 875 /* Lock internal state before querying paths/properties */ 876 BHND_NVSTORE_LOCK(sc); 877 878 /* Fetch referenced path */ 879 top = bhnd_nvstore_get_path(sc, path, strlen(path)); 880 if (top == NULL) { 881 error = ENOENT; 882 goto failed; 883 } 884 885 /* Allocate new, empty property list */ 886 if ((unordered = bhnd_nvram_plist_new()) == NULL) { 887 error = ENOMEM; 888 goto failed; 889 } 890 891 /* Export the top-level path first */ 892 error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags); 893 if (error) 894 goto failed; 895 896 /* Attempt to export any children of the root path */ 897 for (size_t i = 0; i < nitems(sc->paths); i++) { 898 bhnd_nvstore_path *child; 899 900 LIST_FOREACH(child, &sc->paths[i], np_link) { 901 /* Top-level path was already exported */ 902 if (child == top) 903 continue; 904 905 error = bhnd_nvram_store_export_child(sc, top, 906 child, unordered, flags); 907 if (error) 908 goto failed; 909 } 910 } 911 912 /* If requested, provide the current class and serialization options */ 913 if (cls != NULL) 914 *cls = bhnd_nvram_data_get_class(sc->data); 915 916 if (options != NULL) 917 *options = bhnd_nvram_plist_retain(sc->data_opts); 918 919 /* 920 * If we're re-encoding device paths, don't bother preserving the 921 * existing NVRAM variable order; our variable names will not match 922 * the existing backing NVRAM data. 923 */ 924 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) { 925 *props = unordered; 926 unordered = NULL; 927 928 goto finished; 929 } 930 931 /* 932 * Re-order the flattened output to match the existing NVRAM variable 933 * ordering. 934 * 935 * We append all new variables at the end of the input; this should 936 * reduce the delta that needs to be written (e.g. to flash) when 937 * committing NVRAM updates, and should result in a serialization 938 * identical to the input serialization if uncommitted updates are 939 * excluded from the export. 940 */ 941 if ((*props = bhnd_nvram_plist_new()) == NULL) { 942 error = ENOMEM; 943 goto failed; 944 } 945 946 /* Using the backing NVRAM data ordering to order all variables 947 * currently defined in the backing store */ 948 cookiep = NULL; 949 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { 950 prop = bhnd_nvram_plist_get_prop(unordered, name); 951 if (prop == NULL) 952 continue; 953 954 /* Append to ordered result */ 955 if ((error = bhnd_nvram_plist_append(*props, prop))) 956 goto failed; 957 958 /* Remove from unordered list */ 959 bhnd_nvram_plist_remove(unordered, name); 960 } 961 962 /* Any remaining variables are new, and should be appended to the 963 * end of the export list */ 964 prop = NULL; 965 while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) { 966 if ((error = bhnd_nvram_plist_append(*props, prop))) 967 goto failed; 968 } 969 970 /* Export complete */ 971 finished: 972 BHND_NVSTORE_UNLOCK(sc); 973 974 if (unordered != NULL) 975 bhnd_nvram_plist_release(unordered); 976 977 return (0); 978 979 failed: 980 BHND_NVSTORE_UNLOCK(sc); 981 982 if (unordered != NULL) 983 bhnd_nvram_plist_release(unordered); 984 985 if (options != NULL && *options != NULL) 986 bhnd_nvram_plist_release(*options); 987 988 if (*props != NULL) 989 bhnd_nvram_plist_release(*props); 990 991 return (error); 992 } 993 994 /** 995 * Encode all NVRAM properties at @p path, using the @p store's current NVRAM 996 * data format. 997 * 998 * @param sc The NVRAM store instance. 999 * @param path The NVRAM path to export, or NULL to select the root 1000 * path. 1001 * @param[out] data On success, will be set to the newly serialized value. 1002 * The caller is responsible for freeing this value 1003 * via bhnd_nvram_io_free(). 1004 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. 1005 * 1006 * @retval 0 success 1007 * @retval EINVAL If @p flags is invalid. 1008 * @retval ENOENT The requested path was not found. 1009 * @retval ENOMEM If allocation fails. 1010 * @retval non-zero If serialization of @p path otherwise fails, a regular 1011 * unix error code will be returned. 1012 */ 1013 int 1014 bhnd_nvram_store_serialize(struct bhnd_nvram_store *sc, const char *path, 1015 struct bhnd_nvram_io **data, uint32_t flags) 1016 { 1017 bhnd_nvram_plist *props; 1018 bhnd_nvram_plist *options; 1019 bhnd_nvram_data_class *cls; 1020 struct bhnd_nvram_io *io; 1021 void *outp; 1022 size_t olen; 1023 int error; 1024 1025 props = NULL; 1026 options = NULL; 1027 io = NULL; 1028 1029 /* Perform requested export */ 1030 error = bhnd_nvram_store_export(sc, path, &cls, &props, &options, 1031 flags); 1032 if (error) 1033 return (error); 1034 1035 /* Determine serialized size */ 1036 error = bhnd_nvram_data_serialize(cls, props, options, NULL, &olen); 1037 if (error) 1038 goto failed; 1039 1040 /* Allocate output buffer */ 1041 if ((io = bhnd_nvram_iobuf_empty(olen, olen)) == NULL) { 1042 error = ENOMEM; 1043 goto failed; 1044 } 1045 1046 /* Fetch write pointer */ 1047 if ((error = bhnd_nvram_io_write_ptr(io, 0, &outp, olen, NULL))) 1048 goto failed; 1049 1050 /* Perform serialization */ 1051 error = bhnd_nvram_data_serialize(cls, props, options, outp, &olen); 1052 if (error) 1053 goto failed; 1054 1055 if ((error = bhnd_nvram_io_setsize(io, olen))) 1056 goto failed; 1057 1058 /* Success */ 1059 bhnd_nvram_plist_release(props); 1060 bhnd_nvram_plist_release(options); 1061 1062 *data = io; 1063 return (0); 1064 1065 failed: 1066 if (props != NULL) 1067 bhnd_nvram_plist_release(props); 1068 1069 if (options != NULL) 1070 bhnd_nvram_plist_release(options); 1071 1072 if (io != NULL) 1073 bhnd_nvram_io_free(io); 1074 1075 return (error); 1076 } 1077 1078 /** 1079 * Read an NVRAM variable. 1080 * 1081 * @param sc The NVRAM parser state. 1082 * @param name The NVRAM variable name. 1083 * @param[out] outp On success, the requested value will be written 1084 * to this buffer. This argment may be NULL if 1085 * the value is not desired. 1086 * @param[in,out] olen The capacity of @p outp. On success, will be set 1087 * to the actual size of the requested value. 1088 * @param otype The requested data type to be written to 1089 * @p outp. 1090 * 1091 * @retval 0 success 1092 * @retval ENOENT The requested variable was not found. 1093 * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too 1094 * small to hold the requested value. 1095 * @retval non-zero If reading @p name otherwise fails, a regular unix 1096 * error code will be returned. 1097 */ 1098 int 1099 bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name, 1100 void *outp, size_t *olen, bhnd_nvram_type otype) 1101 { 1102 bhnd_nvstore_name_info info; 1103 bhnd_nvstore_path *path; 1104 bhnd_nvram_prop *prop; 1105 void *cookiep; 1106 int error; 1107 1108 BHND_NVSTORE_LOCK(sc); 1109 1110 /* Parse the variable name */ 1111 error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL, 1112 sc->data_caps, &info); 1113 if (error) 1114 goto finished; 1115 1116 /* Fetch the variable's enclosing path entry */ 1117 if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) { 1118 error = ENOENT; 1119 goto finished; 1120 } 1121 1122 /* Search uncommitted updates first */ 1123 prop = bhnd_nvstore_path_get_update(sc, path, info.name); 1124 if (prop != NULL) { 1125 if (bhnd_nvram_prop_is_null(prop)) { 1126 /* NULL denotes a pending deletion */ 1127 error = ENOENT; 1128 } else { 1129 error = bhnd_nvram_prop_encode(prop, outp, olen, otype); 1130 } 1131 goto finished; 1132 } 1133 1134 /* Search the backing NVRAM data */ 1135 cookiep = bhnd_nvstore_path_data_lookup(sc, path, info.name); 1136 if (cookiep != NULL) { 1137 /* Found in backing store */ 1138 error = bhnd_nvram_data_getvar(sc->data, cookiep, outp, olen, 1139 otype); 1140 goto finished; 1141 } 1142 1143 /* Not found */ 1144 error = ENOENT; 1145 1146 finished: 1147 BHND_NVSTORE_UNLOCK(sc); 1148 return (error); 1149 } 1150 1151 /** 1152 * Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar() 1153 * implementation. 1154 * 1155 * If @p value is NULL, the variable will be marked for deletion. 1156 */ 1157 static int 1158 bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name, 1159 bhnd_nvram_val *value) 1160 { 1161 bhnd_nvstore_path *path; 1162 bhnd_nvstore_name_info info; 1163 int error; 1164 1165 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 1166 1167 /* Parse the variable name */ 1168 error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL, 1169 sc->data_caps, &info); 1170 if (error) 1171 return (error); 1172 1173 /* Fetch the variable's enclosing path entry */ 1174 if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) 1175 return (error); 1176 1177 /* Register the update entry */ 1178 return (bhnd_nvstore_path_register_update(sc, path, info.name, value)); 1179 } 1180 1181 /** 1182 * Set an NVRAM variable. 1183 * 1184 * @param sc The NVRAM parser state. 1185 * @param name The NVRAM variable name. 1186 * @param value The new value. 1187 * 1188 * @retval 0 success 1189 * @retval ENOENT The requested variable @p name was not found. 1190 * @retval EINVAL If @p value is invalid. 1191 */ 1192 int 1193 bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name, 1194 bhnd_nvram_val *value) 1195 { 1196 int error; 1197 1198 BHND_NVSTORE_LOCK(sc); 1199 error = bhnd_nvram_store_setval_common(sc, name, value); 1200 BHND_NVSTORE_UNLOCK(sc); 1201 1202 return (error); 1203 } 1204 1205 /** 1206 * Set an NVRAM variable. 1207 * 1208 * @param sc The NVRAM parser state. 1209 * @param name The NVRAM variable name. 1210 * @param[out] inp The new value. 1211 * @param[in,out] ilen The size of @p inp. 1212 * @param itype The data type of @p inp. 1213 * 1214 * @retval 0 success 1215 * @retval ENOENT The requested variable @p name was not found. 1216 * @retval EINVAL If the new value is invalid. 1217 * @retval EINVAL If @p name is read-only. 1218 */ 1219 int 1220 bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name, 1221 const void *inp, size_t ilen, bhnd_nvram_type itype) 1222 { 1223 bhnd_nvram_val val; 1224 int error; 1225 1226 error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype, 1227 BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA); 1228 if (error) { 1229 BHND_NV_LOG("error initializing value: %d\n", error); 1230 return (EINVAL); 1231 } 1232 1233 BHND_NVSTORE_LOCK(sc); 1234 error = bhnd_nvram_store_setval_common(sc, name, &val); 1235 BHND_NVSTORE_UNLOCK(sc); 1236 1237 bhnd_nvram_val_release(&val); 1238 1239 return (error); 1240 } 1241 1242 /** 1243 * Unset an NVRAM variable. 1244 * 1245 * @param sc The NVRAM parser state. 1246 * @param name The NVRAM variable name. 1247 * 1248 * @retval 0 success 1249 * @retval ENOENT The requested variable @p name was not found. 1250 * @retval EINVAL If @p name is read-only. 1251 */ 1252 int 1253 bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name) 1254 { 1255 int error; 1256 1257 BHND_NVSTORE_LOCK(sc); 1258 error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL); 1259 BHND_NVSTORE_UNLOCK(sc); 1260 1261 return (error); 1262 } 1263