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 /* 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 * Parse and register path and path alias entries for all declarations found in 388 * the NVRAM data backing @p nvram. 389 * 390 * @param sc The NVRAM store instance. 391 * 392 * @retval 0 success 393 * @retval non-zero If parsing fails, a regular unix error code will be 394 * returned. 395 */ 396 static int 397 bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc) 398 { 399 const char *name; 400 void *cookiep; 401 int error; 402 403 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 404 405 /* Skip path registration if the data source does not support device 406 * paths. */ 407 if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) { 408 BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path")); 409 return (0); 410 } 411 412 /* Otherwise, parse and register all paths and path aliases */ 413 cookiep = NULL; 414 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { 415 bhnd_nvstore_name_info info; 416 417 /* Parse the name info */ 418 error = bhnd_nvstore_parse_name_info(name, 419 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info); 420 if (error) 421 return (error); 422 423 /* Register the path */ 424 error = bhnd_nvstore_var_register_path(sc, &info, cookiep); 425 if (error) { 426 BHND_NV_LOG("failed to register path for %s: %d\n", 427 name, error); 428 return (error); 429 } 430 } 431 432 return (0); 433 } 434 435 /** 436 * Merge exported per-path variables (uncommitted, committed, or both) into 437 * the empty @p merged property list. 438 * 439 * @param sc The NVRAM store instance. 440 * @param path The NVRAM path to be exported. 441 * @param merged The property list to populate with the merged results. 442 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. 443 * 444 * @retval 0 success 445 * @retval ENOMEM If allocation fails. 446 * @retval non-zero If merging the variables defined in @p path otherwise 447 * fails, a regular unix error code will be returned. 448 */ 449 static int 450 bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc, 451 bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags) 452 { 453 void *cookiep, *idxp; 454 int error; 455 456 /* Populate merged list with all pending variables */ 457 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) { 458 bhnd_nvram_prop *prop; 459 460 prop = NULL; 461 while ((prop = bhnd_nvram_plist_next(path->pending, prop))) { 462 /* Skip variables marked for deletion */ 463 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) { 464 if (bhnd_nvram_prop_is_null(prop)) 465 continue; 466 } 467 468 /* Append to merged list */ 469 error = bhnd_nvram_plist_append(merged, prop); 470 if (error) 471 return (error); 472 } 473 } 474 475 /* Skip merging committed variables? */ 476 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED)) 477 return (0); 478 479 /* Merge in the committed NVRAM variables */ 480 idxp = NULL; 481 while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) { 482 const char *name; 483 bhnd_nvram_val *val; 484 485 /* Fetch the variable name */ 486 name = bhnd_nvram_data_getvar_name(sc->data, cookiep); 487 488 /* Trim device path prefix */ 489 if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) 490 name = bhnd_nvram_trim_path_name(name); 491 492 /* Skip if already defined in pending updates */ 493 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) { 494 if (bhnd_nvram_plist_contains(path->pending, name)) 495 continue; 496 } 497 498 /* Skip if higher precedence value was already defined. This 499 * may occur if the underlying data store contains duplicate 500 * keys; iteration will always return the definition with 501 * the highest precedence first */ 502 if (bhnd_nvram_plist_contains(merged, name)) 503 continue; 504 505 /* Fetch the variable's value representation */ 506 if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val))) 507 return (error); 508 509 /* Add to path variable list */ 510 error = bhnd_nvram_plist_append_val(merged, name, val); 511 bhnd_nvram_val_release(val); 512 if (error) 513 return (error); 514 } 515 516 return (0); 517 } 518 519 /** 520 * Find a free alias value for @p path, and append the devpathXX alias 521 * declaration to @p plist. 522 * 523 * @param sc The NVRAM store instance. 524 * @param path The NVRAM path for which a devpath alias 525 * variable should be produced. 526 * @param devpath The devpathXX path value for @p path. 527 * @param plist The property list to which @p path's devpath 528 * variable will be appended. 529 * @param[out] alias_val On success, will be set to the alias value 530 * allocated for @p path. 531 * 532 * @retval 0 success 533 * @retval ENOMEM If allocation fails. 534 * @retval non-zero If merging the variables defined in @p path otherwise 535 * fails, a regular unix error code will be returned. 536 */ 537 static int 538 bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc, 539 bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist, 540 u_long *alias_val) 541 { 542 bhnd_nvstore_alias *alias; 543 char *pathvar; 544 int error; 545 546 *alias_val = 0; 547 548 /* Prefer alias value already reserved for this path. */ 549 alias = bhnd_nvstore_find_alias(sc, path->path_str); 550 if (alias != NULL) { 551 *alias_val = alias->alias; 552 553 /* Allocate devpathXX variable name */ 554 bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val); 555 if (pathvar == NULL) 556 return (ENOMEM); 557 558 /* Append alias variable to property list */ 559 error = bhnd_nvram_plist_append_string(plist, pathvar, devpath); 560 561 BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use", 562 * alias_val, path->path_str)); 563 564 bhnd_nv_free(pathvar); 565 return (error); 566 } 567 568 /* Find the next free devpathXX alias entry */ 569 while (1) { 570 /* Skip existing reserved alias values */ 571 while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) { 572 if (*alias_val == ULONG_MAX) 573 return (ENOMEM); 574 575 (*alias_val)++; 576 } 577 578 /* Allocate devpathXX variable name */ 579 bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val); 580 if (pathvar == NULL) 581 return (ENOMEM); 582 583 /* If not in-use, we can terminate the search */ 584 if (!bhnd_nvram_plist_contains(plist, pathvar)) 585 break; 586 587 /* Keep searching */ 588 bhnd_nv_free(pathvar); 589 590 if (*alias_val == ULONG_MAX) 591 return (ENOMEM); 592 593 (*alias_val)++; 594 } 595 596 /* Append alias variable to property list */ 597 error = bhnd_nvram_plist_append_string(plist, pathvar, devpath); 598 599 bhnd_nv_free(pathvar); 600 return (error); 601 } 602 603 /** 604 * Export a single @p child path's properties, appending the result to @p plist. 605 * 606 * @param sc The NVRAM store instance. 607 * @param top The root NVRAM path being exported. 608 * @param child The NVRAM path to be exported. 609 * @param plist The property list to which @p child's exported 610 * properties should be appended. 611 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. 612 * 613 * @retval 0 success 614 * @retval ENOMEM If allocation fails. 615 * @retval non-zero If merging the variables defined in @p path otherwise 616 * fails, a regular unix error code will be returned. 617 */ 618 static int 619 bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc, 620 bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist, 621 uint32_t flags) 622 { 623 bhnd_nvram_plist *path_vars; 624 bhnd_nvram_prop *prop; 625 const char *relpath; 626 char *prefix, *namebuf; 627 size_t prefix_len, relpath_len; 628 size_t namebuf_size, num_props; 629 bool emit_compact_devpath; 630 int error; 631 632 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 633 634 prefix = NULL; 635 num_props = 0; 636 path_vars = NULL; 637 namebuf = NULL; 638 639 /* Determine the path relative to the top-level path */ 640 relpath = bhnd_nvstore_parse_relpath(top->path_str, child->path_str); 641 if (relpath == NULL) { 642 /* Skip -- not a child of the root path */ 643 return (0); 644 } 645 relpath_len = strlen(relpath); 646 647 /* Skip sub-path if export of children was not requested, */ 648 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0) 649 return (0); 650 651 /* Collect all variables to be included in the export */ 652 if ((path_vars = bhnd_nvram_plist_new()) == NULL) 653 return (ENOMEM); 654 655 if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) { 656 bhnd_nvram_plist_release(path_vars); 657 return (error); 658 } 659 660 /* Skip if no children are to be exported */ 661 if (bhnd_nvram_plist_count(path_vars) == 0) { 662 bhnd_nvram_plist_release(path_vars); 663 return (0); 664 } 665 666 /* Determine appropriate device path encoding */ 667 emit_compact_devpath = false; 668 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) { 669 /* Re-encode as compact (if non-empty path) */ 670 if (relpath_len > 0) 671 emit_compact_devpath = true; 672 } else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) { 673 /* Re-encode with fully expanded device path */ 674 emit_compact_devpath = false; 675 } else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) { 676 /* Preserve existing encoding of this path */ 677 if (bhnd_nvstore_find_alias(sc, child->path_str) != NULL) 678 emit_compact_devpath = true; 679 } else { 680 BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags); 681 error = EINVAL; 682 goto finished; 683 } 684 685 /* Allocate variable device path prefix to use for all property names, 686 * and if using compact encoding, emit the devpathXX= variable */ 687 prefix = NULL; 688 prefix_len = 0; 689 if (emit_compact_devpath) { 690 u_long alias_val; 691 int len; 692 693 /* Reserve an alias value and append the devpathXX= variable to 694 * the property list */ 695 error = bhnd_nvstore_export_devpath_alias(sc, child, relpath, 696 plist, &alias_val); 697 if (error) 698 goto finished; 699 700 /* Allocate variable name prefix */ 701 len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val); 702 if (prefix == NULL) { 703 error = ENOMEM; 704 goto finished; 705 } 706 707 prefix_len = len; 708 } else if (relpath_len > 0) { 709 int len; 710 711 /* Allocate the variable name prefix, appending '/' to the 712 * relative path */ 713 len = bhnd_nv_asprintf(&prefix, "%s/", relpath); 714 if (prefix == NULL) { 715 error = ENOMEM; 716 goto finished; 717 } 718 719 prefix_len = len; 720 } 721 722 /* If prefixing of variable names is required, allocate a name 723 * formatting buffer */ 724 namebuf_size = 0; 725 if (prefix != NULL) { 726 size_t maxlen; 727 728 /* Find the maximum name length */ 729 maxlen = 0; 730 prop = NULL; 731 while ((prop = bhnd_nvram_plist_next(path_vars, prop))) { 732 const char *name; 733 734 name = bhnd_nvram_prop_name(prop); 735 maxlen = bhnd_nv_ummax(strlen(name), maxlen); 736 } 737 738 /* Allocate name buffer (path-prefix + name + '\0') */ 739 namebuf_size = prefix_len + maxlen + 1; 740 namebuf = bhnd_nv_malloc(namebuf_size); 741 if (namebuf == NULL) { 742 error = ENOMEM; 743 goto finished; 744 } 745 } 746 747 /* Append all path variables to the export plist, prepending the 748 * device-path prefix to the variable names, if required */ 749 prop = NULL; 750 while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) { 751 const char *name; 752 753 /* Prepend device prefix to the variable name */ 754 name = bhnd_nvram_prop_name(prop); 755 if (prefix != NULL) { 756 int len; 757 758 /* 759 * Write prefixed variable name to our name buffer. 760 * 761 * We precalcuate the size when scanning all names 762 * above, so this should always succeed. 763 */ 764 len = snprintf(namebuf, namebuf_size, "%s%s", prefix, 765 name); 766 if (len < 0 || (size_t)len >= namebuf_size) 767 BHND_NV_PANIC("invalid max_name_len"); 768 769 name = namebuf; 770 } 771 772 /* Add property to export plist */ 773 error = bhnd_nvram_plist_append_val(plist, name, 774 bhnd_nvram_prop_val(prop)); 775 if (error) 776 goto finished; 777 } 778 779 /* Success */ 780 error = 0; 781 782 finished: 783 if (prefix != NULL) 784 bhnd_nv_free(prefix); 785 786 if (namebuf != NULL) 787 bhnd_nv_free(namebuf); 788 789 if (path_vars != NULL) 790 bhnd_nvram_plist_release(path_vars); 791 792 return (error); 793 } 794 795 /** 796 * Export a flat, ordered NVRAM property list representation of all NVRAM 797 * properties at @p path. 798 * 799 * @param sc The NVRAM store instance. 800 * @param path The NVRAM path to export, or NULL to select the root 801 * path. 802 * @param[out] cls On success, will be set to the backing data class 803 * of @p sc. If the data class is are not desired, 804 * a NULL pointer may be provided. 805 * @param[out] props On success, will be set to a caller-owned property 806 * list containing the exported properties. The caller is 807 * responsible for releasing this value via 808 * bhnd_nvram_plist_release(). 809 * @param[out] options On success, will be set to a caller-owned property 810 * list containing the current NVRAM serialization options 811 * for @p sc. The caller is responsible for releasing this 812 * value via bhnd_nvram_plist_release(). 813 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. 814 * 815 * @retval 0 success 816 * @retval EINVAL If @p flags is invalid. 817 * @retval ENOENT The requested path was not found. 818 * @retval ENOMEM If allocation fails. 819 * @retval non-zero If export of @p path otherwise fails, a regular unix 820 * error code will be returned. 821 */ 822 int 823 bhnd_nvram_store_export(struct bhnd_nvram_store *sc, const char *path, 824 bhnd_nvram_data_class **cls, bhnd_nvram_plist **props, 825 bhnd_nvram_plist **options, uint32_t flags) 826 { 827 bhnd_nvram_plist *unordered; 828 bhnd_nvstore_path *top; 829 bhnd_nvram_prop *prop; 830 const char *name; 831 void *cookiep; 832 size_t num_dpath_flags; 833 int error; 834 835 *props = NULL; 836 unordered = NULL; 837 num_dpath_flags = 0; 838 if (options != NULL) 839 *options = NULL; 840 841 /* Default to exporting root path */ 842 if (path == NULL) 843 path = BHND_NVSTORE_ROOT_PATH; 844 845 /* Default to exporting all properties */ 846 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) && 847 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) 848 { 849 flags |= BHND_NVSTORE_EXPORT_ALL_VARS; 850 } 851 852 /* Default to preserving the current device path encoding */ 853 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS) && 854 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) 855 { 856 flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS; 857 } 858 859 /* Exactly one device path encoding flag must be set */ 860 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) 861 num_dpath_flags++; 862 863 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) 864 num_dpath_flags++; 865 866 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) 867 num_dpath_flags++; 868 869 if (num_dpath_flags != 1) 870 return (EINVAL); 871 872 /* If EXPORT_DELETED is set, EXPORT_UNCOMMITTED must be set too */ 873 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED) && 874 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) 875 { 876 return (EINVAL); 877 } 878 879 /* Lock internal state before querying paths/properties */ 880 BHND_NVSTORE_LOCK(sc); 881 882 /* Fetch referenced path */ 883 top = bhnd_nvstore_get_path(sc, path, strlen(path)); 884 if (top == NULL) { 885 error = ENOENT; 886 goto failed; 887 } 888 889 /* Allocate new, empty property list */ 890 if ((unordered = bhnd_nvram_plist_new()) == NULL) { 891 error = ENOMEM; 892 goto failed; 893 } 894 895 /* Export the top-level path first */ 896 error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags); 897 if (error) 898 goto failed; 899 900 /* Attempt to export any children of the root path */ 901 for (size_t i = 0; i < nitems(sc->paths); i++) { 902 bhnd_nvstore_path *child; 903 904 LIST_FOREACH(child, &sc->paths[i], np_link) { 905 /* Top-level path was already exported */ 906 if (child == top) 907 continue; 908 909 error = bhnd_nvram_store_export_child(sc, top, 910 child, unordered, flags); 911 if (error) 912 goto failed; 913 } 914 } 915 916 /* If requested, provide the current class and serialization options */ 917 if (cls != NULL) 918 *cls = bhnd_nvram_data_get_class(sc->data); 919 920 if (options != NULL) 921 *options = bhnd_nvram_plist_retain(sc->data_opts); 922 923 /* 924 * If we're re-encoding device paths, don't bother preserving the 925 * existing NVRAM variable order; our variable names will not match 926 * the existing backing NVRAM data. 927 */ 928 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) { 929 *props = unordered; 930 unordered = NULL; 931 932 goto finished; 933 } 934 935 /* 936 * Re-order the flattened output to match the existing NVRAM variable 937 * ordering. 938 * 939 * We append all new variables at the end of the input; this should 940 * reduce the delta that needs to be written (e.g. to flash) when 941 * committing NVRAM updates, and should result in a serialization 942 * identical to the input serialization if uncommitted updates are 943 * excluded from the export. 944 */ 945 if ((*props = bhnd_nvram_plist_new()) == NULL) { 946 error = ENOMEM; 947 goto failed; 948 } 949 950 /* Using the backing NVRAM data ordering to order all variables 951 * currently defined in the backing store */ 952 cookiep = NULL; 953 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) { 954 prop = bhnd_nvram_plist_get_prop(unordered, name); 955 if (prop == NULL) 956 continue; 957 958 /* Append to ordered result */ 959 if ((error = bhnd_nvram_plist_append(*props, prop))) 960 goto failed; 961 962 /* Remove from unordered list */ 963 bhnd_nvram_plist_remove(unordered, name); 964 } 965 966 /* Any remaining variables are new, and should be appended to the 967 * end of the export list */ 968 prop = NULL; 969 while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) { 970 if ((error = bhnd_nvram_plist_append(*props, prop))) 971 goto failed; 972 } 973 974 /* Export complete */ 975 finished: 976 BHND_NVSTORE_UNLOCK(sc); 977 978 if (unordered != NULL) 979 bhnd_nvram_plist_release(unordered); 980 981 return (0); 982 983 failed: 984 BHND_NVSTORE_UNLOCK(sc); 985 986 if (unordered != NULL) 987 bhnd_nvram_plist_release(unordered); 988 989 if (options != NULL && *options != NULL) 990 bhnd_nvram_plist_release(*options); 991 992 if (*props != NULL) 993 bhnd_nvram_plist_release(*props); 994 995 return (error); 996 } 997 998 /** 999 * Encode all NVRAM properties at @p path, using the @p store's current NVRAM 1000 * data format. 1001 * 1002 * @param sc The NVRAM store instance. 1003 * @param path The NVRAM path to export, or NULL to select the root 1004 * path. 1005 * @param[out] data On success, will be set to the newly serialized value. 1006 * The caller is responsible for freeing this value 1007 * via bhnd_nvram_io_free(). 1008 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*. 1009 * 1010 * @retval 0 success 1011 * @retval EINVAL If @p flags is invalid. 1012 * @retval ENOENT The requested path was not found. 1013 * @retval ENOMEM If allocation fails. 1014 * @retval non-zero If serialization of @p path otherwise fails, a regular 1015 * unix error code will be returned. 1016 */ 1017 int 1018 bhnd_nvram_store_serialize(struct bhnd_nvram_store *sc, const char *path, 1019 struct bhnd_nvram_io **data, uint32_t flags) 1020 { 1021 bhnd_nvram_plist *props; 1022 bhnd_nvram_plist *options; 1023 bhnd_nvram_data_class *cls; 1024 struct bhnd_nvram_io *io; 1025 void *outp; 1026 size_t olen; 1027 int error; 1028 1029 props = NULL; 1030 options = NULL; 1031 io = NULL; 1032 1033 /* Perform requested export */ 1034 error = bhnd_nvram_store_export(sc, path, &cls, &props, &options, 1035 flags); 1036 if (error) 1037 return (error); 1038 1039 /* Determine serialized size */ 1040 error = bhnd_nvram_data_serialize(cls, props, options, NULL, &olen); 1041 if (error) 1042 goto failed; 1043 1044 /* Allocate output buffer */ 1045 if ((io = bhnd_nvram_iobuf_empty(olen, olen)) == NULL) { 1046 error = ENOMEM; 1047 goto failed; 1048 } 1049 1050 /* Fetch write pointer */ 1051 if ((error = bhnd_nvram_io_write_ptr(io, 0, &outp, olen, NULL))) 1052 goto failed; 1053 1054 /* Perform serialization */ 1055 error = bhnd_nvram_data_serialize(cls, props, options, outp, &olen); 1056 if (error) 1057 goto failed; 1058 1059 if ((error = bhnd_nvram_io_setsize(io, olen))) 1060 goto failed; 1061 1062 /* Success */ 1063 bhnd_nvram_plist_release(props); 1064 bhnd_nvram_plist_release(options); 1065 1066 *data = io; 1067 return (0); 1068 1069 failed: 1070 if (props != NULL) 1071 bhnd_nvram_plist_release(props); 1072 1073 if (options != NULL) 1074 bhnd_nvram_plist_release(options); 1075 1076 if (io != NULL) 1077 bhnd_nvram_io_free(io); 1078 1079 return (error); 1080 } 1081 1082 /** 1083 * Read an NVRAM variable. 1084 * 1085 * @param sc The NVRAM parser state. 1086 * @param name The NVRAM variable name. 1087 * @param[out] outp On success, the requested value will be written 1088 * to this buffer. This argment may be NULL if 1089 * the value is not desired. 1090 * @param[in,out] olen The capacity of @p outp. On success, will be set 1091 * to the actual size of the requested value. 1092 * @param otype The requested data type to be written to 1093 * @p outp. 1094 * 1095 * @retval 0 success 1096 * @retval ENOENT The requested variable was not found. 1097 * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too 1098 * small to hold the requested value. 1099 * @retval non-zero If reading @p name otherwise fails, a regular unix 1100 * error code will be returned. 1101 */ 1102 int 1103 bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name, 1104 void *outp, size_t *olen, bhnd_nvram_type otype) 1105 { 1106 bhnd_nvstore_name_info info; 1107 bhnd_nvstore_path *path; 1108 bhnd_nvram_prop *prop; 1109 void *cookiep; 1110 int error; 1111 1112 BHND_NVSTORE_LOCK(sc); 1113 1114 /* Parse the variable name */ 1115 error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL, 1116 sc->data_caps, &info); 1117 if (error) 1118 goto finished; 1119 1120 /* Fetch the variable's enclosing path entry */ 1121 if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) { 1122 error = ENOENT; 1123 goto finished; 1124 } 1125 1126 /* Search uncommitted updates first */ 1127 prop = bhnd_nvstore_path_get_update(sc, path, info.name); 1128 if (prop != NULL) { 1129 if (bhnd_nvram_prop_is_null(prop)) { 1130 /* NULL denotes a pending deletion */ 1131 error = ENOENT; 1132 } else { 1133 error = bhnd_nvram_prop_encode(prop, outp, olen, otype); 1134 } 1135 goto finished; 1136 } 1137 1138 /* Search the backing NVRAM data */ 1139 cookiep = bhnd_nvstore_path_data_lookup(sc, path, info.name); 1140 if (cookiep != NULL) { 1141 /* Found in backing store */ 1142 error = bhnd_nvram_data_getvar(sc->data, cookiep, outp, olen, 1143 otype); 1144 goto finished; 1145 } 1146 1147 /* Not found */ 1148 error = ENOENT; 1149 1150 finished: 1151 BHND_NVSTORE_UNLOCK(sc); 1152 return (error); 1153 } 1154 1155 /** 1156 * Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar() 1157 * implementation. 1158 * 1159 * If @p value is NULL, the variable will be marked for deletion. 1160 */ 1161 static int 1162 bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name, 1163 bhnd_nvram_val *value) 1164 { 1165 bhnd_nvstore_path *path; 1166 bhnd_nvstore_name_info info; 1167 int error; 1168 1169 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 1170 1171 /* Parse the variable name */ 1172 error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL, 1173 sc->data_caps, &info); 1174 if (error) 1175 return (error); 1176 1177 /* Fetch the variable's enclosing path entry */ 1178 if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) 1179 return (error); 1180 1181 /* Register the update entry */ 1182 return (bhnd_nvstore_path_register_update(sc, path, info.name, value)); 1183 } 1184 1185 /** 1186 * Set an NVRAM variable. 1187 * 1188 * @param sc The NVRAM parser state. 1189 * @param name The NVRAM variable name. 1190 * @param value The new value. 1191 * 1192 * @retval 0 success 1193 * @retval ENOENT The requested variable @p name was not found. 1194 * @retval EINVAL If @p value is invalid. 1195 */ 1196 int 1197 bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name, 1198 bhnd_nvram_val *value) 1199 { 1200 int error; 1201 1202 BHND_NVSTORE_LOCK(sc); 1203 error = bhnd_nvram_store_setval_common(sc, name, value); 1204 BHND_NVSTORE_UNLOCK(sc); 1205 1206 return (error); 1207 } 1208 1209 /** 1210 * Set an NVRAM variable. 1211 * 1212 * @param sc The NVRAM parser state. 1213 * @param name The NVRAM variable name. 1214 * @param[out] inp The new value. 1215 * @param[in,out] ilen The size of @p inp. 1216 * @param itype The data type of @p inp. 1217 * 1218 * @retval 0 success 1219 * @retval ENOENT The requested variable @p name was not found. 1220 * @retval EINVAL If the new value is invalid. 1221 * @retval EINVAL If @p name is read-only. 1222 */ 1223 int 1224 bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name, 1225 const void *inp, size_t ilen, bhnd_nvram_type itype) 1226 { 1227 bhnd_nvram_val val; 1228 int error; 1229 1230 error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype, 1231 BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA); 1232 if (error) { 1233 BHND_NV_LOG("error initializing value: %d\n", error); 1234 return (EINVAL); 1235 } 1236 1237 BHND_NVSTORE_LOCK(sc); 1238 error = bhnd_nvram_store_setval_common(sc, name, &val); 1239 BHND_NVSTORE_UNLOCK(sc); 1240 1241 bhnd_nvram_val_release(&val); 1242 1243 return (error); 1244 } 1245 1246 /** 1247 * Unset an NVRAM variable. 1248 * 1249 * @param sc The NVRAM parser state. 1250 * @param name The NVRAM variable name. 1251 * 1252 * @retval 0 success 1253 * @retval ENOENT The requested variable @p name was not found. 1254 * @retval EINVAL If @p name is read-only. 1255 */ 1256 int 1257 bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name) 1258 { 1259 int error; 1260 1261 BHND_NVSTORE_LOCK(sc); 1262 error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL); 1263 BHND_NVSTORE_UNLOCK(sc); 1264 1265 return (error); 1266 } 1267