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