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