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