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