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