1 /*- 2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/hash.h> 35 #include <sys/queue.h> 36 37 #ifdef _KERNEL 38 39 #include <sys/ctype.h> 40 #include <sys/systm.h> 41 42 #include <machine/_inttypes.h> 43 44 #else /* !_KERNEL */ 45 46 #include <ctype.h> 47 #include <errno.h> 48 #include <inttypes.h> 49 #include <stdbool.h> 50 #include <stdio.h> 51 #include <stdint.h> 52 #include <stdlib.h> 53 #include <string.h> 54 55 #endif /* _KERNEL */ 56 57 #include "bhnd_nvram_private.h" 58 #include "bhnd_nvram_datavar.h" 59 60 #include "bhnd_nvram_storevar.h" 61 62 static int bhnd_nvstore_idx_cmp(void *ctx, 63 const void *lhs, const void *rhs); 64 65 /** 66 * Allocate and initialize a new path instance. 67 * 68 * The caller is responsible for deallocating the instance via 69 * bhnd_nvstore_path_free(). 70 * 71 * @param path_str The path's canonical string representation. 72 * @param path_len The length of @p path_str. 73 * 74 * @retval non-NULL success 75 * @retval NULL if allocation fails. 76 */ 77 bhnd_nvstore_path * 78 bhnd_nvstore_path_new(const char *path_str, size_t path_len) 79 { 80 bhnd_nvstore_path *path; 81 82 /* Allocate new entry */ 83 path = bhnd_nv_malloc(sizeof(*path)); 84 if (path == NULL) 85 return (NULL); 86 87 path->index = NULL; 88 path->num_vars = 0; 89 90 path->pending = bhnd_nvram_plist_new(); 91 if (path->pending == NULL) 92 goto failed; 93 94 path->path_str = bhnd_nv_strndup(path_str, path_len); 95 if (path->path_str == NULL) 96 goto failed; 97 98 return (path); 99 100 failed: 101 if (path->pending != NULL) 102 bhnd_nvram_plist_release(path->pending); 103 104 if (path->path_str != NULL) 105 bhnd_nv_free(path->path_str); 106 107 bhnd_nv_free(path); 108 109 return (NULL); 110 } 111 112 /** 113 * Free an NVRAM path instance, releasing all associated resources. 114 */ 115 void 116 bhnd_nvstore_path_free(struct bhnd_nvstore_path *path) 117 { 118 /* Free the per-path index */ 119 if (path->index != NULL) 120 bhnd_nvstore_index_free(path->index); 121 122 bhnd_nvram_plist_release(path->pending); 123 bhnd_nv_free(path->path_str); 124 bhnd_nv_free(path); 125 } 126 127 /** 128 * Allocate and initialize a new index instance with @p capacity. 129 * 130 * The caller is responsible for deallocating the instance via 131 * bhnd_nvstore_index_free(). 132 * 133 * @param capacity The maximum number of variables to be indexed. 134 * 135 * @retval non-NULL success 136 * @retval NULL if allocation fails. 137 */ 138 bhnd_nvstore_index * 139 bhnd_nvstore_index_new(size_t capacity) 140 { 141 bhnd_nvstore_index *index; 142 size_t bytes; 143 144 /* Allocate and populate variable index */ 145 bytes = sizeof(struct bhnd_nvstore_index) + (sizeof(void *) * capacity); 146 index = bhnd_nv_malloc(bytes); 147 if (index == NULL) { 148 BHND_NV_LOG("error allocating %zu byte index\n", bytes); 149 return (NULL); 150 } 151 152 index->count = 0; 153 index->capacity = capacity; 154 155 return (index); 156 } 157 158 /** 159 * Free an index instance, releasing all associated resources. 160 * 161 * @param index An index instance previously allocated via 162 * bhnd_nvstore_index_new(). 163 */ 164 void 165 bhnd_nvstore_index_free(bhnd_nvstore_index *index) 166 { 167 bhnd_nv_free(index); 168 } 169 170 /** 171 * Append a new NVRAM variable's @p cookiep value to @p index. 172 * 173 * After one or more append requests, the index must be prepared via 174 * bhnd_nvstore_index_prepare() before any indexed lookups are performed. 175 * 176 * @param sc The NVRAM store from which NVRAM values will be queried. 177 * @param index The index to be modified. 178 * @param cookiep The cookiep value (as provided by the backing NVRAM 179 * data instance of @p sc) to be included in @p index. 180 * 181 * @retval 0 success 182 * @retval ENOMEM if appending an additional entry would exceed the 183 * capacity of @p index. 184 */ 185 int 186 bhnd_nvstore_index_append(struct bhnd_nvram_store *sc, 187 bhnd_nvstore_index *index, void *cookiep) 188 { 189 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 190 191 if (index->count >= index->capacity) 192 return (ENOMEM); 193 194 index->cookiep[index->count] = cookiep; 195 index->count++; 196 return (0); 197 } 198 199 /* sort function for bhnd_nvstore_index_prepare() */ 200 static int 201 bhnd_nvstore_idx_cmp(void *ctx, const void *lhs, const void *rhs) 202 { 203 struct bhnd_nvram_store *sc; 204 void *l_cookiep, *r_cookiep; 205 const char *l_str, *r_str; 206 const char *l_name, *r_name; 207 int order; 208 209 sc = ctx; 210 l_cookiep = *(void * const *)lhs; 211 r_cookiep = *(void * const *)rhs; 212 213 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 214 215 /* Fetch string pointers from the cookiep values */ 216 l_str = bhnd_nvram_data_getvar_name(sc->data, l_cookiep); 217 r_str = bhnd_nvram_data_getvar_name(sc->data, r_cookiep); 218 219 /* Trim device path prefixes */ 220 if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) { 221 l_name = bhnd_nvram_trim_path_name(l_str); 222 r_name = bhnd_nvram_trim_path_name(r_str); 223 } else { 224 l_name = l_str; 225 r_name = r_str; 226 } 227 228 /* Perform comparison */ 229 order = strcmp(l_name, r_name); 230 if (order != 0 || lhs == rhs) 231 return (order); 232 233 /* If the backing data incorrectly contains variables with duplicate 234 * names, we need a sort order that provides stable behavior. 235 * 236 * Since Broadcom's own code varies wildly on this question, we just 237 * use a simple precedence rule: The first declaration of a variable 238 * takes precedence. */ 239 return (bhnd_nvram_data_getvar_order(sc->data, l_cookiep, r_cookiep)); 240 } 241 242 /** 243 * Prepare @p index for querying via bhnd_nvstore_index_lookup(). 244 * 245 * After one or more append requests, the index must be prepared via 246 * bhnd_nvstore_index_prepare() before any indexed lookups are performed. 247 * 248 * @param sc The NVRAM store from which NVRAM values will be queried. 249 * @param index The index to be prepared. 250 * 251 * @retval 0 success 252 * @retval non-zero if preparing @p index otherwise fails, a regular unix 253 * error code will be returned. 254 */ 255 int 256 bhnd_nvstore_index_prepare(struct bhnd_nvram_store *sc, 257 bhnd_nvstore_index *index) 258 { 259 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 260 261 /* Sort the index table */ 262 qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]), sc, 263 bhnd_nvstore_idx_cmp); 264 265 return (0); 266 } 267 268 /** 269 * Return a borrowed reference to the root path node. 270 * 271 * @param sc The NVRAM store. 272 */ 273 bhnd_nvstore_path * 274 bhnd_nvstore_get_root_path(struct bhnd_nvram_store *sc) 275 { 276 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 277 return (sc->root_path); 278 } 279 280 /** 281 * Return true if @p path is the root path node. 282 * 283 * @param sc The NVRAM store. 284 * @param path The path to query. 285 */ 286 bool 287 bhnd_nvstore_is_root_path(struct bhnd_nvram_store *sc, bhnd_nvstore_path *path) 288 { 289 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 290 return (sc->root_path == path); 291 } 292 293 /** 294 * Return the update entry matching @p name in @p path, or NULL if no entry 295 * found. 296 * 297 * @param sc The NVRAM store. 298 * @param path The path to query. 299 * @param name The NVRAM variable name to search for in @p path's update list. 300 * 301 * @retval non-NULL success 302 * @retval NULL if @p name is not found in @p path. 303 */ 304 bhnd_nvram_prop * 305 bhnd_nvstore_path_get_update(struct bhnd_nvram_store *sc, 306 bhnd_nvstore_path *path, const char *name) 307 { 308 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 309 return (bhnd_nvram_plist_get_prop(path->pending, name)); 310 } 311 312 /** 313 * Register or remove an update record for @p name in @p path. 314 * 315 * @param sc The NVRAM store. 316 * @param path The path to be modified. 317 * @param name The path-relative variable name to be modified. 318 * @param value The new value. A value of BHND_NVRAM_TYPE_NULL denotes deletion. 319 * 320 * @retval 0 success 321 * @retval ENOMEM if allocation fails. 322 * @retval ENOENT if @p name is unknown. 323 * @retval EINVAL if @p value is NULL, and deletion of @p is not 324 * supported. 325 * @retval EINVAL if @p value cannot be converted to a supported value 326 * type. 327 */ 328 int 329 bhnd_nvstore_path_register_update(struct bhnd_nvram_store *sc, 330 bhnd_nvstore_path *path, const char *name, bhnd_nvram_val *value) 331 { 332 bhnd_nvram_val *prop_val; 333 const char *full_name; 334 void *cookiep; 335 char *namebuf; 336 int error; 337 bool nvram_committed; 338 339 namebuf = NULL; 340 prop_val = NULL; 341 342 /* Determine whether the variable is currently defined in the 343 * backing NVRAM data, and derive its full path-prefixed name */ 344 nvram_committed = false; 345 cookiep = bhnd_nvstore_path_data_lookup(sc, path, name); 346 if (cookiep != NULL) { 347 /* Variable is defined in the backing data */ 348 nvram_committed = true; 349 350 /* Use the existing variable name */ 351 full_name = bhnd_nvram_data_getvar_name(sc->data, cookiep); 352 } else if (path == sc->root_path) { 353 /* No prefix required for root path */ 354 full_name = name; 355 } else { 356 bhnd_nvstore_alias *alias; 357 int len; 358 359 /* New variable is being set; we need to determine the 360 * appropriate path prefix */ 361 alias = bhnd_nvstore_find_alias(sc, path->path_str); 362 if (alias != NULL) { 363 /* Use <alias>:name */ 364 len = bhnd_nv_asprintf(&namebuf, "%lu:%s", alias->alias, 365 name); 366 } else { 367 /* Use path/name */ 368 len = bhnd_nv_asprintf(&namebuf, "%s/%s", 369 path->path_str, name); 370 } 371 372 if (len < 0) 373 return (ENOMEM); 374 375 full_name = namebuf; 376 } 377 378 /* Allow the data store to filter the NVRAM operation */ 379 if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) { 380 error = bhnd_nvram_data_filter_unsetvar(sc->data, full_name); 381 if (error) { 382 BHND_NV_LOG("cannot unset property %s: %d\n", full_name, 383 error); 384 goto cleanup; 385 } 386 387 if ((prop_val = bhnd_nvram_val_copy(value)) == NULL) { 388 error = ENOMEM; 389 goto cleanup; 390 } 391 } else { 392 error = bhnd_nvram_data_filter_setvar(sc->data, full_name, 393 value, &prop_val); 394 if (error) { 395 BHND_NV_LOG("cannot set property %s: %d\n", full_name, 396 error); 397 goto cleanup; 398 } 399 } 400 401 /* Add relative variable name to the per-path update list */ 402 if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL && 403 !nvram_committed) 404 { 405 /* This is a deletion request for a variable not defined in 406 * out backing store; we can simply remove the corresponding 407 * update entry. */ 408 bhnd_nvram_plist_remove(path->pending, name); 409 } else { 410 /* Update or append a pending update entry */ 411 error = bhnd_nvram_plist_replace_val(path->pending, name, 412 prop_val); 413 if (error) 414 goto cleanup; 415 } 416 417 /* Success */ 418 error = 0; 419 420 cleanup: 421 if (namebuf != NULL) 422 bhnd_nv_free(namebuf); 423 424 if (prop_val != NULL) 425 bhnd_nvram_val_release(prop_val); 426 427 return (error); 428 } 429 430 /** 431 * Iterate over all variable cookiep values retrievable from the backing 432 * data store in @p path. 433 * 434 * @warning Pending updates in @p path are ignored by this function. 435 * 436 * @param sc The NVRAM store. 437 * @param path The NVRAM path to be iterated. 438 * @param[in,out] indexp A pointer to an opaque indexp value previously 439 * returned by bhnd_nvstore_path_data_next(), or a 440 * NULL value to begin iteration. 441 * 442 * @return Returns the next variable name, or NULL if there are no more 443 * variables defined in @p path. 444 */ 445 void * 446 bhnd_nvstore_path_data_next(struct bhnd_nvram_store *sc, 447 bhnd_nvstore_path *path, void **indexp) 448 { 449 void **index_ref; 450 451 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 452 453 /* No index */ 454 if (path->index == NULL) { 455 /* An index is required for all non-empty, non-root path 456 * instances */ 457 BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path), 458 ("missing index for non-root path %s", path->path_str)); 459 460 /* Iterate NVRAM data directly, using the NVRAM data's cookiep 461 * value as our indexp context */ 462 if ((bhnd_nvram_data_next(sc->data, indexp)) == NULL) 463 return (NULL); 464 465 return (*indexp); 466 } 467 468 /* Empty index */ 469 if (path->index->count == 0) 470 return (NULL); 471 472 if (*indexp == NULL) { 473 /* First index entry */ 474 index_ref = &path->index->cookiep[0]; 475 } else { 476 size_t idxpos; 477 478 /* Advance to next index entry */ 479 index_ref = *indexp; 480 index_ref++; 481 482 /* Hit end of index? */ 483 BHND_NV_ASSERT(index_ref > path->index->cookiep, 484 ("invalid indexp")); 485 486 idxpos = (index_ref - path->index->cookiep); 487 if (idxpos >= path->index->count) 488 return (NULL); 489 } 490 491 /* Provide new index position */ 492 *indexp = index_ref; 493 494 /* Return the data's cookiep value */ 495 return (*index_ref); 496 } 497 498 /** 499 * Perform an lookup of @p name in the backing NVRAM data for @p path, 500 * returning the associated cookiep value, or NULL if the variable is not found 501 * in the backing NVRAM data. 502 * 503 * @warning Pending updates in @p path are ignored by this function. 504 * 505 * @param sc The NVRAM store from which NVRAM values will be queried. 506 * @param path The path to be queried. 507 * @param name The variable name to be queried. 508 * 509 * @retval non-NULL success 510 * @retval NULL if @p name is not found in @p index. 511 */ 512 void * 513 bhnd_nvstore_path_data_lookup(struct bhnd_nvram_store *sc, 514 bhnd_nvstore_path *path, const char *name) 515 { 516 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 517 518 /* No index */ 519 if (path->index == NULL) { 520 /* An index is required for all non-empty, non-root path 521 * instances */ 522 BHND_NV_ASSERT(bhnd_nvstore_is_root_path(sc, path), 523 ("missing index for non-root path %s", path->path_str)); 524 525 /* Look up directly in NVRAM data */ 526 return (bhnd_nvram_data_find(sc->data, name)); 527 } 528 529 /* Otherwise, delegate to an index-based lookup */ 530 return (bhnd_nvstore_index_lookup(sc, path->index, name)); 531 } 532 533 /** 534 * Perform an index lookup of @p name, returning the associated cookiep 535 * value, or NULL if the variable does not exist. 536 * 537 * @param sc The NVRAM store from which NVRAM values will be queried. 538 * @param index The index to be queried. 539 * @param name The variable name to be queried. 540 * 541 * @retval non-NULL success 542 * @retval NULL if @p name is not found in @p index. 543 */ 544 void * 545 bhnd_nvstore_index_lookup(struct bhnd_nvram_store *sc, 546 bhnd_nvstore_index *index, const char *name) 547 { 548 void *cookiep; 549 const char *indexed_name; 550 size_t min, mid, max; 551 uint32_t data_caps; 552 int order; 553 554 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 555 BHND_NV_ASSERT(index != NULL, ("NULL index")); 556 557 /* 558 * Locate the requested variable using a binary search. 559 */ 560 if (index->count == 0) 561 return (NULL); 562 563 data_caps = sc->data_caps; 564 min = 0; 565 max = index->count - 1; 566 567 while (max >= min) { 568 /* Select midpoint */ 569 mid = (min + max) / 2; 570 cookiep = index->cookiep[mid]; 571 572 /* Fetch variable name */ 573 indexed_name = bhnd_nvram_data_getvar_name(sc->data, cookiep); 574 575 /* Trim any path prefix */ 576 if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) 577 indexed_name = bhnd_nvram_trim_path_name(indexed_name); 578 579 /* Determine which side of the partition to search */ 580 order = strcmp(indexed_name, name); 581 if (order < 0) { 582 /* Search upper partition */ 583 min = mid + 1; 584 } else if (order > 0) { 585 /* Search (non-empty) lower partition */ 586 if (mid == 0) 587 break; 588 max = mid - 1; 589 } else if (order == 0) { 590 size_t idx; 591 592 /* 593 * Match found. 594 * 595 * If this happens to be a key with multiple definitions 596 * in the backing store, we need to find the entry with 597 * the highest declaration precedence. 598 * 599 * Duplicates are sorted in order of descending 600 * precedence; to find the highest precedence entry, 601 * we search backwards through the index. 602 */ 603 idx = mid; 604 while (idx > 0) { 605 void *dup_cookiep; 606 const char *dup_name; 607 608 /* Fetch preceding index entry */ 609 idx--; 610 dup_cookiep = index->cookiep[idx]; 611 dup_name = bhnd_nvram_data_getvar_name(sc->data, 612 dup_cookiep); 613 614 /* Trim any path prefix */ 615 if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) { 616 dup_name = bhnd_nvram_trim_path_name( 617 dup_name); 618 } 619 620 /* If no match, current cookiep is the variable 621 * definition with the highest precedence */ 622 if (strcmp(indexed_name, dup_name) != 0) 623 return (cookiep); 624 625 /* Otherwise, prefer this earlier definition, 626 * and keep searching for a higher-precedence 627 * definitions */ 628 cookiep = dup_cookiep; 629 } 630 631 return (cookiep); 632 } 633 } 634 635 /* Not found */ 636 return (NULL); 637 } 638 639 /** 640 * Return the device path entry registered for @p path, if any. 641 * 642 * @param sc The NVRAM store to be queried. 643 * @param path The device path to search for. 644 * @param path_len The length of @p path. 645 * 646 * @retval non-NULL if found. 647 * @retval NULL if not found. 648 */ 649 bhnd_nvstore_path * 650 bhnd_nvstore_get_path(struct bhnd_nvram_store *sc, const char *path, 651 size_t path_len) 652 { 653 bhnd_nvstore_path_list *plist; 654 bhnd_nvstore_path *p; 655 uint32_t h; 656 657 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 658 659 /* Use hash lookup */ 660 h = hash32_strn(path, path_len, HASHINIT); 661 plist = &sc->paths[h % nitems(sc->paths)]; 662 663 LIST_FOREACH(p, plist, np_link) { 664 /* Check for prefix match */ 665 if (strncmp(p->path_str, path, path_len) != 0) 666 continue; 667 668 /* Check for complete match */ 669 if (strnlen(path, path_len) != strlen(p->path_str)) 670 continue; 671 672 return (p); 673 } 674 675 /* Not found */ 676 return (NULL); 677 } 678 679 /** 680 * Resolve @p aval to its corresponding device path entry, if any. 681 * 682 * @param sc The NVRAM store to be queried. 683 * @param aval The device path alias value to search for. 684 * 685 * @retval non-NULL if found. 686 * @retval NULL if not found. 687 */ 688 bhnd_nvstore_path * 689 bhnd_nvstore_resolve_path_alias(struct bhnd_nvram_store *sc, u_long aval) 690 { 691 bhnd_nvstore_alias *alias; 692 693 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 694 695 /* Fetch alias entry */ 696 if ((alias = bhnd_nvstore_get_alias(sc, aval)) == NULL) 697 return (NULL); 698 699 return (alias->path); 700 } 701 702 /** 703 * Register a device path entry for the path referenced by variable name 704 * @p info, if any. 705 * 706 * @param sc The NVRAM store to be updated. 707 * @param info The NVRAM variable name info. 708 * @param cookiep The NVRAM variable's cookiep value. 709 * 710 * @retval 0 if the path was successfully registered, or an identical 711 * path or alias entry exists. 712 * @retval EEXIST if a conflicting entry already exists for the path or 713 * alias referenced by @p info. 714 * @retval ENOENT if @p info contains a dangling alias reference. 715 * @retval EINVAL if @p info contains an unsupported bhnd_nvstore_var_type 716 * and bhnd_nvstore_path_type combination. 717 * @retval ENOMEM if allocation fails. 718 */ 719 int 720 bhnd_nvstore_var_register_path(struct bhnd_nvram_store *sc, 721 bhnd_nvstore_name_info *info, void *cookiep) 722 { 723 switch (info->type) { 724 case BHND_NVSTORE_VAR: 725 /* Variable */ 726 switch (info->path_type) { 727 case BHND_NVSTORE_PATH_STRING: 728 /* Variable contains a full path string 729 * (pci/1/1/varname); register the path */ 730 return (bhnd_nvstore_register_path(sc, 731 info->path.str.value, info->path.str.value_len)); 732 733 case BHND_NVSTORE_PATH_ALIAS: 734 /* Variable contains an alias reference (0:varname). 735 * There's no path to register */ 736 return (0); 737 } 738 739 BHND_NV_PANIC("unsupported path type %d", info->path_type); 740 break; 741 742 case BHND_NVSTORE_ALIAS_DECL: 743 /* Alias declaration */ 744 return (bhnd_nvstore_register_alias(sc, info, cookiep)); 745 } 746 747 BHND_NV_PANIC("unsupported var type %d", info->type); 748 } 749 750 /** 751 * Resolve the device path entry referenced by @p info. 752 * 753 * @param sc The NVRAM store to be updated. 754 * @param info Variable name information descriptor containing 755 * the path or path alias to be resolved. 756 * 757 * @retval non-NULL if found. 758 * @retval NULL if not found. 759 */ 760 bhnd_nvstore_path * 761 bhnd_nvstore_var_get_path(struct bhnd_nvram_store *sc, 762 bhnd_nvstore_name_info *info) 763 { 764 switch (info->path_type) { 765 case BHND_NVSTORE_PATH_STRING: 766 return (bhnd_nvstore_get_path(sc, info->path.str.value, 767 info->path.str.value_len)); 768 case BHND_NVSTORE_PATH_ALIAS: 769 return (bhnd_nvstore_resolve_path_alias(sc, 770 info->path.alias.value)); 771 } 772 773 BHND_NV_PANIC("unsupported path type %d", info->path_type); 774 } 775 776 /** 777 * Return the device path alias entry registered for @p alias_val, if any. 778 * 779 * @param sc The NVRAM store to be queried. 780 * @param alias_val The alias value to search for. 781 * 782 * @retval non-NULL if found. 783 * @retval NULL if not found. 784 */ 785 bhnd_nvstore_alias * 786 bhnd_nvstore_get_alias(struct bhnd_nvram_store *sc, u_long alias_val) 787 { 788 bhnd_nvstore_alias_list *alist; 789 bhnd_nvstore_alias *alias; 790 791 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 792 793 /* Can use hash lookup */ 794 alist = &sc->aliases[alias_val % nitems(sc->aliases)]; 795 LIST_FOREACH(alias, alist, na_link) { 796 if (alias->alias == alias_val) 797 return (alias); 798 } 799 800 /* Not found */ 801 return (NULL); 802 } 803 804 /** 805 * Return the device path alias entry registered for @p path, if any. 806 * 807 * @param sc The NVRAM store to be queried. 808 * @param path The alias path to search for. 809 * 810 * @retval non-NULL if found. 811 * @retval NULL if not found. 812 */ 813 bhnd_nvstore_alias * 814 bhnd_nvstore_find_alias(struct bhnd_nvram_store *sc, const char *path) 815 { 816 bhnd_nvstore_alias *alias; 817 818 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 819 820 /* Have to scan the full table */ 821 for (size_t i = 0; i < nitems(sc->aliases); i++) { 822 LIST_FOREACH(alias, &sc->aliases[i], na_link) { 823 if (strcmp(alias->path->path_str, path) == 0) 824 return (alias); 825 } 826 } 827 828 /* Not found */ 829 return (NULL); 830 } 831 832 /** 833 * Register a device path entry for @p path. 834 * 835 * @param sc The NVRAM store to be updated. 836 * @param path_str The absolute device path string. 837 * @param path_len The length of @p path_str. 838 * 839 * @retval 0 if the path was successfully registered, or an identical 840 * path/alias entry already exists. 841 * @retval ENOMEM if allocation fails. 842 */ 843 int 844 bhnd_nvstore_register_path(struct bhnd_nvram_store *sc, const char *path_str, 845 size_t path_len) 846 { 847 bhnd_nvstore_path_list *plist; 848 bhnd_nvstore_path *path; 849 uint32_t h; 850 851 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 852 853 /* Already exists? */ 854 if (bhnd_nvstore_get_path(sc, path_str, path_len) != NULL) 855 return (0); 856 857 /* Can't represent more than SIZE_MAX paths */ 858 if (sc->num_paths == SIZE_MAX) 859 return (ENOMEM); 860 861 /* Allocate new entry */ 862 path = bhnd_nvstore_path_new(path_str, path_len); 863 if (path == NULL) 864 return (ENOMEM); 865 866 /* Insert in path hash table */ 867 h = hash32_str(path->path_str, HASHINIT); 868 plist = &sc->paths[h % nitems(sc->paths)]; 869 LIST_INSERT_HEAD(plist, path, np_link); 870 871 /* Increment path count */ 872 sc->num_paths++; 873 874 return (0); 875 } 876 877 /** 878 * Register a device path alias for an NVRAM 'devpathX' variable. 879 * 880 * The path value for the alias will be fetched from the backing NVRAM data. 881 * 882 * @param sc The NVRAM store to be updated. 883 * @param info The NVRAM variable name info. 884 * @param cookiep The NVRAM variable's cookiep value. 885 * 886 * @retval 0 if the alias was successfully registered, or an 887 * identical alias entry exists. 888 * @retval EEXIST if a conflicting alias or path entry already exists. 889 * @retval EINVAL if @p info is not a BHND_NVSTORE_ALIAS_DECL or does 890 * not contain a BHND_NVSTORE_PATH_ALIAS entry. 891 * @retval ENOMEM if allocation fails. 892 */ 893 int 894 bhnd_nvstore_register_alias(struct bhnd_nvram_store *sc, 895 const bhnd_nvstore_name_info *info, void *cookiep) 896 { 897 bhnd_nvstore_alias_list *alist; 898 bhnd_nvstore_alias *alias; 899 bhnd_nvstore_path *path; 900 char *path_str; 901 size_t path_len; 902 int error; 903 904 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); 905 906 path_str = NULL; 907 alias = NULL; 908 909 /* Can't represent more than SIZE_MAX aliases */ 910 if (sc->num_aliases == SIZE_MAX) 911 return (ENOMEM); 912 913 /* Must be an alias declaration */ 914 if (info->type != BHND_NVSTORE_ALIAS_DECL) 915 return (EINVAL); 916 917 if (info->path_type != BHND_NVSTORE_PATH_ALIAS) 918 return (EINVAL); 919 920 /* Fetch the devpath variable's value length */ 921 error = bhnd_nvram_data_getvar(sc->data, cookiep, NULL, &path_len, 922 BHND_NVRAM_TYPE_STRING); 923 if (error) 924 return (ENOMEM); 925 926 /* Allocate path string buffer */ 927 if ((path_str = bhnd_nv_malloc(path_len)) == NULL) 928 return (ENOMEM); 929 930 /* Decode to our new buffer */ 931 error = bhnd_nvram_data_getvar(sc->data, cookiep, path_str, &path_len, 932 BHND_NVRAM_TYPE_STRING); 933 if (error) 934 goto failed; 935 936 /* Trim trailing '/' character(s) from the path length */ 937 path_len = strnlen(path_str, path_len); 938 while (path_len > 0 && path_str[path_len-1] == '/') { 939 path_str[path_len-1] = '\0'; 940 path_len--; 941 } 942 943 /* Is a conflicting alias entry already registered for this alias 944 * value? */ 945 alias = bhnd_nvstore_get_alias(sc, info->path.alias.value); 946 if (alias != NULL) { 947 if (alias->cookiep != cookiep || 948 strcmp(alias->path->path_str, path_str) != 0) 949 { 950 error = EEXIST; 951 goto failed; 952 } 953 } 954 955 /* Is a conflicting entry already registered for the alias path? */ 956 if ((alias = bhnd_nvstore_find_alias(sc, path_str)) != NULL) { 957 if (alias->alias != info->path.alias.value || 958 alias->cookiep != cookiep || 959 strcmp(alias->path->path_str, path_str) != 0) 960 { 961 error = EEXIST; 962 goto failed; 963 } 964 } 965 966 /* Get (or register) the target path entry */ 967 path = bhnd_nvstore_get_path(sc, path_str, path_len); 968 if (path == NULL) { 969 error = bhnd_nvstore_register_path(sc, path_str, path_len); 970 if (error) 971 goto failed; 972 973 path = bhnd_nvstore_get_path(sc, path_str, path_len); 974 BHND_NV_ASSERT(path != NULL, ("missing registered path")); 975 } 976 977 /* Allocate alias entry */ 978 alias = bhnd_nv_calloc(1, sizeof(*alias)); 979 if (alias == NULL) { 980 error = ENOMEM; 981 goto failed; 982 } 983 984 alias->path = path; 985 alias->cookiep = cookiep; 986 alias->alias = info->path.alias.value; 987 988 /* Insert in alias hash table */ 989 alist = &sc->aliases[alias->alias % nitems(sc->aliases)]; 990 LIST_INSERT_HEAD(alist, alias, na_link); 991 992 /* Increment alias count */ 993 sc->num_aliases++; 994 995 bhnd_nv_free(path_str); 996 return (0); 997 998 failed: 999 if (path_str != NULL) 1000 bhnd_nv_free(path_str); 1001 1002 if (alias != NULL) 1003 bhnd_nv_free(alias); 1004 1005 return (error); 1006 } 1007 1008 /** 1009 * If @p child is equal to or a child path of @p parent, return a pointer to 1010 * @p child's path component(s) relative to @p parent; otherwise, return NULL. 1011 */ 1012 const char * 1013 bhnd_nvstore_parse_relpath(const char *parent, const char *child) 1014 { 1015 size_t prefix_len; 1016 1017 /* All paths have an implicit leading '/'; this allows us to treat 1018 * our manufactured root path of "/" as a prefix to all NVRAM-defined 1019 * paths (which do not necessarily include a leading '/' */ 1020 if (*parent == '/') 1021 parent++; 1022 1023 if (*child == '/') 1024 child++; 1025 1026 /* Is parent a prefix of child? */ 1027 prefix_len = strlen(parent); 1028 if (strncmp(parent, child, prefix_len) != 0) 1029 return (NULL); 1030 1031 /* A zero-length prefix matches everything */ 1032 if (prefix_len == 0) 1033 return (child); 1034 1035 /* Is child equal to parent? */ 1036 if (child[prefix_len] == '\0') 1037 return (child + prefix_len); 1038 1039 /* Is child actually a child of parent? */ 1040 if (child[prefix_len] == '/') 1041 return (child + prefix_len + 1); 1042 1043 /* No match (e.g. parent=/foo..., child=/fooo...) */ 1044 return (NULL); 1045 } 1046 1047 /** 1048 * Parse a raw NVRAM variable name and return its @p entry_type, its 1049 * type-specific @p prefix (e.g. '0:', 'pci/1/1', 'devpath'), and its 1050 * type-specific @p suffix (e.g. 'varname', '0'). 1051 * 1052 * @param name The NVRAM variable name to be parsed. This 1053 * value must remain valid for the lifetime of 1054 * @p info. 1055 * @param type The NVRAM name type -- either INTERNAL for names 1056 * parsed from backing NVRAM data, or EXTERNAL for 1057 * names provided by external NVRAM store clients. 1058 * @param data_caps The backing NVRAM data capabilities 1059 * (see bhnd_nvram_data_caps()). 1060 * @param[out] info On success, the parsed variable name info. 1061 * 1062 * @retval 0 success 1063 * @retval non-zero if parsing @p name otherwise fails, a regular unix 1064 * error code will be returned. 1065 */ 1066 int 1067 bhnd_nvstore_parse_name_info(const char *name, bhnd_nvstore_name_type type, 1068 uint32_t data_caps, bhnd_nvstore_name_info *info) 1069 { 1070 const char *p; 1071 char *endp; 1072 1073 /* Skip path parsing? */ 1074 if (data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS) { 1075 /* devpath declaration? (devpath0=pci/1/1) */ 1076 if (strncmp(name, "devpath", strlen("devpath")) == 0) { 1077 u_long alias; 1078 1079 /* Perform standard validation on the relative 1080 * variable name */ 1081 if (type != BHND_NVSTORE_NAME_INTERNAL && 1082 !bhnd_nvram_validate_name(name)) 1083 { 1084 return (ENOENT); 1085 } 1086 1087 /* Parse alias value that should follow a 'devpath' 1088 * prefix */ 1089 p = name + strlen("devpath"); 1090 alias = strtoul(p, &endp, 10); 1091 if (endp != p && *endp == '\0') { 1092 info->type = BHND_NVSTORE_ALIAS_DECL; 1093 info->path_type = BHND_NVSTORE_PATH_ALIAS; 1094 info->name = name; 1095 info->path.alias.value = alias; 1096 1097 return (0); 1098 } 1099 } 1100 1101 /* device aliased variable? (0:varname) */ 1102 if (bhnd_nv_isdigit(*name)) { 1103 u_long alias; 1104 1105 /* Parse '0:' alias prefix */ 1106 alias = strtoul(name, &endp, 10); 1107 if (endp != name && *endp == ':') { 1108 /* Perform standard validation on the relative 1109 * variable name */ 1110 if (type != BHND_NVSTORE_NAME_INTERNAL && 1111 !bhnd_nvram_validate_name(name)) 1112 { 1113 return (ENOENT); 1114 } 1115 1116 info->type = BHND_NVSTORE_VAR; 1117 info->path_type = BHND_NVSTORE_PATH_ALIAS; 1118 1119 /* name follows 0: prefix */ 1120 info->name = endp + 1; 1121 info->path.alias.value = alias; 1122 1123 return (0); 1124 } 1125 } 1126 1127 /* device variable? (pci/1/1/varname) */ 1128 if ((p = strrchr(name, '/')) != NULL) { 1129 const char *path, *relative_name; 1130 size_t path_len; 1131 1132 /* Determine the path length; 'p' points at the last 1133 * path separator in 'name' */ 1134 path_len = p - name; 1135 path = name; 1136 1137 /* The relative variable name directly follows the 1138 * final path separator '/' */ 1139 relative_name = path + path_len + 1; 1140 1141 /* Now that we calculated the name offset, exclude all 1142 * trailing '/' characters from the path length */ 1143 while (path_len > 0 && path[path_len-1] == '/') 1144 path_len--; 1145 1146 /* Perform standard validation on the relative 1147 * variable name */ 1148 if (type != BHND_NVSTORE_NAME_INTERNAL && 1149 !bhnd_nvram_validate_name(relative_name)) 1150 { 1151 return (ENOENT); 1152 } 1153 1154 /* Initialize result with pointers into the name 1155 * buffer */ 1156 info->type = BHND_NVSTORE_VAR; 1157 info->path_type = BHND_NVSTORE_PATH_STRING; 1158 info->name = relative_name; 1159 info->path.str.value = path; 1160 info->path.str.value_len = path_len; 1161 1162 return (0); 1163 } 1164 } 1165 1166 /* If all other parsing fails, the result is a simple variable with 1167 * an implicit path of "/" */ 1168 if (type != BHND_NVSTORE_NAME_INTERNAL && 1169 !bhnd_nvram_validate_name(name)) 1170 { 1171 /* Invalid relative name */ 1172 return (ENOENT); 1173 } 1174 1175 info->type = BHND_NVSTORE_VAR; 1176 info->path_type = BHND_NVSTORE_PATH_STRING; 1177 info->name = name; 1178 info->path.str.value = BHND_NVSTORE_ROOT_PATH; 1179 info->path.str.value_len = BHND_NVSTORE_ROOT_PATH_LEN; 1180 1181 return (0); 1182 } 1183