1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Interfaces for getting device configuration data from kernel 30 * through the devinfo driver. 31 */ 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <stropts.h> 38 #include <fcntl.h> 39 #include <poll.h> 40 #include <synch.h> 41 #include <unistd.h> 42 #include <sys/mkdev.h> 43 #include <sys/obpdefs.h> 44 #include <sys/stat.h> 45 #include <sys/types.h> 46 #include <sys/time.h> 47 #include <sys/autoconf.h> 48 #include <stdarg.h> 49 50 #define NDEBUG 1 51 #include <assert.h> 52 53 #include "libdevinfo.h" 54 55 /* 56 * Debug message levels 57 */ 58 typedef enum { 59 DI_QUIET = 0, /* No debug messages - the default */ 60 DI_ERR = 1, 61 DI_INFO, 62 DI_TRACE, 63 DI_TRACE1, 64 DI_TRACE2 65 } di_debug_t; 66 67 int di_debug = DI_QUIET; 68 69 #define DPRINTF(args) { if (di_debug != DI_QUIET) dprint args; } 70 71 void dprint(di_debug_t msglevel, const char *fmt, ...); 72 73 74 #pragma init(_libdevinfo_init) 75 76 void 77 _libdevinfo_init() 78 { 79 char *debug_str = getenv("_LIBDEVINFO_DEBUG"); 80 81 if (debug_str) { 82 errno = 0; 83 di_debug = atoi(debug_str); 84 if (errno || di_debug < DI_QUIET) 85 di_debug = DI_QUIET; 86 } 87 } 88 89 di_node_t 90 di_init(const char *phys_path, uint_t flag) 91 { 92 return (di_init_impl(phys_path, flag, NULL)); 93 } 94 95 /* 96 * We use blocking_open() to guarantee access to the devinfo device, if open() 97 * is failing with EAGAIN. 98 */ 99 static int 100 blocking_open(const char *path, int oflag) 101 { 102 int fd; 103 104 while ((fd = open(path, oflag)) == -1 && errno == EAGAIN) 105 (void) poll(NULL, 0, 1 * MILLISEC); 106 107 return (fd); 108 } 109 110 /* private interface */ 111 di_node_t 112 di_init_driver(const char *drv_name, uint_t flag) 113 { 114 int fd; 115 char driver[MAXPATHLEN]; 116 117 /* 118 * Don't allow drv_name to exceed MAXPATHLEN - 1, or 1023, 119 * which should be sufficient for any sensible programmer. 120 */ 121 if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) { 122 errno = EINVAL; 123 return (DI_NODE_NIL); 124 } 125 (void) strcpy(driver, drv_name); 126 127 /* 128 * open the devinfo driver 129 */ 130 if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo", 131 O_RDONLY)) == -1) { 132 DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno)); 133 return (DI_NODE_NIL); 134 } 135 136 if (ioctl(fd, DINFOLODRV, driver) != 0) { 137 DPRINTF((DI_ERR, "failed to load driver %s\n", driver)); 138 (void) close(fd); 139 errno = ENXIO; 140 return (DI_NODE_NIL); 141 } 142 (void) close(fd); 143 144 /* 145 * Driver load succeeded, return a snapshot 146 */ 147 return (di_init("/", flag)); 148 } 149 150 di_node_t 151 di_init_impl(const char *phys_path, uint_t flag, 152 struct di_priv_data *priv) 153 { 154 caddr_t pa; 155 int fd, map_size; 156 struct di_all *dap; 157 struct dinfo_io dinfo_io; 158 159 uint_t pageoffset = sysconf(_SC_PAGESIZE) - 1; 160 uint_t pagemask = ~pageoffset; 161 162 DPRINTF((DI_INFO, "di_init: taking a snapshot\n")); 163 164 /* 165 * Make sure there is no minor name in the path 166 * and the path do not start with /devices.... 167 */ 168 if (strchr(phys_path, ':') || 169 (strncmp(phys_path, "/devices", 8) == 0) || 170 (strlen(phys_path) > MAXPATHLEN)) { 171 errno = EINVAL; 172 return (DI_NODE_NIL); 173 } 174 175 if (strlen(phys_path) == 0) 176 (void) sprintf(dinfo_io.root_path, "/"); 177 else if (*phys_path != '/') 178 (void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path), 179 "/%s", phys_path); 180 else 181 (void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path), 182 "%s", phys_path); 183 184 /* 185 * If private data is requested, copy the format specification 186 */ 187 if (flag & DINFOPRIVDATA & 0xff) { 188 if (priv) 189 bcopy(priv, &dinfo_io.priv, 190 sizeof (struct di_priv_data)); 191 else { 192 errno = EINVAL; 193 return (DI_NODE_NIL); 194 } 195 } 196 197 /* 198 * Attempt to open the devinfo driver. Make a second attempt at the 199 * read-only minor node if we don't have privileges to open the full 200 * version _and_ if we're not requesting operations that the read-only 201 * node can't perform. (Setgid processes would fail an access() test, 202 * of course.) 203 */ 204 if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo", 205 O_RDONLY)) == -1) { 206 if ((flag & DINFOFORCE) == DINFOFORCE || 207 (flag & DINFOPRIVDATA) == DINFOPRIVDATA) { 208 /* 209 * We wanted to perform a privileged operation, but the 210 * privileged node isn't available. Don't modify errno 211 * on our way out (but display it if we're running with 212 * di_debug set). 213 */ 214 DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", 215 errno)); 216 return (DI_NODE_NIL); 217 } 218 219 if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo,ro", 220 O_RDONLY)) == -1) { 221 DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", 222 errno)); 223 return (DI_NODE_NIL); 224 } 225 } 226 227 /* 228 * Verify that there is no major conflict, i.e., we are indeed opening 229 * the devinfo driver. 230 */ 231 if (ioctl(fd, DINFOIDENT, NULL) != DI_MAGIC) { 232 DPRINTF((DI_ERR, 233 "driver ID failed; check for major conflict\n")); 234 (void) close(fd); 235 return (DI_NODE_NIL); 236 } 237 238 /* 239 * create snapshot 240 */ 241 if ((map_size = ioctl(fd, flag, &dinfo_io)) < 0) { 242 DPRINTF((DI_ERR, "devinfo ioctl failed with " 243 "error: %d\n", errno)); 244 (void) close(fd); 245 return (DI_NODE_NIL); 246 } else if (map_size == 0) { 247 DPRINTF((DI_ERR, "%s not found\n", phys_path)); 248 errno = ENXIO; 249 (void) close(fd); 250 return (DI_NODE_NIL); 251 } 252 253 /* 254 * copy snapshot to userland 255 */ 256 map_size = (map_size + pageoffset) & pagemask; 257 if ((pa = valloc(map_size)) == NULL) { 258 DPRINTF((DI_ERR, "valloc failed for snapshot\n")); 259 (void) close(fd); 260 return (DI_NODE_NIL); 261 } 262 263 if (ioctl(fd, DINFOUSRLD, pa) != map_size) { 264 DPRINTF((DI_ERR, "failed to copy snapshot to usrld\n")); 265 (void) close(fd); 266 free(pa); 267 errno = EFAULT; 268 return (DI_NODE_NIL); 269 } 270 271 (void) close(fd); 272 273 dap = DI_ALL(pa); 274 if (dap->version != DI_SNAPSHOT_VERSION) { 275 DPRINTF((DI_ERR, "wrong snapshot version " 276 "(expected=%d, actual=%d)\n", 277 DI_SNAPSHOT_VERSION, dap->version)); 278 free(pa); 279 errno = ESTALE; 280 return (DI_NODE_NIL); 281 } 282 if (dap->top_devinfo == 0) { /* phys_path not found */ 283 DPRINTF((DI_ERR, "%s not found\n", phys_path)); 284 free(pa); 285 errno = EINVAL; 286 return (DI_NODE_NIL); 287 } 288 289 return (DI_NODE(pa + dap->top_devinfo)); 290 } 291 292 void 293 di_fini(di_node_t root) 294 { 295 caddr_t pa; /* starting address of map */ 296 297 DPRINTF((DI_INFO, "di_fini: freeing a snapshot\n")); 298 299 /* 300 * paranoid checking 301 */ 302 if (root == DI_NODE_NIL) { 303 DPRINTF((DI_ERR, "di_fini called with NIL arg\n")); 304 return; 305 } 306 307 /* 308 * The root contains its own offset--self. 309 * Subtracting it from root address, we get the starting addr. 310 * The map_size is stored at the beginning of snapshot. 311 * Once we have starting address and size, we can free(). 312 */ 313 pa = (caddr_t)root - DI_NODE(root)->self; 314 315 free(pa); 316 } 317 318 di_node_t 319 di_parent_node(di_node_t node) 320 { 321 caddr_t pa; /* starting address of map */ 322 323 if (node == DI_NODE_NIL) { 324 errno = EINVAL; 325 return (DI_NODE_NIL); 326 } 327 328 DPRINTF((DI_TRACE, "Get parent of node %s\n", di_node_name(node))); 329 330 pa = (caddr_t)node - DI_NODE(node)->self; 331 332 if (DI_NODE(node)->parent) { 333 return (DI_NODE(pa + DI_NODE(node)->parent)); 334 } 335 336 /* 337 * Deal with error condition: 338 * If parent doesn't exist and node is not the root, 339 * set errno to ENOTSUP. Otherwise, set errno to ENXIO. 340 */ 341 if (strcmp(DI_ALL(pa)->root_path, "/") != 0) 342 errno = ENOTSUP; 343 else 344 errno = ENXIO; 345 346 return (DI_NODE_NIL); 347 } 348 349 di_node_t 350 di_sibling_node(di_node_t node) 351 { 352 caddr_t pa; /* starting address of map */ 353 354 if (node == DI_NODE_NIL) { 355 errno = EINVAL; 356 return (DI_NODE_NIL); 357 } 358 359 DPRINTF((DI_TRACE, "Get sibling of node %s\n", di_node_name(node))); 360 361 pa = (caddr_t)node - DI_NODE(node)->self; 362 363 if (DI_NODE(node)->sibling) { 364 return (DI_NODE(pa + DI_NODE(node)->sibling)); 365 } 366 367 /* 368 * Deal with error condition: 369 * Sibling doesn't exist, figure out if ioctl command 370 * has DINFOSUBTREE set. If it doesn't, set errno to 371 * ENOTSUP. 372 */ 373 if (!(DI_ALL(pa)->command & DINFOSUBTREE)) 374 errno = ENOTSUP; 375 else 376 errno = ENXIO; 377 378 return (DI_NODE_NIL); 379 } 380 381 di_node_t 382 di_child_node(di_node_t node) 383 { 384 caddr_t pa; /* starting address of map */ 385 386 DPRINTF((DI_TRACE, "Get child of node %s\n", di_node_name(node))); 387 388 if (node == DI_NODE_NIL) { 389 errno = EINVAL; 390 return (DI_NODE_NIL); 391 } 392 393 pa = (caddr_t)node - DI_NODE(node)->self; 394 395 if (DI_NODE(node)->child) { 396 return (DI_NODE(pa + DI_NODE(node)->child)); 397 } 398 399 /* 400 * Deal with error condition: 401 * Child doesn't exist, figure out if DINFOSUBTREE is set. 402 * If it isn't, set errno to ENOTSUP. 403 */ 404 if (!(DI_ALL(pa)->command & DINFOSUBTREE)) 405 errno = ENOTSUP; 406 else 407 errno = ENXIO; 408 409 return (DI_NODE_NIL); 410 } 411 412 di_node_t 413 di_drv_first_node(const char *drv_name, di_node_t root) 414 { 415 caddr_t pa; /* starting address of map */ 416 int major, devcnt; 417 struct di_devnm *devnm; 418 419 DPRINTF((DI_INFO, "Get first node of driver %s\n", drv_name)); 420 421 if (root == DI_NODE_NIL) { 422 errno = EINVAL; 423 return (DI_NODE_NIL); 424 } 425 426 /* 427 * get major number of driver 428 */ 429 pa = (caddr_t)root - DI_NODE(root)->self; 430 devcnt = DI_ALL(pa)->devcnt; 431 devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); 432 433 for (major = 0; major < devcnt; major++) 434 if (devnm[major].name && (strcmp(drv_name, 435 (char *)(pa + devnm[major].name)) == 0)) 436 break; 437 438 if (major >= devcnt) { 439 errno = EINVAL; 440 return (DI_NODE_NIL); 441 } 442 443 if (!(devnm[major].head)) { 444 errno = ENXIO; 445 return (DI_NODE_NIL); 446 } 447 448 return (DI_NODE(pa + devnm[major].head)); 449 } 450 451 di_node_t 452 di_drv_next_node(di_node_t node) 453 { 454 caddr_t pa; /* starting address of map */ 455 456 if (node == DI_NODE_NIL) { 457 errno = EINVAL; 458 return (DI_NODE_NIL); 459 } 460 461 DPRINTF((DI_TRACE, "next node on per driver list:" 462 " current=%s, driver=%s\n", 463 di_node_name(node), di_driver_name(node))); 464 465 if (DI_NODE(node)->next == (di_off_t)-1) { 466 errno = ENOTSUP; 467 return (DI_NODE_NIL); 468 } 469 470 pa = (caddr_t)node - DI_NODE(node)->self; 471 472 if (DI_NODE(node)->next == NULL) { 473 errno = ENXIO; 474 return (DI_NODE_NIL); 475 } 476 477 return (DI_NODE(pa + DI_NODE(node)->next)); 478 } 479 480 /* 481 * Internal library interfaces: 482 * node_list etc. for node walking 483 */ 484 struct node_list { 485 struct node_list *next; 486 di_node_t node; 487 }; 488 489 static void 490 free_node_list(struct node_list **headp) 491 { 492 struct node_list *tmp; 493 494 while (*headp) { 495 tmp = *headp; 496 *headp = (*headp)->next; 497 free(tmp); 498 } 499 } 500 501 static void 502 append_node_list(struct node_list **headp, struct node_list *list) 503 { 504 struct node_list *tmp; 505 506 if (*headp == NULL) { 507 *headp = list; 508 return; 509 } 510 511 if (list == NULL) /* a minor optimization */ 512 return; 513 514 tmp = *headp; 515 while (tmp->next) 516 tmp = tmp->next; 517 518 tmp->next = list; 519 } 520 521 static void 522 prepend_node_list(struct node_list **headp, struct node_list *list) 523 { 524 struct node_list *tmp; 525 526 if (list == NULL) 527 return; 528 529 tmp = *headp; 530 *headp = list; 531 532 if (tmp == NULL) /* a minor optimization */ 533 return; 534 535 while (list->next) 536 list = list->next; 537 538 list->next = tmp; 539 } 540 541 /* 542 * returns 1 if node is a descendant of parent, 0 otherwise 543 */ 544 static int 545 is_descendant(di_node_t node, di_node_t parent) 546 { 547 /* 548 * DI_NODE_NIL is parent of root, so it is 549 * the parent of all nodes. 550 */ 551 if (parent == DI_NODE_NIL) { 552 return (1); 553 } 554 555 do { 556 node = di_parent_node(node); 557 } while ((node != DI_NODE_NIL) && (node != parent)); 558 559 return (node != DI_NODE_NIL); 560 } 561 562 /* 563 * Insert list before the first node which is NOT a descendent of parent. 564 * This is needed to reproduce the exact walking order of link generators. 565 */ 566 static void 567 insert_node_list(struct node_list **headp, struct node_list *list, 568 di_node_t parent) 569 { 570 struct node_list *tmp, *tmp1; 571 572 if (list == NULL) 573 return; 574 575 tmp = *headp; 576 if (tmp == NULL) { /* a minor optimization */ 577 *headp = list; 578 return; 579 } 580 581 if (!is_descendant(tmp->node, parent)) { 582 prepend_node_list(headp, list); 583 return; 584 } 585 586 /* 587 * Find first node which is not a descendant 588 */ 589 while (tmp->next && is_descendant(tmp->next->node, parent)) { 590 tmp = tmp->next; 591 } 592 593 tmp1 = tmp->next; 594 tmp->next = list; 595 append_node_list(headp, tmp1); 596 } 597 598 /* 599 * Get a linked list of handles of all children 600 */ 601 static struct node_list * 602 get_children(di_node_t node) 603 { 604 di_node_t child; 605 struct node_list *result, *tmp; 606 607 DPRINTF((DI_TRACE1, "Get children of node %s\n", di_node_name(node))); 608 609 if ((child = di_child_node(node)) == DI_NODE_NIL) { 610 return (NULL); 611 } 612 613 if ((result = malloc(sizeof (struct node_list))) == NULL) { 614 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 615 return (NULL); 616 } 617 618 result->node = child; 619 tmp = result; 620 621 while ((child = di_sibling_node(tmp->node)) != DI_NODE_NIL) { 622 if ((tmp->next = malloc(sizeof (struct node_list))) == NULL) { 623 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 624 free_node_list(&result); 625 return (NULL); 626 } 627 tmp = tmp->next; 628 tmp->node = child; 629 } 630 631 tmp->next = NULL; 632 633 return (result); 634 } 635 636 /* 637 * Internal library interface: 638 * Delete all siblings of the first node from the node_list, along with 639 * the first node itself. 640 */ 641 static void 642 prune_sib(struct node_list **headp) 643 { 644 di_node_t parent, curr_par, curr_gpar; 645 struct node_list *curr, *prev; 646 647 /* 648 * get handle to parent of first node 649 */ 650 if ((parent = di_parent_node((*headp)->node)) == DI_NODE_NIL) { 651 /* 652 * This must be the root of the snapshot, so can't 653 * have any siblings. 654 * 655 * XXX Put a check here just in case. 656 */ 657 if ((*headp)->next) 658 DPRINTF((DI_ERR, "Unexpected err in di_walk_node.\n")); 659 660 free(*headp); 661 *headp = NULL; 662 return; 663 } 664 665 /* 666 * To be complete, we should also delete the children 667 * of siblings that have already been visited. 668 * This happens for DI_WALK_SIBFIRST when the first node 669 * is NOT the first in the linked list of siblings. 670 * 671 * Hence, we compare parent with BOTH the parent and grandparent 672 * of nodes, and delete node is a match is found. 673 */ 674 prev = *headp; 675 curr = prev->next; 676 while (curr) { 677 if (((curr_par = di_parent_node(curr->node)) != DI_NODE_NIL) && 678 ((curr_par == parent) || ((curr_gpar = 679 di_parent_node(curr_par)) != DI_NODE_NIL) && 680 (curr_gpar == parent))) { 681 /* 682 * match parent/grandparent: delete curr 683 */ 684 prev->next = curr->next; 685 free(curr); 686 curr = prev->next; 687 } else 688 curr = curr->next; 689 } 690 691 /* 692 * delete the first node 693 */ 694 curr = *headp; 695 *headp = curr->next; 696 free(curr); 697 } 698 699 /* 700 * Internal library function: 701 * Update node list based on action (return code from callback) 702 * and flag specifying walking behavior. 703 */ 704 static void 705 update_node_list(int action, uint_t flag, struct node_list **headp) 706 { 707 struct node_list *children, *tmp; 708 di_node_t parent = di_parent_node((*headp)->node); 709 710 switch (action) { 711 case DI_WALK_TERMINATE: 712 /* 713 * free the node list and be done 714 */ 715 children = NULL; 716 free_node_list(headp); 717 break; 718 719 case DI_WALK_PRUNESIB: 720 /* 721 * Get list of children and prune siblings 722 */ 723 children = get_children((*headp)->node); 724 prune_sib(headp); 725 break; 726 727 case DI_WALK_PRUNECHILD: 728 /* 729 * Set children to NULL and pop first node 730 */ 731 children = NULL; 732 tmp = *headp; 733 *headp = tmp->next; 734 free(tmp); 735 break; 736 737 case DI_WALK_CONTINUE: 738 default: 739 /* 740 * Get list of children and pop first node 741 */ 742 children = get_children((*headp)->node); 743 tmp = *headp; 744 *headp = tmp->next; 745 free(tmp); 746 break; 747 } 748 749 /* 750 * insert the list of children 751 */ 752 switch (flag) { 753 case DI_WALK_CLDFIRST: 754 prepend_node_list(headp, children); 755 break; 756 757 case DI_WALK_SIBFIRST: 758 append_node_list(headp, children); 759 break; 760 761 case DI_WALK_LINKGEN: 762 default: 763 insert_node_list(headp, children, parent); 764 break; 765 } 766 } 767 768 /* 769 * Internal library function: 770 * Invoke callback on one node and update the list of nodes to be walked 771 * based on the flag and return code. 772 */ 773 static void 774 walk_one_node(struct node_list **headp, uint_t flag, void *arg, 775 int (*callback)(di_node_t, void *)) 776 { 777 DPRINTF((DI_TRACE, "Walking node %s\n", di_node_name((*headp)->node))); 778 779 update_node_list(callback((*headp)->node, arg), 780 flag & DI_WALK_MASK, headp); 781 } 782 783 int 784 di_walk_node(di_node_t root, uint_t flag, void *arg, 785 int (*node_callback)(di_node_t, void *)) 786 { 787 struct node_list *head; /* node_list for tree walk */ 788 789 if (root == NULL) { 790 errno = EINVAL; 791 return (-1); 792 } 793 794 if ((head = malloc(sizeof (struct node_list))) == NULL) { 795 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 796 return (-1); 797 } 798 799 head->next = NULL; 800 head->node = root; 801 802 DPRINTF((DI_INFO, "Start node walking from node %s\n", 803 di_node_name(root))); 804 805 while (head != NULL) 806 walk_one_node(&head, flag, arg, node_callback); 807 808 return (0); 809 } 810 811 /* 812 * Internal library function: 813 * Invoke callback for each minor on the minor list of first node 814 * on node_list headp, and place children of first node on the list. 815 * 816 * This is similar to walk_one_node, except we only walk in child 817 * first mode. 818 */ 819 static void 820 walk_one_minor_list(struct node_list **headp, const char *desired_type, 821 uint_t flag, void *arg, int (*callback)(di_node_t, di_minor_t, void *)) 822 { 823 int ddm_type; 824 int action = DI_WALK_CONTINUE; 825 char *node_type; 826 di_minor_t minor = DI_MINOR_NIL; 827 di_node_t node = (*headp)->node; 828 829 while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 830 ddm_type = di_minor_type(minor); 831 832 if ((ddm_type == DDM_ALIAS) && !(flag & DI_CHECK_ALIAS)) 833 continue; 834 835 if ((ddm_type == DDM_INTERNAL_PATH) && 836 !(flag & DI_CHECK_INTERNAL_PATH)) 837 continue; 838 839 node_type = di_minor_nodetype(minor); 840 if ((desired_type != NULL) && ((node_type == NULL) || 841 strncmp(desired_type, node_type, strlen(desired_type)) 842 != 0)) 843 continue; 844 845 if ((action = callback(node, minor, arg)) == 846 DI_WALK_TERMINATE) { 847 break; 848 } 849 } 850 851 update_node_list(action, DI_WALK_LINKGEN, headp); 852 } 853 854 int 855 di_walk_minor(di_node_t root, const char *minor_type, uint_t flag, void *arg, 856 int (*minor_callback)(di_node_t, di_minor_t, void *)) 857 { 858 struct node_list *head; /* node_list for tree walk */ 859 860 #ifdef DEBUG 861 char *devfspath = di_devfs_path(root); 862 DPRINTF((DI_INFO, "walking minor nodes under %s\n", devfspath)); 863 di_devfs_path_free(devfspath); 864 #endif 865 866 if (root == NULL) { 867 errno = EINVAL; 868 return (-1); 869 } 870 871 if ((head = malloc(sizeof (struct node_list))) == NULL) { 872 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 873 return (-1); 874 } 875 876 head->next = NULL; 877 head->node = root; 878 879 DPRINTF((DI_INFO, "Start minor walking from node %s\n", 880 di_node_name(root))); 881 882 while (head != NULL) 883 walk_one_minor_list(&head, minor_type, flag, arg, 884 minor_callback); 885 886 return (0); 887 } 888 889 /* 890 * generic node parameters 891 * Calling these routines always succeeds. 892 */ 893 char * 894 di_node_name(di_node_t node) 895 { 896 return ((caddr_t)node + DI_NODE(node)->node_name - DI_NODE(node)->self); 897 } 898 899 /* returns NULL ptr or a valid ptr to non-NULL string */ 900 char * 901 di_bus_addr(di_node_t node) 902 { 903 caddr_t pa = (caddr_t)node - DI_NODE(node)->self; 904 905 if (DI_NODE(node)->address == 0) 906 return (NULL); 907 908 return ((char *)(pa + DI_NODE(node)->address)); 909 } 910 911 char * 912 di_binding_name(di_node_t node) 913 { 914 caddr_t pa = (caddr_t)node - DI_NODE(node)->self; 915 916 if (DI_NODE(node)->bind_name == 0) 917 return (NULL); 918 919 return ((char *)(pa + DI_NODE(node)->bind_name)); 920 } 921 922 int 923 di_compatible_names(di_node_t node, char **names) 924 { 925 char *c; 926 int len, size, entries = 0; 927 928 if (DI_NODE(node)->compat_names == 0) { 929 *names = NULL; 930 return (0); 931 } 932 933 *names = (caddr_t)node + 934 DI_NODE(node)->compat_names - DI_NODE(node)->self; 935 936 c = *names; 937 len = DI_NODE(node)->compat_length; 938 while (len > 0) { 939 entries++; 940 size = strlen(c) + 1; 941 len -= size; 942 c += size; 943 } 944 945 return (entries); 946 } 947 948 int 949 di_instance(di_node_t node) 950 { 951 return (DI_NODE(node)->instance); 952 } 953 954 /* 955 * XXX: emulate the return value of the old implementation 956 * using info from devi_node_class and devi_node_attributes. 957 */ 958 int 959 di_nodeid(di_node_t node) 960 { 961 if (DI_NODE(node)->node_class == DDI_NC_PROM) 962 return (DI_PROM_NODEID); 963 964 if (DI_NODE(node)->attributes & DDI_PERSISTENT) 965 return (DI_SID_NODEID); 966 967 return (DI_PSEUDO_NODEID); 968 } 969 970 uint_t 971 di_state(di_node_t node) 972 { 973 uint_t result = 0; 974 975 if (di_node_state(node) < DS_ATTACHED) 976 result |= DI_DRIVER_DETACHED; 977 if (DI_NODE(node)->state & DEVI_DEVICE_OFFLINE) 978 result |= DI_DEVICE_OFFLINE; 979 if (DI_NODE(node)->state & DEVI_DEVICE_DOWN) 980 result |= DI_DEVICE_OFFLINE; 981 if (DI_NODE(node)->state & DEVI_DEVICE_DEGRADED) 982 result |= DI_DEVICE_DEGRADED; 983 if (DI_NODE(node)->state & DEVI_BUS_QUIESCED) 984 result |= DI_BUS_QUIESCED; 985 if (DI_NODE(node)->state & DEVI_BUS_DOWN) 986 result |= DI_BUS_DOWN; 987 988 return (result); 989 } 990 991 ddi_node_state_t 992 di_node_state(di_node_t node) 993 { 994 return (DI_NODE(node)->node_state); 995 } 996 997 uint_t 998 di_flags(di_node_t node) 999 { 1000 return (DI_NODE(node)->flags); 1001 } 1002 1003 uint_t 1004 di_retired(di_node_t node) 1005 { 1006 return (di_flags(node) & DEVI_RETIRED); 1007 } 1008 1009 ddi_devid_t 1010 di_devid(di_node_t node) 1011 { 1012 if (DI_NODE(node)->devid == 0) 1013 return (NULL); 1014 1015 return ((ddi_devid_t)((caddr_t)node + 1016 DI_NODE(node)->devid - DI_NODE(node)->self)); 1017 } 1018 1019 int 1020 di_driver_major(di_node_t node) 1021 { 1022 int major; 1023 1024 major = DI_NODE(node)->drv_major; 1025 if (major < 0) 1026 return (-1); 1027 return (major); 1028 } 1029 1030 char * 1031 di_driver_name(di_node_t node) 1032 { 1033 int major; 1034 caddr_t pa; 1035 struct di_devnm *devnm; 1036 1037 major = DI_NODE(node)->drv_major; 1038 if (major < 0) 1039 return (NULL); 1040 1041 pa = (caddr_t)node - DI_NODE(node)->self; 1042 devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); 1043 1044 if (devnm[major].name) 1045 return (pa + devnm[major].name); 1046 else 1047 return (NULL); 1048 } 1049 1050 uint_t 1051 di_driver_ops(di_node_t node) 1052 { 1053 int major; 1054 caddr_t pa; 1055 struct di_devnm *devnm; 1056 1057 major = DI_NODE(node)->drv_major; 1058 if (major < 0) 1059 return (0); 1060 1061 pa = (caddr_t)node - DI_NODE(node)->self; 1062 devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); 1063 1064 return (devnm[major].ops); 1065 } 1066 1067 /* 1068 * returns the length of the path, caller must free memory 1069 */ 1070 char * 1071 di_devfs_path(di_node_t node) 1072 { 1073 caddr_t pa; 1074 di_node_t parent; 1075 int depth = 0, len = 0; 1076 char *buf, *name[MAX_TREE_DEPTH], *addr[MAX_TREE_DEPTH]; 1077 1078 if (node == DI_NODE_NIL) { 1079 errno = EINVAL; 1080 return (NULL); 1081 } 1082 1083 /* 1084 * trace back to root, note the node_name & address 1085 */ 1086 while ((parent = di_parent_node(node)) != DI_NODE_NIL) { 1087 name[depth] = di_node_name(node); 1088 len += strlen(name[depth]) + 1; /* 1 for '/' */ 1089 1090 if ((addr[depth] = di_bus_addr(node)) != NULL) 1091 len += strlen(addr[depth]) + 1; /* 1 for '@' */ 1092 1093 node = parent; 1094 depth++; 1095 } 1096 1097 /* 1098 * get the path to the root of snapshot 1099 */ 1100 pa = (caddr_t)node - DI_NODE(node)->self; 1101 name[depth] = DI_ALL(pa)->root_path; 1102 len += strlen(name[depth]) + 1; 1103 1104 /* 1105 * allocate buffer and assemble path 1106 */ 1107 if ((buf = malloc(len)) == NULL) { 1108 return (NULL); 1109 } 1110 1111 (void) strcpy(buf, name[depth]); 1112 len = strlen(buf); 1113 if (buf[len - 1] == '/') 1114 len--; /* delete trailing '/' */ 1115 1116 while (depth) { 1117 depth--; 1118 buf[len] = '/'; 1119 (void) strcpy(buf + len + 1, name[depth]); 1120 len += strlen(name[depth]) + 1; 1121 if (addr[depth] && addr[depth][0] != '\0') { 1122 buf[len] = '@'; 1123 (void) strcpy(buf + len + 1, addr[depth]); 1124 len += strlen(addr[depth]) + 1; 1125 } 1126 } 1127 1128 return (buf); 1129 } 1130 1131 char * 1132 di_devfs_minor_path(di_minor_t minor) 1133 { 1134 di_node_t node; 1135 char *full_path, *name, *devfspath; 1136 int full_path_len; 1137 1138 if (minor == DI_MINOR_NIL) { 1139 errno = EINVAL; 1140 return (NULL); 1141 } 1142 1143 name = di_minor_name(minor); 1144 node = di_minor_devinfo(minor); 1145 devfspath = di_devfs_path(node); 1146 if (devfspath == NULL) 1147 return (NULL); 1148 1149 /* make the full path to the device minor node */ 1150 full_path_len = strlen(devfspath) + strlen(name) + 2; 1151 full_path = (char *)calloc(1, full_path_len); 1152 if (full_path != NULL) 1153 (void) snprintf(full_path, full_path_len, "%s:%s", 1154 devfspath, name); 1155 1156 di_devfs_path_free(devfspath); 1157 return (full_path); 1158 } 1159 1160 /* 1161 * Produce a string representation of path to di_path_t (pathinfo node). This 1162 * string is identical to di_devfs_path had the device been enumerated under 1163 * the pHCI: it has a base path to pHCI, then uses node_name of client, and 1164 * device unit-address of pathinfo node. 1165 */ 1166 char * 1167 di_path_devfs_path(di_path_t path) 1168 { 1169 di_node_t phci_node; 1170 char *phci_path, *path_name, *path_addr; 1171 char *full_path; 1172 int full_path_len; 1173 1174 if (path == DI_PATH_NIL) { 1175 errno =