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