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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Interfaces for getting device configuration data from kernel 31 * through the devinfo driver. 32 */ 33 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <stropts.h> 39 #include <fcntl.h> 40 #include <poll.h> 41 #include <synch.h> 42 #include <unistd.h> 43 #include <sys/mkdev.h> 44 #include <sys/obpdefs.h> 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 #include <sys/time.h> 48 #include <sys/autoconf.h> 49 #include <stdarg.h> 50 51 #define NDEBUG 1 52 #include <assert.h> 53 54 #include "libdevinfo.h" 55 56 /* 57 * Debug message levels 58 */ 59 typedef enum { 60 DI_QUIET = 0, /* No debug messages - the default */ 61 DI_ERR = 1, 62 DI_INFO, 63 DI_TRACE, 64 DI_TRACE1, 65 DI_TRACE2 66 } di_debug_t; 67 68 int di_debug = DI_QUIET; 69 70 #define DPRINTF(args) { if (di_debug != DI_QUIET) dprint args; } 71 72 void dprint(di_debug_t msglevel, const char *fmt, ...); 73 74 75 #pragma init(_libdevinfo_init) 76 77 void 78 _libdevinfo_init() 79 { 80 char *debug_str = getenv("_LIBDEVINFO_DEBUG"); 81 82 if (debug_str) { 83 errno = 0; 84 di_debug = atoi(debug_str); 85 if (errno || di_debug < DI_QUIET) 86 di_debug = DI_QUIET; 87 } 88 } 89 90 di_node_t 91 di_init(const char *phys_path, uint_t flag) 92 { 93 return (di_init_impl(phys_path, flag, NULL)); 94 } 95 96 /* 97 * We use blocking_open() to guarantee access to the devinfo device, if open() 98 * is failing with EAGAIN. 99 */ 100 static int 101 blocking_open(const char *path, int oflag) 102 { 103 int fd; 104 105 while ((fd = open(path, oflag)) == -1 && errno == EAGAIN) 106 (void) poll(NULL, 0, 1 * MILLISEC); 107 108 return (fd); 109 } 110 111 /* private interface */ 112 di_node_t 113 di_init_driver(const char *drv_name, uint_t flag) 114 { 115 int fd; 116 char driver[MAXPATHLEN]; 117 118 /* 119 * Don't allow drv_name to exceed MAXPATHLEN - 1, or 1023, 120 * which should be sufficient for any sensible programmer. 121 */ 122 if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) { 123 errno = EINVAL; 124 return (DI_NODE_NIL); 125 } 126 (void) strcpy(driver, drv_name); 127 128 /* 129 * open the devinfo driver 130 */ 131 if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo", 132 O_RDONLY)) == -1) { 133 DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno)); 134 return (DI_NODE_NIL); 135 } 136 137 if (ioctl(fd, DINFOLODRV, driver) != 0) { 138 DPRINTF((DI_ERR, "failed to load driver %s\n", driver)); 139 (void) close(fd); 140 errno = ENXIO; 141 return (DI_NODE_NIL); 142 } 143 (void) close(fd); 144 145 /* 146 * Driver load succeeded, return a snapshot 147 */ 148 return (di_init("/", flag)); 149 } 150 151 di_node_t 152 di_init_impl(const char *phys_path, uint_t flag, 153 struct di_priv_data *priv) 154 { 155 caddr_t pa; 156 int fd, map_size; 157 struct di_all *dap; 158 struct dinfo_io dinfo_io; 159 160 uint_t pageoffset = sysconf(_SC_PAGESIZE) - 1; 161 uint_t pagemask = ~pageoffset; 162 163 DPRINTF((DI_INFO, "di_init: taking a snapshot\n")); 164 165 /* 166 * Make sure there is no minor name in the path 167 * and the path do not start with /devices.... 168 */ 169 if (strchr(phys_path, ':') || 170 (strncmp(phys_path, "/devices", 8) == 0) || 171 (strlen(phys_path) > MAXPATHLEN)) { 172 errno = EINVAL; 173 return (DI_NODE_NIL); 174 } 175 176 if (strlen(phys_path) == 0) 177 (void) sprintf(dinfo_io.root_path, "/"); 178 else if (*phys_path != '/') 179 (void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path), 180 "/%s", phys_path); 181 else 182 (void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path), 183 "%s", phys_path); 184 185 /* 186 * If private data is requested, copy the format specification 187 */ 188 if (flag & DINFOPRIVDATA & 0xff) { 189 if (priv) 190 bcopy(priv, &dinfo_io.priv, 191 sizeof (struct di_priv_data)); 192 else { 193 errno = EINVAL; 194 return (DI_NODE_NIL); 195 } 196 } 197 198 /* 199 * Attempt to open the devinfo driver. Make a second attempt at the 200 * read-only minor node if we don't have privileges to open the full 201 * version _and_ if we're not requesting operations that the read-only 202 * node can't perform. (Setgid processes would fail an access() test, 203 * of course.) 204 */ 205 if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo", 206 O_RDONLY)) == -1) { 207 if ((flag & DINFOFORCE) == DINFOFORCE || 208 (flag & DINFOPRIVDATA) == DINFOPRIVDATA) { 209 /* 210 * We wanted to perform a privileged operation, but the 211 * privileged node isn't available. Don't modify errno 212 * on our way out (but display it if we're running with 213 * di_debug set). 214 */ 215 DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", 216 errno)); 217 return (DI_NODE_NIL); 218 } 219 220 if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo,ro", 221 O_RDONLY)) == -1) { 222 DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", 223 errno)); 224 return (DI_NODE_NIL); 225 } 226 } 227 228 /* 229 * Verify that there is no major conflict, i.e., we are indeed opening 230 * the devinfo driver. 231 */ 232 if (ioctl(fd, DINFOIDENT, NULL) != DI_MAGIC) { 233 DPRINTF((DI_ERR, 234 "driver ID failed; check for major conflict\n")); 235 (void) close(fd); 236 return (DI_NODE_NIL); 237 } 238 239 /* 240 * create snapshot 241 */ 242 if ((map_size = ioctl(fd, flag, &dinfo_io)) < 0) { 243 DPRINTF((DI_ERR, "devinfo ioctl failed with " 244 "error: %d\n", errno)); 245 (void) close(fd); 246 return (DI_NODE_NIL); 247 } else if (map_size == 0) { 248 DPRINTF((DI_ERR, "%s not found\n", phys_path)); 249 errno = ENXIO; 250 (void) close(fd); 251 return (DI_NODE_NIL); 252 } 253 254 /* 255 * copy snapshot to userland 256 */ 257 map_size = (map_size + pageoffset) & pagemask; 258 if ((pa = valloc(map_size)) == NULL) { 259 DPRINTF((DI_ERR, "valloc failed for snapshot\n")); 260 (void) close(fd); 261 return (DI_NODE_NIL); 262 } 263 264 if (ioctl(fd, DINFOUSRLD, pa) != map_size) { 265 DPRINTF((DI_ERR, "failed to copy snapshot to usrld\n")); 266 (void) close(fd); 267 free(pa); 268 errno = EFAULT; 269 return (DI_NODE_NIL); 270 } 271 272 (void) close(fd); 273 274 dap = DI_ALL(pa); 275 if (dap->top_devinfo == 0) { /* phys_path not found */ 276 DPRINTF((DI_ERR, "%s not found\n", phys_path)); 277 free(pa); 278 errno = EINVAL; 279 return (DI_NODE_NIL); 280 } 281 282 return (DI_NODE(pa + dap->top_devinfo)); 283 } 284 285 void 286 di_fini(di_node_t root) 287 { 288 caddr_t pa; /* starting address of map */ 289 290 DPRINTF((DI_INFO, "di_fini: freeing a snapshot\n")); 291 292 /* 293 * paranoid checking 294 */ 295 if (root == DI_NODE_NIL) { 296 DPRINTF((DI_ERR, "di_fini called with NIL arg\n")); 297 return; 298 } 299 300 /* 301 * The root contains its own offset--self. 302 * Subtracting it from root address, we get the starting addr. 303 * The map_size is stored at the beginning of snapshot. 304 * Once we have starting address and size, we can free(). 305 */ 306 pa = (caddr_t)root - DI_NODE(root)->self; 307 308 free(pa); 309 } 310 311 di_node_t 312 di_parent_node(di_node_t node) 313 { 314 caddr_t pa; /* starting address of map */ 315 316 if (node == DI_NODE_NIL) { 317 errno = EINVAL; 318 return (DI_NODE_NIL); 319 } 320 321 DPRINTF((DI_TRACE, "Get parent of node %s\n", di_node_name(node))); 322 323 pa = (caddr_t)node - DI_NODE(node)->self; 324 325 if (DI_NODE(node)->parent) { 326 return (DI_NODE(pa + DI_NODE(node)->parent)); 327 } 328 329 /* 330 * Deal with error condition: 331 * If parent doesn't exist and node is not the root, 332 * set errno to ENOTSUP. Otherwise, set errno to ENXIO. 333 */ 334 if (strcmp(DI_ALL(pa)->root_path, "/") != 0) 335 errno = ENOTSUP; 336 else 337 errno = ENXIO; 338 339 return (DI_NODE_NIL); 340 } 341 342 di_node_t 343 di_sibling_node(di_node_t node) 344 { 345 caddr_t pa; /* starting address of map */ 346 347 if (node == DI_NODE_NIL) { 348 errno = EINVAL; 349 return (DI_NODE_NIL); 350 } 351 352 DPRINTF((DI_TRACE, "Get sibling of node %s\n", di_node_name(node))); 353 354 pa = (caddr_t)node - DI_NODE(node)->self; 355 356 if (DI_NODE(node)->sibling) { 357 return (DI_NODE(pa + DI_NODE(node)->sibling)); 358 } 359 360 /* 361 * Deal with error condition: 362 * Sibling doesn't exist, figure out if ioctl command 363 * has DINFOSUBTREE set. If it doesn't, set errno to 364 * ENOTSUP. 365 */ 366 if (!(DI_ALL(pa)->command & DINFOSUBTREE)) 367 errno = ENOTSUP; 368 else 369 errno = ENXIO; 370 371 return (DI_NODE_NIL); 372 } 373 374 di_node_t 375 di_child_node(di_node_t node) 376 { 377 caddr_t pa; /* starting address of map */ 378 379 DPRINTF((DI_TRACE, "Get child of node %s\n", di_node_name(node))); 380 381 if (node == DI_NODE_NIL) { 382 errno = EINVAL; 383 return (DI_NODE_NIL); 384 } 385 386 pa = (caddr_t)node - DI_NODE(node)->self; 387 388 if (DI_NODE(node)->child) { 389 return (DI_NODE(pa + DI_NODE(node)->child)); 390 } 391 392 /* 393 * Deal with error condition: 394 * Child doesn't exist, figure out if DINFOSUBTREE is set. 395 * If it isn't, set errno to ENOTSUP. 396 */ 397 if (!(DI_ALL(pa)->command & DINFOSUBTREE)) 398 errno = ENOTSUP; 399 else 400 errno = ENXIO; 401 402 return (DI_NODE_NIL); 403 } 404 405 di_node_t 406 di_drv_first_node(const char *drv_name, di_node_t root) 407 { 408 caddr_t pa; /* starting address of map */ 409 int major, devcnt; 410 struct di_devnm *devnm; 411 412 DPRINTF((DI_INFO, "Get first node of driver %s\n", drv_name)); 413 414 if (root == DI_NODE_NIL) { 415 errno = EINVAL; 416 return (DI_NODE_NIL); 417 } 418 419 /* 420 * get major number of driver 421 */ 422 pa = (caddr_t)root - DI_NODE(root)->self; 423 devcnt = DI_ALL(pa)->devcnt; 424 devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); 425 426 for (major = 0; major < devcnt; major++) 427 if (devnm[major].name && (strcmp(drv_name, 428 (char *)(pa + devnm[major].name)) == 0)) 429 break; 430 431 if (major >= devcnt) { 432 errno = EINVAL; 433 return (DI_NODE_NIL); 434 } 435 436 if (!(devnm[major].head)) { 437 errno = ENXIO; 438 return (DI_NODE_NIL); 439 } 440 441 return (DI_NODE(pa + devnm[major].head)); 442 } 443 444 di_node_t 445 di_drv_next_node(di_node_t node) 446 { 447 caddr_t pa; /* starting address of map */ 448 449 if (node == DI_NODE_NIL) { 450 errno = EINVAL; 451 return (DI_NODE_NIL); 452 } 453 454 DPRINTF((DI_TRACE, "next node on per driver list:" 455 " current=%s, driver=%s\n", 456 di_node_name(node), di_driver_name(node))); 457 458 if (DI_NODE(node)->next == (di_off_t)-1) { 459 errno = ENOTSUP; 460 return (DI_NODE_NIL); 461 } 462 463 pa = (caddr_t)node - DI_NODE(node)->self; 464 465 if (DI_NODE(node)->next == NULL) { 466 errno = ENXIO; 467 return (DI_NODE_NIL); 468 } 469 470 return (DI_NODE(pa + DI_NODE(node)->next)); 471 } 472 473 /* 474 * Internal library interfaces: 475 * node_list etc. for node walking 476 */ 477 struct node_list { 478 struct node_list *next; 479 di_node_t node; 480 }; 481 482 static void 483 free_node_list(struct node_list **headp) 484 { 485 struct node_list *tmp; 486 487 while (*headp) { 488 tmp = *headp; 489 *headp = (*headp)->next; 490 free(tmp); 491 } 492 } 493 494 static void 495 append_node_list(struct node_list **headp, struct node_list *list) 496 { 497 struct node_list *tmp; 498 499 if (*headp == NULL) { 500 *headp = list; 501 return; 502 } 503 504 if (list == NULL) /* a minor optimization */ 505 return; 506 507 tmp = *headp; 508 while (tmp->next) 509 tmp = tmp->next; 510 511 tmp->next = list; 512 } 513 514 static void 515 prepend_node_list(struct node_list **headp, struct node_list *list) 516 { 517 struct node_list *tmp; 518 519 if (list == NULL) 520 return; 521 522 tmp = *headp; 523 *headp = list; 524 525 if (tmp == NULL) /* a minor optimization */ 526 return; 527 528 while (list->next) 529 list = list->next; 530 531 list->next = tmp; 532 } 533 534 /* 535 * returns 1 if node is a descendant of parent, 0 otherwise 536 */ 537 static int 538 is_descendant(di_node_t node, di_node_t parent) 539 { 540 /* 541 * DI_NODE_NIL is parent of root, so it is 542 * the parent of all nodes. 543 */ 544 if (parent == DI_NODE_NIL) { 545 return (1); 546 } 547 548 do { 549 node = di_parent_node(node); 550 } while ((node != DI_NODE_NIL) && (node != parent)); 551 552 return (node != DI_NODE_NIL); 553 } 554 555 /* 556 * Insert list before the first node which is NOT a descendent of parent. 557 * This is needed to reproduce the exact walking order of link generators. 558 */ 559 static void 560 insert_node_list(struct node_list **headp, struct node_list *list, 561 di_node_t parent) 562 { 563 struct node_list *tmp, *tmp1; 564 565 if (list == NULL) 566 return; 567 568 tmp = *headp; 569 if (tmp == NULL) { /* a minor optimization */ 570 *headp = list; 571 return; 572 } 573 574 if (!is_descendant(tmp->node, parent)) { 575 prepend_node_list(headp, list); 576 return; 577 } 578 579 /* 580 * Find first node which is not a descendant 581 */ 582 while (tmp->next && is_descendant(tmp->next->node, parent)) { 583 tmp = tmp->next; 584 } 585 586 tmp1 = tmp->next; 587 tmp->next = list; 588 append_node_list(headp, tmp1); 589 } 590 591 /* 592 * Get a linked list of handles of all children 593 */ 594 static struct node_list * 595 get_children(di_node_t node) 596 { 597 di_node_t child; 598 struct node_list *result, *tmp; 599 600 DPRINTF((DI_TRACE1, "Get children of node %s\n", di_node_name(node))); 601 602 if ((child = di_child_node(node)) == DI_NODE_NIL) { 603 return (NULL); 604 } 605 606 if ((result = malloc(sizeof (struct node_list))) == NULL) { 607 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 608 return (NULL); 609 } 610 611 result->node = child; 612 tmp = result; 613 614 while ((child = di_sibling_node(tmp->node)) != DI_NODE_NIL) { 615 if ((tmp->next = malloc(sizeof (struct node_list))) == NULL) { 616 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 617 free_node_list(&result); 618 return (NULL); 619 } 620 tmp = tmp->next; 621 tmp->node = child; 622 } 623 624 tmp->next = NULL; 625 626 return (result); 627 } 628 629 /* 630 * Internal library interface: 631 * Delete all siblings of the first node from the node_list, along with 632 * the first node itself. 633 */ 634 static void 635 prune_sib(struct node_list **headp) 636 { 637 di_node_t parent, curr_par, curr_gpar; 638 struct node_list *curr, *prev; 639 640 /* 641 * get handle to parent of first node 642 */ 643 if ((parent = di_parent_node((*headp)->node)) == DI_NODE_NIL) { 644 /* 645 * This must be the root of the snapshot, so can't 646 * have any siblings. 647 * 648 * XXX Put a check here just in case. 649 */ 650 if ((*headp)->next) 651 DPRINTF((DI_ERR, "Unexpected err in di_walk_node.\n")); 652 653 free(*headp); 654 *headp = NULL; 655 return; 656 } 657 658 /* 659 * To be complete, we should also delete the children 660 * of siblings that have already been visited. 661 * This happens for DI_WALK_SIBFIRST when the first node 662 * is NOT the first in the linked list of siblings. 663 * 664 * Hence, we compare parent with BOTH the parent and grandparent 665 * of nodes, and delete node is a match is found. 666 */ 667 prev = *headp; 668 curr = prev->next; 669 while (curr) { 670 if (((curr_par = di_parent_node(curr->node)) != DI_NODE_NIL) && 671 ((curr_par == parent) || ((curr_gpar = 672 di_parent_node(curr_par)) != DI_NODE_NIL) && 673 (curr_gpar == parent))) { 674 /* 675 * match parent/grandparent: delete curr 676 */ 677 prev->next = curr->next; 678 free(curr); 679 curr = prev->next; 680 } else 681 curr = curr->next; 682 } 683 684 /* 685 * delete the first node 686 */ 687 curr = *headp; 688 *headp = curr->next; 689 free(curr); 690 } 691 692 /* 693 * Internal library function: 694 * Update node list based on action (return code from callback) 695 * and flag specifying walking behavior. 696 */ 697 static void 698 update_node_list(int action, uint_t flag, struct node_list **headp) 699 { 700 struct node_list *children, *tmp; 701 di_node_t parent = di_parent_node((*headp)->node); 702 703 switch (action) { 704 case DI_WALK_TERMINATE: 705 /* 706 * free the node list and be done 707 */ 708 children = NULL; 709 free_node_list(headp); 710 break; 711 712 case DI_WALK_PRUNESIB: 713 /* 714 * Get list of children and prune siblings 715 */ 716 children = get_children((*headp)->node); 717 prune_sib(headp); 718 break; 719 720 case DI_WALK_PRUNECHILD: 721 /* 722 * Set children to NULL and pop first node 723 */ 724 children = NULL; 725 tmp = *headp; 726 *headp = tmp->next; 727 free(tmp); 728 break; 729 730 case DI_WALK_CONTINUE: 731 default: 732 /* 733 * Get list of children and pop first node 734 */ 735 children = get_children((*headp)->node); 736 tmp = *headp; 737 *headp = tmp->next; 738 free(tmp); 739 break; 740 } 741 742 /* 743 * insert the list of children 744 */ 745 switch (flag) { 746 case DI_WALK_CLDFIRST: 747 prepend_node_list(headp, children); 748 break; 749 750 case DI_WALK_SIBFIRST: 751 append_node_list(headp, children); 752 break; 753 754 case DI_WALK_LINKGEN: 755 default: 756 insert_node_list(headp, children, parent); 757 break; 758 } 759 } 760 761 /* 762 * Internal library function: 763 * Invoke callback on one node and update the list of nodes to be walked 764 * based on the flag and return code. 765 */ 766 static void 767 walk_one_node(struct node_list **headp, uint_t flag, void *arg, 768 int (*callback)(di_node_t, void *)) 769 { 770 DPRINTF((DI_TRACE, "Walking node %s\n", di_node_name((*headp)->node))); 771 772 update_node_list(callback((*headp)->node, arg), 773 flag & DI_WALK_MASK, headp); 774 } 775 776 int 777 di_walk_node(di_node_t root, uint_t flag, void *arg, 778 int (*node_callback)(di_node_t, void *)) 779 { 780 struct node_list *head; /* node_list for tree walk */ 781 782 if (root == NULL) { 783 errno = EINVAL; 784 return (-1); 785 } 786 787 if ((head = malloc(sizeof (struct node_list))) == NULL) { 788 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 789 return (-1); 790 } 791 792 head->next = NULL; 793 head->node = root; 794 795 DPRINTF((DI_INFO, "Start node walking from node %s\n", 796 di_node_name(root))); 797 798 while (head != NULL) 799 walk_one_node(&head, flag, arg, node_callback); 800 801 return (0); 802 } 803 804 /* 805 * Internal library function: 806 * Invoke callback for each minor on the minor list of first node 807 * on node_list headp, and place children of first node on the list. 808 * 809 * This is similar to walk_one_node, except we only walk in child 810 * first mode. 811 */ 812 static void 813 walk_one_minor_list(struct node_list **headp, const char *desired_type, 814 uint_t flag, void *arg, int (*callback)(di_node_t, di_minor_t, void *)) 815 { 816 int ddm_type; 817 int action = DI_WALK_CONTINUE; 818 char *node_type; 819 di_minor_t minor = DI_MINOR_NIL; 820 di_node_t node = (*headp)->node; 821 822 while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 823 ddm_type = di_minor_type(minor); 824 825 if ((ddm_type == DDM_ALIAS) && !(flag & DI_CHECK_ALIAS)) 826 continue; 827 828 if ((ddm_type == DDM_INTERNAL_PATH) && 829 !(flag & DI_CHECK_INTERNAL_PATH)) 830 continue; 831 832 node_type = di_minor_nodetype(minor); 833 if ((desired_type != NULL) && ((node_type == NULL) || 834 strncmp(desired_type, node_type, strlen(desired_type)) 835 != 0)) 836 continue; 837 838 if ((action = callback(node, minor, arg)) == 839 DI_WALK_TERMINATE) { 840 break; 841 } 842 } 843 844 update_node_list(action, DI_WALK_LINKGEN, headp); 845 } 846 847 int 848 di_walk_minor(di_node_t root, const char *minor_type, uint_t flag, void *arg, 849 int (*minor_callback)(di_node_t, di_minor_t, void *)) 850 { 851 struct node_list *head; /* node_list for tree walk */ 852 853 #ifdef DEBUG 854 char *path = di_devfs_path(root); 855 DPRINTF((DI_INFO, "walking minor nodes under %s\n", path)); 856 di_devfs_path_free(path); 857 #endif 858 859 if (root == NULL) { 860 errno = EINVAL; 861 return (-1); 862 } 863 864 if ((head = malloc(sizeof (struct node_list))) == NULL) { 865 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 866 return (-1); 867 } 868 869 head->next = NULL; 870 head->node = root; 871 872 DPRINTF((DI_INFO, "Start minor walking from node %s\n", 873 di_node_name(root))); 874 875 while (head != NULL) 876 walk_one_minor_list(&head, minor_type, flag, arg, 877 minor_callback); 878 879 return (0); 880 } 881 882 /* 883 * generic node parameters 884 * Calling these routines always succeeds. 885 */ 886 char * 887 di_node_name(di_node_t node) 888 { 889 return ((caddr_t)node + DI_NODE(node)->node_name - DI_NODE(node)->self); 890 } 891 892 /* returns NULL ptr or a valid ptr to non-NULL string */ 893 char * 894 di_bus_addr(di_node_t node) 895 { 896 caddr_t pa = (caddr_t)node - DI_NODE(node)->self; 897 898 if (DI_NODE(node)->address == 0) 899 return (NULL); 900 901 return ((char *)(pa + DI_NODE(node)->address)); 902 } 903 904 char * 905 di_binding_name(di_node_t node) 906 { 907 caddr_t pa = (caddr_t)node - DI_NODE(node)->self; 908 909 if (DI_NODE(node)->bind_name == 0) 910 return (NULL); 911 912 return ((char *)(pa + DI_NODE(node)->bind_name)); 913 } 914 915 int 916 di_compatible_names(di_node_t node, char **names) 917 { 918 char *c; 919 int len, size, entries = 0; 920 921 if (DI_NODE(node)->compat_names == 0) { 922 *names = NULL; 923 return (0); 924 } 925 926 *names = (caddr_t)node + 927 DI_NODE(node)->compat_names - DI_NODE(node)->self; 928 929 c = *names; 930 len = DI_NODE(node)->compat_length; 931 while (len > 0) { 932 entries++; 933 size = strlen(c) + 1; 934 len -= size; 935 c += size; 936 } 937 938 return (entries); 939 } 940 941 int 942 di_instance(di_node_t node) 943 { 944 return (DI_NODE(node)->instance); 945 } 946 947 /* 948 * XXX: emulate the return value of the old implementation 949 * using info from devi_node_class and devi_node_attributes. 950 */ 951 int 952 di_nodeid(di_node_t node) 953 { 954 if (DI_NODE(node)->node_class == DDI_NC_PROM) 955 return (DI_PROM_NODEID); 956 957 if (DI_NODE(node)->attributes & DDI_PERSISTENT) 958 return (DI_SID_NODEID); 959 960 return (DI_PSEUDO_NODEID); 961 } 962 963 uint_t 964 di_state(di_node_t node) 965 { 966 uint_t result = 0; 967 968 if (di_node_state(node) < DS_ATTACHED) 969 result |= DI_DRIVER_DETACHED; 970 if (DI_NODE(node)->state & DEVI_DEVICE_OFFLINE) 971 result |= DI_DEVICE_OFFLINE; 972 if (DI_NODE(node)->state & DEVI_DEVICE_DOWN) 973 result |= DI_DEVICE_OFFLINE; 974 if (DI_NODE(node)->state & DEVI_BUS_QUIESCED) 975 result |= DI_BUS_QUIESCED; 976 if (DI_NODE(node)->state & DEVI_BUS_DOWN) 977 result |= DI_BUS_DOWN; 978 979 return (result); 980 } 981 982 ddi_node_state_t 983 di_node_state(di_node_t node) 984 { 985 return (DI_NODE(node)->node_state); 986 } 987 988 ddi_devid_t 989 di_devid(di_node_t node) 990 { 991 if (DI_NODE(node)->devid == 0) 992 return (NULL); 993 994 return ((ddi_devid_t)((caddr_t)node + 995 DI_NODE(node)->devid - DI_NODE(node)->self)); 996 } 997 998 int 999 di_driver_major(di_node_t node) 1000 { 1001 int major; 1002 1003 major = DI_NODE(node)->drv_major; 1004 if (major < 0) 1005 return (-1); 1006 return (major); 1007 } 1008 1009 char * 1010 di_driver_name(di_node_t node) 1011 { 1012 int major; 1013 caddr_t pa; 1014 struct di_devnm *devnm; 1015 1016 major = DI_NODE(node)->drv_major; 1017 if (major < 0) 1018 return (NULL); 1019 1020 pa = (caddr_t)node - DI_NODE(node)->self; 1021 devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); 1022 1023 if (devnm[major].name) 1024 return (pa + devnm[major].name); 1025 else 1026 return (NULL); 1027 } 1028 1029 uint_t 1030 di_driver_ops(di_node_t node) 1031 { 1032 int major; 1033 caddr_t pa; 1034 struct di_devnm *devnm; 1035 1036 major = DI_NODE(node)->drv_major; 1037 if (major < 0) 1038 return (0); 1039 1040 pa = (caddr_t)node - DI_NODE(node)->self; 1041 devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames); 1042 1043 return (devnm[major].ops); 1044 } 1045 1046 /* 1047 * returns the length of the path, caller must free memory 1048 */ 1049 char * 1050 di_devfs_path(di_node_t node) 1051 { 1052 caddr_t pa; 1053 di_node_t parent; 1054 int depth = 0, len = 0; 1055 char *buf, *name[MAX_TREE_DEPTH], *addr[MAX_TREE_DEPTH]; 1056 1057 if (node == DI_NODE_NIL) { 1058 errno = EINVAL; 1059 return (NULL); 1060 } 1061 1062 /* 1063 * trace back to root, note the node_name & address 1064 */ 1065 while ((parent = di_parent_node(node)) != DI_NODE_NIL) { 1066 name[depth] = di_node_name(node); 1067 len += strlen(name[depth]) + 1; /* 1 for '/' */ 1068 1069 if ((addr[depth] = di_bus_addr(node)) != NULL) 1070 len += strlen(addr[depth]) + 1; /* 1 for '@' */ 1071 1072 node = parent; 1073 depth++; 1074 } 1075 1076 /* 1077 * get the path to the root of snapshot 1078 */ 1079 pa = (caddr_t)node - DI_NODE(node)->self; 1080 name[depth] = DI_ALL(pa)->root_path; 1081 len += strlen(name[depth]) + 1; 1082 1083 /* 1084 * allocate buffer and assemble path 1085 */ 1086 if ((buf = malloc(len)) == NULL) { 1087 return (NULL); 1088 } 1089 1090 (void) strcpy(buf, name[depth]); 1091 len = strlen(buf); 1092 if (buf[len - 1] == '/') 1093 len--; /* delete trailing '/' */ 1094 1095 while (depth) { 1096 depth--; 1097 buf[len] = '/'; 1098 (void) strcpy(buf + len + 1, name[depth]); 1099 len += strlen(name[depth]) + 1; 1100 if (addr[depth] && addr[depth][0] != '\0') { 1101 buf[len] = '@'; 1102 (void) strcpy(buf + len + 1, addr[depth]); 1103 len += strlen(addr[depth]) + 1; 1104 } 1105 } 1106 1107 return (buf); 1108 } 1109 1110 char * 1111 di_devfs_minor_path(di_minor_t minor) 1112 { 1113 di_node_t node; 1114 char *full_path, *name, *path; 1115 int full_path_len; 1116 1117 if (minor == DI_MINOR_NIL) { 1118 errno = EINVAL; 1119 return (NULL); 1120 } 1121 1122 name = di_minor_name(minor); 1123 node = di_minor_devinfo(minor); 1124 path = di_devfs_path(node); 1125 if (path == NULL) 1126 return (NULL); 1127 1128 /* make the full path to the device minor node */ 1129 full_path_len = strlen(path) + strlen(name) + 2; 1130 full_path = (char *)calloc(1, full_path_len); 1131 if (full_path != NULL) 1132 (void) snprintf(full_path, full_path_len, "%s:%s", path, name); 1133 1134 di_devfs_path_free(path); 1135 return (full_path); 1136 } 1137 1138 void 1139 di_devfs_path_free(char *buf) 1140 { 1141 if (buf == NULL) { 1142 DPRINTF((DI_ERR, "di_devfs_path_free NULL arg!\n")); 1143 return; 1144 } 1145 1146 free(buf); 1147 } 1148 1149 /* minor data access */ 1150 di_minor_t 1151 di_minor_next(di_node_t node, di_minor_t minor) 1152 { 1153 caddr_t pa; 1154 1155 /* 1156 * paranoid error checking 1157 */ 1158 if (node == DI_NODE_NIL) { 1159 errno = EINVAL; 1160 return (DI_MINOR_NIL); 1161 } 1162 1163 /* 1164 * minor is not NIL 1165 */ 1166 if (minor != DI_MINOR_NIL) { 1167 if (DI_MINOR(minor)->next != 0) 1168 return ((di_minor_t)((void *)((caddr_t)minor - 1169 DI_MINOR(minor)->self + DI_MINOR(minor)->next))); 1170 else { 1171 errno = ENXIO; 1172 return (DI_MINOR_NIL); 1173 } 1174 } 1175 1176 /* 1177 * minor is NIL-->caller asks for first minor node 1178 */ 1179 if (DI_NODE(node)->minor_data != 0) { 1180 return (DI_MINOR((caddr_t)node - DI_NODE(node)->self + 1181 DI_NODE(node)->minor_data)); 1182 } 1183 1184 /* 1185 * no minor data-->check if snapshot includes minor data 1186 * in order to set the correct errno 1187 */ 1188 pa = (caddr_t)node - DI_NODE(node)->self; 1189 if (DINFOMINOR & DI_ALL(pa)->command) 1190 errno = ENXIO; 1191 else 1192 errno = ENOTSUP; 1193 1194 return (DI_MINOR_NIL); 1195 } 1196 1197 /* private interface for dealing with alias minor link generation */ 1198 di_node_t 1199 di_minor_devinfo(di_minor_t minor) 1200 { 1201 if (minor == DI_MINOR_NIL) { 1202 errno = EINVAL; 1203 return (DI_NODE_NIL); 1204 } 1205 1206 return (DI_NODE((caddr_t)minor - DI_MINOR(minor)->self + 1207 DI_MINOR(minor)->node)); 1208 } 1209 1210 ddi_minor_type 1211 di_minor_type(di_minor_t minor) 1212 { 1213 return (DI_MINOR(minor)->type); 1214 } 1215 1216 char * 1217 di_minor_name(di_minor_t minor) 1218 { 1219 if (DI_MINOR(minor)->name == 0) 1220 return (NULL); 1221 1222 return ((caddr_t)minor - DI_MINOR(minor)->self + DI_MINOR(minor)->name); 1223 } 1224 1225 dev_t 1226 di_minor_devt(di_minor_t minor) 1227 { 1228 return (makedev(DI_MINOR(minor)->dev_major, 1229 DI_MINOR(minor)->dev_minor)); 1230 } 1231 1232 int 1233 di_minor_spectype(di_minor_t minor) 1234 { 1235 return (DI_MINOR(minor)->spec_type); 1236 } 1237 1238 char * 1239 di_minor_nodetype(di_minor_t minor) 1240 { 1241 if (DI_MINOR(minor)->node_type == 0) 1242 return (NULL); 1243 1244 return ((caddr_t)minor - 1245 DI_MINOR(minor)->self + DI_MINOR(minor)->node_type); 1246 } 1247 1248 /* 1249 * Single public interface for accessing software properties 1250 */ 1251 di_prop_t 1252 di_prop_next(di_node_t node, di_prop_t prop) 1253 { 1254 int list = DI_PROP_DRV_LIST; 1255 1256 /* 1257 * paranoid check 1258 */ 1259 if (node == DI_NODE_NIL) { 1260 errno = EINVAL; 1261 return (DI_PROP_NIL); 1262 } 1263 1264 /* 1265 * Find which prop list we are at 1266 */ 1267 if (prop != DI_PROP_NIL) 1268 list = DI_PROP(prop)->prop_list; 1269 1270 do { 1271 switch (list++) { 1272 case DI_PROP_DRV_LIST: 1273 prop = di_prop_drv_next(node, prop); 1274 break; 1275 case DI_PROP_SYS_LIST: 1276 prop = di_prop_sys_next(node, prop); 1277 break; 1278 case DI_PROP_GLB_LIST: 1279 prop = di_prop_global_next(node, prop); 1280 break; 1281 case DI_PROP_HW_LIST: 1282 prop = di_prop_hw_next(node, prop); 1283 break; 1284 default: /* shouldn't happen */ 1285 errno = EFAULT; 1286 return (DI_PROP_NIL); 1287 } 1288 } while ((prop == DI_PROP_NIL) && (list <= DI_PROP_HW_LIST)); 1289 1290 return (prop); 1291 } 1292 1293 dev_t 1294 di_prop_devt(di_prop_t prop) 1295 { 1296 return (makedev(DI_PROP(prop)->dev_major, DI_PROP(prop)->dev_minor)); 1297 } 1298 1299 char * 1300 di_prop_name(di_prop_t prop) 1301 { 1302 if (DI_PROP(prop)->prop_name == 0) 1303 return (NULL); 1304 1305 return ((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_name); 1306 } 1307 1308 int 1309 di_prop_type(di_prop_t prop) 1310 { 1311 uint_t flags = DI_PROP(prop)->prop_flags; 1312 1313 if (flags & DDI_PROP_UNDEF_IT) 1314 return (DI_PROP_TYPE_UNDEF_IT); 1315 1316 if (DI_PROP(prop)->prop_len == 0) 1317 return (DI_PROP_TYPE_BOOLEAN); 1318 1319 if ((flags & DDI_PROP_TYPE_MASK) == DDI_PROP_TYPE_ANY) 1320 return (DI_PROP_TYPE_UNKNOWN); 1321 1322 if (flags & DDI_PROP_TYPE_INT) 1323 return (DI_PROP_TYPE_INT); 1324 1325 if (flags & DDI_PROP_TYPE_INT64) 1326 return (DI_PROP_TYPE_INT64); 1327 1328 if (flags & DDI_PROP_TYPE_STRING) 1329 return (DI_PROP_TYPE_STRING); 1330 1331 if (flags & DDI_PROP_TYPE_BYTE) 1332 return (DI_PROP_TYPE_BYTE); 1333 1334 /* 1335 * Shouldn't get here. In case we do, return unknown type. 1336 * 1337 * XXX--When DDI_PROP_TYPE_COMPOSITE is implemented, we need 1338 * to add DI_PROP_TYPE_COMPOSITE. 1339 */ 1340 DPRINTF((DI_ERR, "Unimplemented property type: 0x%x\n", flags)); 1341 1342 return (DI_PROP_TYPE_UNKNOWN); 1343 } 1344 1345 /* 1346 * Extract type-specific values of an property 1347 */ 1348 extern int di_prop_decode_common(void *prop_data, int len, 1349 int ddi_type, int prom); 1350 1351 int 1352 di_prop_ints(di_prop_t prop, int **prop_data) 1353 { 1354 if (DI_PROP(prop)->prop_len == 0) 1355 return (0); /* boolean property */ 1356 1357 if ((DI_PROP(prop)->prop_data == 0) || 1358 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1359 errno = EFAULT; 1360 *prop_data = NULL; 1361 return (-1); 1362 } 1363 1364 *prop_data = (int *)((void *)((caddr_t)prop - DI_PROP(prop)->self 1365 + DI_PROP(prop)->prop_data)); 1366 1367 return (di_prop_decode_common((void *)prop_data, 1368 DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT, 0)); 1369 } 1370 1371 int 1372 di_prop_int64(di_prop_t prop, int64_t **prop_data) 1373 { 1374 if (DI_PROP(prop)->prop_len == 0) 1375 return (0); /* boolean property */ 1376 1377 if ((DI_PROP(prop)->prop_data == 0) || 1378 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1379 errno = EFAULT; 1380 *prop_data = NULL; 1381 return (-1); 1382 } 1383 1384 *prop_data = (int64_t *)((void *)((caddr_t)prop - DI_PROP(prop)->self 1385 + DI_PROP(prop)->prop_data)); 1386 1387 return (di_prop_decode_common((void *)prop_data, 1388 DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0)); 1389 } 1390 1391 int 1392 di_prop_strings(di_prop_t prop, char **prop_data) 1393 { 1394 if (DI_PROP(prop)->prop_len == 0) 1395 return (0); /* boolean property */ 1396 1397 if ((DI_PROP(prop)->prop_data == 0) || 1398 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1399 errno = EFAULT; 1400 *prop_data = NULL; 1401 return (-1); 1402 } 1403 1404 *prop_data = (char *)((caddr_t)prop - DI_PROP(prop)->self 1405 + DI_PROP(prop)->prop_data); 1406 1407 return (di_prop_decode_common((void *)prop_data, 1408 DI_PROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0)); 1409 } 1410 1411 int 1412 di_prop_bytes(di_prop_t prop, uchar_t **prop_data) 1413 { 1414 if (DI_PROP(prop)->prop_len == 0) 1415 return (0); /* boolean property */ 1416 1417 if ((DI_PROP(prop)->prop_data == 0) || 1418 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1419 errno = EFAULT; 1420 *prop_data = NULL; 1421 return (-1); 1422 } 1423 1424 *prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self 1425 + DI_PROP(prop)->prop_data); 1426 1427 return (di_prop_decode_common((void *)prop_data, 1428 DI_PROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0)); 1429 } 1430 1431 /* 1432 * returns 1 for match, 0 for no match 1433 */ 1434 static int 1435 match_prop(di_prop_t prop, dev_t match_dev, const char *name, int type) 1436 { 1437 int prop_type; 1438 1439 #ifdef DEBUG 1440 if (di_prop_name(prop) == NULL) { 1441 DPRINTF((DI_ERR, "libdevinfo: property has no name!\n")); 1442 return (0); 1443 } 1444 #endif /* DEBUG */ 1445 1446 if (strcmp(name, di_prop_name(prop)) != 0) 1447 return (0); 1448 1449 if ((match_dev != DDI_DEV_T_ANY) && (di_prop_devt(prop) != match_dev)) 1450 return (0); 1451 1452 /* 1453 * XXX prop_type is different from DDI_*. See PSARC 1997/127. 1454 */ 1455 prop_type = di_prop_type(prop); 1456 if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type) && 1457 (prop_type != DI_PROP_TYPE_BOOLEAN)) 1458 return (0); 1459 1460 return (1); 1461 } 1462 1463 static di_prop_t 1464 di_prop_search(dev_t match_dev, di_node_t node, const char *name, 1465 int type) 1466 { 1467 di_prop_t prop = DI_PROP_NIL; 1468 1469 /* 1470 * The check on match_dev follows ddi_prop_lookup_common(). 1471 * Other checks are libdevinfo specific implementation. 1472 */ 1473 if ((node == DI_NODE_NIL) || (name == NULL) || (strlen(name) == 0) || 1474 (match_dev == DDI_DEV_T_NONE) || !DI_PROP_TYPE_VALID(type)) { 1475 errno = EINVAL; 1476 return (DI_PROP_NIL); 1477 } 1478 1479 while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) { 1480 DPRINTF((DI_TRACE1, "match prop name %s, devt 0x%lx, type %d\n", 1481 di_prop_name(prop), di_prop_devt(prop), 1482 di_prop_type(prop))); 1483 if (match_prop(prop, match_dev, name, type)) 1484 return (prop); 1485 } 1486 1487 return (DI_PROP_NIL); 1488 } 1489 1490 int 1491 di_prop_lookup_ints(dev_t dev, di_node_t node, const char *prop_name, 1492 int **prop_data) 1493 { 1494 di_prop_t prop; 1495 1496 if ((prop = di_prop_search(dev, node, prop_name, 1497 DI_PROP_TYPE_INT)) == DI_PROP_NIL) 1498 return (-1); 1499 1500 return (di_prop_ints(prop, (void *)prop_data)); 1501 } 1502 1503 int 1504 di_prop_lookup_int64(dev_t dev, di_node_t node, const char *prop_name, 1505 int64_t **prop_data) 1506 { 1507 di_prop_t prop; 1508 1509 if ((prop = di_prop_search(dev, node, prop_name, 1510 DI_PROP_TYPE_INT64)) == DI_PROP_NIL) 1511 return (-1); 1512 1513 return (di_prop_int64(prop, (void *)prop_data)); 1514 } 1515 1516 int 1517 di_prop_lookup_strings(dev_t dev, di_node_t node, const char *prop_name, 1518 char **prop_data) 1519 { 1520 di_prop_t prop; 1521 1522 if ((prop = di_prop_search(dev, node, prop_name, 1523 DI_PROP_TYPE_STRING)) == DI_PROP_NIL) 1524 return (-1); 1525 1526 return (di_prop_strings(prop, (void *)prop_data)); 1527 } 1528 1529 int 1530 di_prop_lookup_bytes(dev_t dev, di_node_t node, const char *prop_name, 1531 uchar_t **prop_data) 1532 { 1533 di_prop_t prop; 1534 1535 if ((prop = di_prop_search(dev, node, prop_name, 1536 DI_PROP_TYPE_BYTE)) == DI_PROP_NIL) 1537 return (-1); 1538 1539 return (di_prop_bytes(prop, (void *)prop_data)); 1540 } 1541 1542 /* 1543 * Consolidation private property access functions 1544 */ 1545 enum prop_type { 1546 PROP_TYPE_DRV, 1547 PROP_TYPE_SYS, 1548 PROP_TYPE_GLOB, 1549 PROP_TYPE_HW 1550 }; 1551 1552 static di_prop_t 1553 di_prop_next_common(di_node_t node, di_prop_t prop, int prop_type) 1554 { 1555 caddr_t pa; 1556 di_off_t prop_off = 0; 1557 1558 if (prop != DI_PROP_NIL) { 1559 if (DI_PROP(prop)->next) { 1560 return (DI_PROP((caddr_t)prop - 1561 DI_PROP(prop)->self + DI_PROP(prop)->next)); 1562 } else { 1563 return (DI_PROP_NIL); 1564 } 1565 } 1566 1567 1568 /* 1569 * prop is NIL, caller asks for first property 1570 */ 1571 pa = (caddr_t)node - DI_NODE(node)->self; 1572 switch (prop_type) { 1573 case PROP_TYPE_DRV: 1574 prop_off = DI_NODE(node)->drv_prop; 1575 break; 1576 case PROP_TYPE_SYS: 1577 prop_off = DI_NODE(node)->sys_prop; 1578 break; 1579 case PROP_TYPE_HW: 1580 prop_off = DI_NODE(node)->hw_prop; 1581 break; 1582 case PROP_TYPE_GLOB: 1583 prop_off = DI_NODE(node)->glob_prop; 1584 if (prop_off == -1) { 1585 /* no global property */ 1586 prop_off = 0; 1587 } else if ((prop_off == 0) && (DI_NODE(node)->drv_major >= 0)) { 1588 /* refer to devnames array */ 1589 struct di_devnm *devnm = DI_DEVNM(pa + 1590 DI_ALL(pa)->devnames + (DI_NODE(node)->drv_major * 1591 sizeof (struct di_devnm))); 1592 prop_off = devnm->global_prop; 1593 } 1594 break; 1595 } 1596 1597 if (prop_off) { 1598 return (DI_PROP(pa + prop_off)); 1599 } 1600 1601 /* 1602 * no prop found. Check the reason for not found 1603 */ 1604 if (DINFOPROP & DI_ALL(pa)->command) 1605 errno = ENXIO; 1606 else 1607 errno = ENOTSUP; 1608 1609 return (DI_PROP_NIL); 1610 } 1611 1612 di_prop_t 1613 di_prop_drv_next(di_node_t node, di_prop_t prop) 1614 { 1615 return (di_prop_next_common(node, prop, PROP_TYPE_DRV)); 1616 } 1617 1618 di_prop_t 1619 di_prop_sys_next(di_node_t node, di_prop_t prop) 1620 { 1621 return (di_prop_next_common(node, prop, PROP_TYPE_SYS)); 1622 } 1623 1624 di_prop_t 1625 di_prop_global_next(di_node_t node, di_prop_t prop) 1626 { 1627 return (di_prop_next_common(node, prop, PROP_TYPE_GLOB)); 1628 } 1629 1630 di_prop_t 1631 di_prop_hw_next(di_node_t node, di_prop_t prop) 1632 { 1633 return (di_prop_next_common(node, prop, PROP_TYPE_HW)); 1634 } 1635 1636 int 1637 di_prop_rawdata(di_prop_t prop, uchar_t **prop_data) 1638 { 1639 #ifdef DEBUG 1640 if (prop == DI_PROP_NIL) { 1641 errno = EINVAL; 1642 return (-1); 1643 } 1644 #endif /* DEBUG */ 1645 1646 if (DI_PROP(prop)->prop_len == 0) { 1647 *prop_data = NULL; 1648 return (0); 1649 } 1650 1651 if ((DI_PROP(prop)->prop_data == 0) || 1652 (DI_PROP(prop)->prop_data == (di_off_t)-1)) { 1653 errno = EFAULT; 1654 *prop_data = NULL; 1655 return (-1); 1656 } 1657 1658 /* 1659 * No memory allocation. 1660 */ 1661 *prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self + 1662 DI_PROP(prop)->prop_data); 1663 1664 return (DI_PROP(prop)->prop_len); 1665 } 1666 1667 /* 1668 * Consolidation private interfaces for accessing I/O multipathing data 1669 */ 1670 di_path_t 1671 di_path_next_client(di_node_t node, di_path_t path) 1672 { 1673 caddr_t pa; 1674 1675 /* 1676 * path is not NIL 1677 */ 1678 if (path != DI_PATH_NIL) { 1679 if (DI_PATH(path)->path_p_link != 0) 1680 return (DI_PATH((void *)((caddr_t)path - 1681 DI_PATH(path)->self + DI_PATH(path)->path_p_link))); 1682 else { 1683 errno = ENXIO; 1684 return (DI_PATH_NIL); 1685 } 1686 } 1687 1688 /* 1689 * Path is NIL; the caller is asking for the first path info node 1690 */ 1691 if (DI_NODE(node)->multipath_phci != 0) { 1692 DPRINTF((DI_INFO, "phci: returning %p\n", ((caddr_t)node - 1693 DI_NODE(node)->self + DI_NODE(node)->multipath_phci))); 1694 return (DI_PATH((caddr_t)node - DI_NODE(node)->self + 1695 DI_NODE(node)->multipath_phci)); 1696 } 1697 1698 /* 1699 * No pathing data; check if the snapshot includes path data in order 1700 * to set errno properly. 1701 */ 1702 pa = (caddr_t)node - DI_NODE(node)->self; 1703 if (DINFOPATH & (DI_ALL(pa)->command)) 1704 errno = ENXIO; 1705 else 1706 errno = ENOTSUP; 1707 1708 return (DI_PATH_NIL); 1709 } 1710 1711 di_path_t 1712 di_path_next_phci(di_node_t node, di_path_t path) 1713 { 1714 caddr_t pa; 1715 1716 /* 1717 * path is not NIL 1718 */ 1719 if (path != DI_PATH_NIL) { 1720 if (DI_PATH(path)->path_c_link != 0) 1721 return (DI_PATH((caddr_t)path - DI_PATH(path)->self 1722 + DI_PATH(path)->path_c_link)); 1723 else { 1724 errno = ENXIO; 1725 return (DI_PATH_NIL); 1726 } 1727 } 1728 1729 /* 1730 * Path is NIL; the caller is asking for the first path info node 1731 */ 1732 if (DI_NODE(node)->multipath_client != 0) { 1733 DPRINTF((DI_INFO, "client: returning %p\n", ((caddr_t)node - 1734 DI_NODE(node)->self + DI_NODE(node)->multipath_client))); 1735 return (DI_PATH((caddr_t)node - DI_NODE(node)->self + 1736 DI_NODE(node)->multipath_client)); 1737 } 1738 1739 /* 1740 * No pathing data; check if the snapshot includes path data in order 1741 * to set errno properly. 1742 */ 1743 pa = (caddr_t)node - DI_NODE(node)->self; 1744 if (DINFOPATH & (DI_ALL(pa)->command)) 1745 errno = ENXIO; 1746 else 1747 errno = ENOTSUP; 1748 1749 return (DI_PATH_NIL); 1750 } 1751 1752 /* 1753 * XXX Obsolete wrapper to be removed. Won't work under multilevel. 1754 */ 1755 di_path_t 1756 di_path_next(di_node_t node, di_path_t path) 1757 { 1758 if (node == DI_NODE_NIL) { 1759 errno = EINVAL; 1760 return (DI_PATH_NIL); 1761 } 1762 1763 if (DI_NODE(node)->multipath_client) { 1764 return (di_path_next_phci(node, path)); 1765 } else if (DI_NODE(node)->multipath_phci) { 1766 return (di_path_next_client(node, path)); 1767 } else { 1768 /* 1769 * The node had multipathing data but didn't appear to be a 1770 * phci *or* a client; probably a programmer error. 1771 */ 1772 errno = EINVAL; 1773 return (DI_PATH_NIL); 1774 } 1775 } 1776 1777 di_path_state_t 1778 di_path_state(di_path_t path) 1779 { 1780 return ((di_path_state_t)DI_PATH(path)->path_state); 1781 } 1782 1783 char * 1784 di_path_addr(di_path_t path, char *buf) 1785 { 1786 caddr_t pa; /* starting address of map */ 1787 1788 pa = (caddr_t)path - DI_PATH(path)->self; 1789 1790 (void) strncpy(buf, (char *)(pa + DI_PATH(path)->path_addr), 1791 MAXPATHLEN); 1792 return (buf); 1793 } 1794 1795 di_node_t 1796 di_path_client_node(di_path_t path) 1797 { 1798 caddr_t pa; /* starting address of map */ 1799 1800 if (path == DI_PATH_NIL) { 1801 errno = EINVAL; 1802 return (DI_PATH_NIL); 1803 } 1804 1805 DPRINTF((DI_TRACE, "Get client node for path %p\n", path)); 1806 1807 pa = (caddr_t)path - DI_PATH(path)->self; 1808 1809 if (DI_PATH(path)->path_client) { 1810 return (DI_NODE(pa + DI_PATH(path)->path_client)); 1811 } 1812 1813 /* 1814 * Deal with error condition: 1815 * If parent doesn't exist and node is not the root, 1816 * set errno to ENOTSUP. Otherwise, set errno to ENXIO. 1817 */ 1818 if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOCLIENT) == 0) 1819 errno = ENOTSUP; 1820 else 1821 errno = ENXIO; 1822 1823 return (DI_NODE_NIL); 1824 } 1825 1826 di_node_t 1827 di_path_phci_node(di_path_t path) 1828 { 1829 caddr_t pa; /* starting address of map */ 1830 1831 if (path == DI_PATH_NIL) { 1832 errno = EINVAL; 1833 return (DI_PATH_NIL); 1834 } 1835 1836 DPRINTF((DI_TRACE, "Get phci node for path %p\n", path)); 1837 1838 pa = (caddr_t)path - DI_PATH(path)->self; 1839 1840 if (DI_PATH(path)->path_phci) { 1841 return (DI_NODE(pa + DI_PATH(path)->path_phci)); 1842 } 1843 1844 /* 1845 * Deal with error condition: 1846 * If parent doesn't exist and node is not the root, 1847 * set errno to ENOTSUP. Otherwise, set errno to ENXIO. 1848 */ 1849 if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOPHCI) == 0) 1850 errno = ENOTSUP; 1851 else 1852 errno = ENXIO; 1853 1854 return (DI_NODE_NIL); 1855 } 1856 1857 di_path_prop_t 1858 di_path_prop_next(di_path_t path, di_path_prop_t prop) 1859 { 1860 caddr_t pa; 1861 1862 if (path == DI_PATH_NIL) { 1863 errno = EINVAL; 1864 return (DI_PROP_NIL); 1865 } 1866 1867 /* 1868 * prop is not NIL 1869 */ 1870 if (prop != DI_PROP_NIL) { 1871 if (DI_PROP(prop)->next != 0) 1872 return (DI_PATHPROP((caddr_t)prop - 1873 DI_PROP(prop)->self + DI_PROP(prop)->next)); 1874 else { 1875 errno = ENXIO; 1876 return (DI_PROP_NIL); 1877 } 1878 } 1879 1880 /* 1881 * prop is NIL-->caller asks for first property 1882 */ 1883 pa = (caddr_t)path - DI_PATH(path)->self; 1884 if (DI_PATH(path)->path_prop != 0) { 1885 return (DI_PATHPROP(pa + DI_PATH(path)->path_prop)); 1886 } 1887 1888 /* 1889 * no property data-->check if snapshot includes props 1890 * in order to set the correct errno 1891 */ 1892 if (DINFOPROP & (DI_ALL(pa)->command)) 1893 errno = ENXIO; 1894 else 1895 errno = ENOTSUP; 1896 1897 return (DI_PROP_NIL); 1898 } 1899 1900 char * 1901 di_path_prop_name(di_path_prop_t prop) 1902 { 1903 caddr_t pa; /* starting address of map */ 1904 pa = (caddr_t)prop - DI_PATHPROP(prop)->self; 1905 return ((char *)(pa + DI_PATHPROP(prop)->prop_name)); 1906 } 1907 1908 int 1909 di_path_prop_len(di_path_prop_t prop) 1910 { 1911 return (DI_PATHPROP(prop)->prop_len); 1912 } 1913 1914 int 1915 di_path_prop_type(di_path_prop_t prop) 1916 { 1917 switch (DI_PATHPROP(prop)->prop_type) { 1918 case DDI_PROP_TYPE_INT: 1919 return (DI_PROP_TYPE_INT); 1920 case DDI_PROP_TYPE_INT64: 1921 return (DI_PROP_TYPE_INT64); 1922 case DDI_PROP_TYPE_BYTE: 1923 return (DI_PROP_TYPE_BYTE); 1924 case DDI_PROP_TYPE_STRING: 1925 return (DI_PROP_TYPE_STRING); 1926 } 1927 return (DI_PROP_TYPE_UNKNOWN); 1928 } 1929 1930 int 1931 di_path_prop_bytes(di_path_prop_t prop, uchar_t **prop_data) 1932 { 1933 if ((DI_PATHPROP(prop)->prop_data == 0) || 1934 (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { 1935 errno = EFAULT; 1936 *prop_data = NULL; 1937 return (-1); 1938 } 1939 1940 *prop_data = (uchar_t *)((caddr_t)prop - DI_PATHPROP(prop)->self 1941 + DI_PATHPROP(prop)->prop_data); 1942 1943 return (di_prop_decode_common((void *)prop_data, 1944 DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0)); 1945 } 1946 1947 int 1948 di_path_prop_ints(di_path_prop_t prop, int **prop_data) 1949 { 1950 if (DI_PATHPROP(prop)->prop_len == 0) 1951 return (0); 1952 1953 if ((DI_PATHPROP(prop)->prop_data == 0) || 1954 (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { 1955 errno = EFAULT; 1956 *prop_data = NULL; 1957 return (-1); 1958 } 1959 1960 *prop_data = (int *)((void *)((caddr_t)prop - DI_PATHPROP(prop)->self 1961 + DI_PATHPROP(prop)->prop_data)); 1962 1963 return (di_prop_decode_common((void *)prop_data, 1964 DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT, 0)); 1965 } 1966 1967 int 1968 di_path_prop_int64s(di_path_prop_t prop, int64_t **prop_data) 1969 { 1970 if (DI_PATHPROP(prop)->prop_len == 0) 1971 return (0); 1972 1973 if ((DI_PATHPROP(prop)->prop_data == 0) || 1974 (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { 1975 errno = EFAULT; 1976 *prop_data = NULL; 1977 return (-1); 1978 } 1979 1980 *prop_data = (int64_t *)((void *)((caddr_t)prop - 1981 DI_PATHPROP(prop)->self + DI_PATHPROP(prop)->prop_data)); 1982 1983 return (di_prop_decode_common((void *)prop_data, 1984 DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0)); 1985 } 1986 1987 int 1988 di_path_prop_strings(di_path_prop_t prop, char **prop_data) 1989 { 1990 if (DI_PATHPROP(prop)->prop_len == 0) 1991 return (0); 1992 1993 if ((DI_PATHPROP(prop)->prop_data == 0) || 1994 (DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) { 1995 errno = EFAULT; 1996 *prop_data = NULL; 1997 return (-1); 1998 } 1999 2000 *prop_data = (char *)((caddr_t)prop - DI_PATHPROP(prop)->self 2001 + DI_PATHPROP(prop)->prop_data); 2002 2003 return (di_prop_decode_common((void *)prop_data, 2004 DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0)); 2005 } 2006 2007 static di_path_prop_t 2008 di_path_prop_search(di_path_t path, const char *name, int type) 2009 { 2010 di_path_prop_t prop = DI_PROP_NIL; 2011 2012 /* 2013 * Sanity check arguments 2014 */ 2015 if ((path == DI_PATH_NIL) || (name == NULL) || (strlen(name) == 0) || 2016 !DI_PROP_TYPE_VALID(type)) { 2017 errno = EINVAL; 2018 return (DI_PROP_NIL); 2019 } 2020 2021 while ((prop = di_path_prop_next(path, prop)) != DI_PROP_NIL) { 2022 int prop_type = di_path_prop_type(prop); 2023 2024 DPRINTF((DI_TRACE1, "match path prop name %s, type %d\n", 2025 di_path_prop_name(prop), prop_type)); 2026 2027 if (strcmp(name, di_path_prop_name(prop)) != 0) 2028 continue; 2029 2030 if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type)) 2031 continue; 2032 2033 return (prop); 2034 } 2035 2036 return (DI_PROP_NIL); 2037 } 2038 2039 int 2040 di_path_prop_lookup_bytes(di_path_t path, const char *prop_name, 2041 uchar_t **prop_data) 2042 { 2043 di_path_prop_t prop; 2044 2045 if ((prop = di_path_prop_search(path, prop_name, 2046 DI_PROP_TYPE_BYTE)) == DI_PROP_NIL) 2047 return (-1); 2048 2049 return (di_path_prop_bytes(prop, prop_data)); 2050 } 2051 2052 int 2053 di_path_prop_lookup_ints(di_path_t path, const char *prop_name, 2054 int **prop_data) 2055 { 2056 di_path_prop_t prop; 2057 2058 if ((prop = di_path_prop_search(path, prop_name, 2059 DI_PROP_TYPE_INT)) == DI_PROP_NIL) 2060 return (-1); 2061 2062 return (di_path_prop_ints(prop, prop_data)); 2063 } 2064 2065 int 2066 di_path_prop_lookup_int64s(di_path_t path, const char *prop_name, 2067 int64_t **prop_data) 2068 { 2069 di_path_prop_t prop; 2070 2071 if ((prop = di_path_prop_search(path, prop_name, 2072 DI_PROP_TYPE_INT64)) == DI_PROP_NIL) 2073 return (-1); 2074 2075 return (di_path_prop_int64s(prop, prop_data)); 2076 } 2077 2078 int di_path_prop_lookup_strings(di_path_t path, const char *prop_name, 2079 char **prop_data) 2080 { 2081 di_path_prop_t prop; 2082 2083 if ((prop = di_path_prop_search(path, prop_name, 2084 DI_PROP_TYPE_STRING)) == DI_PROP_NIL) 2085 return (-1); 2086 2087 return (di_path_prop_strings(prop, prop_data)); 2088 } 2089 2090 2091 /* 2092 * Consolidation private interfaces for private data 2093 */ 2094 void * 2095 di_parent_private_data(di_node_t node) 2096 { 2097 caddr_t pa; 2098 2099 if (DI_NODE(node)->parent_data == 0) { 2100 errno = ENXIO; 2101 return (NULL); 2102 } 2103 2104 if (DI_NODE(node)->parent_data == (di_off_t)-1) { 2105 /* 2106 * Private data requested, but not obtained due to a memory 2107 * error (e.g. wrong format specified) 2108 */ 2109 errno = EFAULT; 2110 return (NULL); 2111 } 2112 2113 pa = (caddr_t)node - DI_NODE(node)->self; 2114 if (DI_NODE(node)->parent_data) 2115 return (pa + DI_NODE(node)->parent_data); 2116 2117 if (DI_ALL(pa)->command & DINFOPRIVDATA) 2118 errno = ENXIO; 2119 else 2120 errno = ENOTSUP; 2121 2122 return (NULL); 2123 } 2124 2125 void * 2126 di_driver_private_data(di_node_t node) 2127 { 2128 caddr_t pa; 2129 2130 if (DI_NODE(node)->driver_data == 0) { 2131 errno = ENXIO; 2132 return (NULL); 2133 } 2134 2135 if (DI_NODE(node)->driver_data == (di_off_t)-1) { 2136 /* 2137 * Private data requested, but not obtained due to a memory 2138 * error (e.g. wrong format specified) 2139 */ 2140 errno = EFAULT; 2141 return (NULL); 2142 } 2143 2144 pa = (caddr_t)node - DI_NODE(node)->self; 2145 if (DI_NODE(node)->driver_data) 2146 return (pa + DI_NODE(node)->driver_data); 2147 2148 if (DI_ALL(pa)->command & DINFOPRIVDATA) 2149 errno = ENXIO; 2150 else 2151 errno = ENOTSUP; 2152 2153 return (NULL); 2154 } 2155 2156 /* 2157 * PROM property access 2158 */ 2159 2160 /* 2161 * openprom driver stuff: 2162 * The maximum property length depends on the buffer size. We use 2163 * OPROMMAXPARAM defined in <sys/openpromio.h> 2164 * 2165 * MAXNAMESZ is max property name. obpdefs.h defines it as 32 based on 1275 2166 * MAXVALSZ is maximum value size, which is whatever space left in buf 2167 */ 2168 2169 #define OBP_MAXBUF OPROMMAXPARAM - sizeof (int) 2170 #define OBP_MAXPROPLEN OBP_MAXBUF - OBP_MAXPROPNAME; 2171 2172 struct di_prom_prop { 2173 char *name; 2174 int len; 2175 uchar_t *data; 2176 struct di_prom_prop *next; /* form a linked list */ 2177 }; 2178 2179 struct di_prom_handle { /* handle to prom */ 2180 mutex_t lock; /* synchronize access to openprom fd */ 2181 int fd; /* /dev/openprom file descriptor */ 2182 struct di_prom_prop *list; /* linked list of prop */ 2183 union { 2184 char buf[OPROMMAXPARAM]; 2185 struct openpromio opp; 2186 } oppbuf; 2187 }; 2188 2189 di_prom_handle_t 2190 di_prom_init() 2191 { 2192 struct di_prom_handle *p; 2193 2194 if ((p = malloc(sizeof (struct di_prom_handle))) == NULL) 2195 return (DI_PROM_HANDLE_NIL); 2196 2197 DPRINTF((DI_INFO, "di_prom_init: get prom handle 0x%p\n", p)); 2198 2199 (void) mutex_init(&p->lock, USYNC_THREAD, NULL); 2200 if ((p->fd = open("/dev/openprom", O_RDONLY)) < 0) { 2201 free(p); 2202 return (DI_PROM_HANDLE_NIL); 2203 } 2204 p->list = NULL; 2205 2206 return ((di_prom_handle_t)p); 2207 } 2208 2209 static void 2210 di_prom_prop_free(struct di_prom_prop *list) 2211 { 2212 struct di_prom_prop *tmp = list; 2213 2214 while (tmp != NULL) { 2215 list = tmp->next; 2216 if (tmp->name != NULL) { 2217 free(tmp->name); 2218 } 2219 if (tmp->data != NULL) { 2220 free(tmp->data); 2221 } 2222 free(tmp); 2223 tmp = list; 2224 } 2225 } 2226 2227 void 2228 di_prom_fini(di_prom_handle_t ph) 2229 { 2230 struct di_prom_handle *p = (struct di_prom_handle *)ph; 2231 2232 DPRINTF((DI_INFO, "di_prom_fini: free prom handle 0x%p\n", p)); 2233 2234 (void) close(p->fd); 2235 (void) mutex_destroy(&p->lock); 2236 di_prom_prop_free(p->list); 2237 2238 free(p); 2239 } 2240 2241 /* 2242 * Internal library interface for locating the property 2243 * XXX: ph->lock must be held for the duration of call. 2244 */ 2245 static di_prom_prop_t 2246 di_prom_prop_found(di_prom_handle_t ph, int nodeid, 2247 di_prom_prop_t prom_prop) 2248 { 2249 struct di_prom_handle *p = (struct di_prom_handle *)ph; 2250 struct openpromio *opp = &p->oppbuf.opp; 2251 int *ip = (int *)((void *)opp->oprom_array); 2252 struct di_prom_prop *prop = (struct di_prom_prop *)prom_prop; 2253 2254 DPRINTF((DI_TRACE1, "Looking for nodeid 0x%x\n", nodeid)); 2255 2256 /* 2257 * Set "current" nodeid in the openprom driver 2258 */ 2259 opp->oprom_size = sizeof (int); 2260 *ip = nodeid; 2261 if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) { 2262 DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n", nodeid)); 2263 return (DI_PROM_PROP_NIL); 2264 } 2265 2266 DPRINTF((DI_TRACE, "Found nodeid 0x%x\n", nodeid)); 2267 2268 bzero(opp, OBP_MAXBUF); 2269 opp->oprom_size = OBP_MAXPROPNAME; 2270 if (prom_prop != DI_PROM_PROP_NIL) 2271 (void) strcpy(opp->oprom_array, prop->name); 2272 2273 if ((ioctl(p->fd, OPROMNXTPROP, opp) < 0) || (opp->oprom_size == 0)) 2274 return (DI_PROM_PROP_NIL); 2275 2276 /* 2277 * Prom property found. Allocate struct for storing prop 2278 * (reuse variable prop) 2279 */ 2280 if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) 2281 return (DI_PROM_PROP_NIL); 2282 2283 /* 2284 * Get a copy of property name 2285 */ 2286 if ((prop->name = strdup(opp->oprom_array)) == NULL) { 2287 free(prop); 2288 return (DI_PROM_PROP_NIL); 2289 } 2290 2291 /* 2292 * get property value and length 2293 */ 2294 opp->oprom_size = OBP_MAXPROPLEN; 2295 2296 if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) || 2297 (opp->oprom_size == (uint_t)-1)) { 2298 free(prop->name); 2299 free(prop); 2300 return (DI_PROM_PROP_NIL); 2301 } 2302 2303 /* 2304 * make a copy of the property value 2305 */ 2306 prop->len = opp->oprom_size; 2307 2308 if (prop->len == 0) 2309 prop->data = NULL; 2310 else if ((prop->data = malloc(prop->len)) == NULL) { 2311 free(prop->name); 2312 free(prop); 2313 return (DI_PROM_PROP_NIL); 2314 } 2315 2316 bcopy(opp->oprom_array, prop->data, prop->len); 2317 2318 /* 2319 * Prepend prop to list in prom handle 2320 */ 2321 prop->next = p->list; 2322 p->list = prop; 2323 2324 return ((di_prom_prop_t)prop); 2325 } 2326 2327 di_prom_prop_t 2328 di_prom_prop_next(di_prom_handle_t ph, di_node_t node, di_prom_prop_t prom_prop) 2329 { 2330 struct di_prom_handle *p = (struct di_prom_handle *)ph; 2331 2332 DPRINTF((DI_TRACE1, "Search next prop for node 0x%p with ph 0x%p\n", 2333 node, p)); 2334 2335 /* 2336 * paranoid check 2337 */ 2338 if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) { 2339 errno = EINVAL; 2340 return (DI_PROM_PROP_NIL); 2341 } 2342 2343 if (di_nodeid(node) != DI_PROM_NODEID) { 2344 errno = ENXIO; 2345 return (DI_PROM_PROP_NIL); 2346 } 2347 2348 /* 2349 * synchronize access to prom file descriptor 2350 */ 2351 (void) mutex_lock(&p->lock); 2352 2353 /* 2354 * look for next property 2355 */ 2356 prom_prop = di_prom_prop_found(ph, DI_NODE(node)->nodeid, prom_prop); 2357 2358 (void) mutex_unlock(&p->lock); 2359 2360 return (prom_prop); 2361 } 2362 2363 char * 2364 di_prom_prop_name(di_prom_prop_t prom_prop) 2365 { 2366 /* 2367 * paranoid check 2368 */ 2369 if (prom_prop == DI_PROM_PROP_NIL) { 2370 errno = EINVAL; 2371 return (NULL); 2372 } 2373 2374 return (((struct di_prom_prop *)prom_prop)->name); 2375 } 2376 2377 int 2378 di_prom_prop_data(di_prom_prop_t prom_prop, uchar_t **prom_prop_data) 2379 { 2380 /* 2381 * paranoid check 2382 */ 2383 if (prom_prop == DI_PROM_PROP_NIL) { 2384 errno = EINVAL; 2385 return (NULL); 2386 } 2387 2388 *prom_prop_data = ((struct di_prom_prop *)prom_prop)->data; 2389 2390 return (((struct di_prom_prop *)prom_prop)->len); 2391 } 2392 2393 /* 2394 * Internal library interface for locating the property 2395 * Returns length if found, -1 if prop doesn't exist. 2396 */ 2397 static struct di_prom_prop * 2398 di_prom_prop_lookup_common(di_prom_handle_t ph, di_node_t node, 2399 const char *prom_prop_name) 2400 { 2401 struct openpromio *opp; 2402 struct di_prom_prop *prop; 2403 struct di_prom_handle *p = (struct di_prom_handle *)ph; 2404 2405 /* 2406 * paranoid check 2407 */ 2408 if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) { 2409 errno = EINVAL; 2410 return (NULL); 2411 } 2412 2413 if (di_nodeid(node) != DI_PROM_NODEID) { 2414 errno = ENXIO; 2415 return (NULL); 2416 } 2417 2418 opp = &p->oppbuf.opp; 2419 2420 (void) mutex_lock(&p->lock); 2421 2422 opp->oprom_size = sizeof (int); 2423 opp->oprom_node = DI_NODE(node)->nodeid; 2424 if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) { 2425 errno = ENXIO; 2426 DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n", 2427 DI_NODE(node)->nodeid)); 2428 (void) mutex_unlock(&p->lock); 2429 return (NULL); 2430 } 2431 2432 /* 2433 * get property length 2434 */ 2435 bzero(opp, OBP_MAXBUF); 2436 opp->oprom_size = OBP_MAXPROPLEN; 2437 (void) strcpy(opp->oprom_array, prom_prop_name); 2438 2439 if ((ioctl(p->fd, OPROMGETPROPLEN, opp) < 0) || 2440 (opp->oprom_len == -1)) { 2441 /* no such property */ 2442 (void) mutex_unlock(&p->lock); 2443 return (NULL); 2444 } 2445 2446 /* 2447 * Prom property found. Allocate struct for storing prop 2448 */ 2449 if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) { 2450 (void) mutex_unlock(&p->lock); 2451 return (NULL); 2452 } 2453 prop->name = NULL; /* we don't need the name */ 2454 prop->len = opp->oprom_len; 2455 2456 if (prop->len == 0) { /* boolean property */ 2457 prop->data = NULL; 2458 prop->next = p->list; 2459 p->list = prop; 2460 (void) mutex_unlock(&p->lock); 2461 return (prop); 2462 } 2463 2464 /* 2465 * retrieve the property value 2466 */ 2467 bzero(opp, OBP_MAXBUF); 2468 opp->oprom_size = OBP_MAXPROPLEN; 2469 (void) strcpy(opp->oprom_array, prom_prop_name); 2470 2471 if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) || 2472 (opp->oprom_size == (uint_t)-1)) { 2473 /* error retrieving property value */ 2474 (void) mutex_unlock(&p->lock); 2475 free(prop); 2476 return (NULL); 2477 } 2478 2479 /* 2480 * make a copy of the property value, stick in ph->list 2481 */ 2482 if ((prop->data = malloc(prop->len)) == NULL) { 2483 (void) mutex_unlock(&p->lock); 2484 free(prop); 2485 return (NULL); 2486 } 2487 2488 bcopy(opp->oprom_array, prop->data, prop->len); 2489 2490 prop->next = p->list; 2491 p->list = prop; 2492 (void) mutex_unlock(&p->lock); 2493 2494 return (prop); 2495 } 2496 2497 int 2498 di_prom_prop_lookup_ints(di_prom_handle_t ph, di_node_t node, 2499 const char *prom_prop_name, int **prom_prop_data) 2500 { 2501 int len; 2502 struct di_prom_prop *prop; 2503 2504 prop = di_prom_prop_lookup_common(ph, node, prom_prop_name); 2505 2506 if (prop == NULL) { 2507 *prom_prop_data = NULL; 2508 return (-1); 2509 } 2510 2511 if (prop->len == 0) { /* boolean property */ 2512 *prom_prop_data = NULL; 2513 return (0); 2514 } 2515 2516 len = di_prop_decode_common((void *)&prop->data, prop->len, 2517 DI_PROP_TYPE_INT, 1); 2518 *prom_prop_data = (int *)((void *)prop->data); 2519 2520 return (len); 2521 } 2522 2523 int 2524 di_prom_prop_lookup_strings(di_prom_handle_t ph, di_node_t node, 2525 const char *prom_prop_name, char **prom_prop_data) 2526 { 2527 int len; 2528 struct di_prom_prop *prop; 2529 2530 prop = di_prom_prop_lookup_common(ph, node, prom_prop_name); 2531 2532 if (prop == NULL) { 2533 *prom_prop_data = NULL; 2534 return (-1); 2535 } 2536 2537 if (prop->len == 0) { /* boolean property */ 2538 *prom_prop_data = NULL; 2539 return (0); 2540 } 2541 2542 /* 2543 * Fix an openprom bug (OBP string not NULL terminated). 2544 * XXX This should really be fixed in promif. 2545 */ 2546 if (((char *)prop->data)[prop->len - 1] != '\0') { 2547 uchar_t *tmp; 2548 prop->len++; 2549 if ((tmp = realloc(prop->data, prop->len)) == NULL) 2550 return (-1); 2551 2552 prop->data = tmp; 2553 ((char *)prop->data)[prop->len - 1] = '\0'; 2554 DPRINTF((DI_INFO, "OBP string not NULL terminated: " 2555 "node=%s, prop=%s, val=%s\n", 2556 di_node_name(node), prom_prop_name, prop->data)); 2557 } 2558 2559 len = di_prop_decode_common((void *)&prop->data, prop->len, 2560 DI_PROP_TYPE_STRING, 1); 2561 *prom_prop_data = (char *)prop->data; 2562 2563 return (len); 2564 } 2565 2566 int 2567 di_prom_prop_lookup_bytes(di_prom_handle_t ph, di_node_t node, 2568 const char *prom_prop_name, uchar_t **prom_prop_data) 2569 { 2570 int len; 2571 struct di_prom_prop *prop; 2572 2573 prop = di_prom_prop_lookup_common(ph, node, prom_prop_name); 2574 2575 if (prop == NULL) { 2576 *prom_prop_data = NULL; 2577 return (-1); 2578 } 2579 2580 if (prop->len == 0) { /* boolean property */ 2581 *prom_prop_data = NULL; 2582 return (0); 2583 } 2584 2585 len = di_prop_decode_common((void *)&prop->data, prop->len, 2586 DI_PROP_TYPE_BYTE, 1); 2587 *prom_prop_data = prop->data; 2588 2589 return (len); 2590 } 2591 2592 di_lnode_t 2593 di_link_to_lnode(di_link_t link, uint_t endpoint) 2594 { 2595 struct di_all *di_all; 2596 2597 if ((link == DI_LINK_NIL) || 2598 ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { 2599 errno = EINVAL; 2600 return (DI_LNODE_NIL); 2601 } 2602 2603 di_all = DI_ALL((caddr_t)link - DI_LINK(link)->self); 2604 2605 if (endpoint == DI_LINK_SRC) { 2606 return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->src_lnode)); 2607 } else { 2608 return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->tgt_lnode)); 2609 } 2610 /* NOTREACHED */ 2611 } 2612 2613 char * 2614 di_lnode_name(di_lnode_t lnode) 2615 { 2616 return (di_driver_name(di_lnode_devinfo(lnode))); 2617 } 2618 2619 di_node_t 2620 di_lnode_devinfo(di_lnode_t lnode) 2621 { 2622 struct di_all *di_all; 2623 2624 di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self); 2625 return (DI_NODE((caddr_t)di_all + DI_LNODE(lnode)->node)); 2626 } 2627 2628 int 2629 di_lnode_devt(di_lnode_t lnode, dev_t *devt) 2630 { 2631 if ((lnode == DI_LNODE_NIL) || (devt == NULL)) { 2632 errno = EINVAL; 2633 return (-1); 2634 } 2635 if ((DI_LNODE(lnode)->dev_major == (major_t)-1) && 2636 (DI_LNODE(lnode)->dev_minor == (minor_t)-1)) 2637 return (-1); 2638 2639 *devt = makedev(DI_LNODE(lnode)->dev_major, DI_LNODE(lnode)->dev_minor); 2640 return (0); 2641 } 2642 2643 int 2644 di_link_spectype(di_link_t link) 2645 { 2646 return (DI_LINK(link)->spec_type); 2647 } 2648 2649 void 2650 di_minor_private_set(di_minor_t minor, void *data) 2651 { 2652 DI_MINOR(minor)->user_private_data = (uintptr_t)data; 2653 } 2654 2655 void * 2656 di_minor_private_get(di_minor_t minor) 2657 { 2658 return ((void *)DI_MINOR(minor)->user_private_data); 2659 } 2660 2661 void 2662 di_node_private_set(di_node_t node, void *data) 2663 { 2664 DI_NODE(node)->user_private_data = (uintptr_t)data; 2665 } 2666 2667 void * 2668 di_node_private_get(di_node_t node) 2669 { 2670 return ((void *)DI_NODE(node)->user_private_data); 2671 } 2672 2673 void 2674 di_lnode_private_set(di_lnode_t lnode, void *data) 2675 { 2676 DI_LNODE(lnode)->user_private_data = (uintptr_t)data; 2677 } 2678 2679 void * 2680 di_lnode_private_get(di_lnode_t lnode) 2681 { 2682 return ((void *)DI_LNODE(lnode)->user_private_data); 2683 } 2684 2685 void 2686 di_link_private_set(di_link_t link, void *data) 2687 { 2688 DI_LINK(link)->user_private_data = (uintptr_t)data; 2689 } 2690 2691 void * 2692 di_link_private_get(di_link_t link) 2693 { 2694 return ((void *)DI_LINK(link)->user_private_data); 2695 } 2696 2697 di_lnode_t 2698 di_lnode_next(di_node_t node, di_lnode_t lnode) 2699 { 2700 struct di_all *di_all; 2701 2702 /* 2703 * paranoid error checking 2704 */ 2705 if (node == DI_NODE_NIL) { 2706 errno = EINVAL; 2707 return (DI_LNODE_NIL); 2708 } 2709 2710 di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self); 2711 2712 if (lnode == DI_NODE_NIL) { 2713 if (DI_NODE(node)->lnodes != NULL) 2714 return (DI_LNODE((caddr_t)di_all + 2715 DI_NODE(node)->lnodes)); 2716 } else { 2717 if (DI_LNODE(lnode)->node_next != NULL) 2718 return (DI_LNODE((caddr_t)di_all + 2719 DI_LNODE(lnode)->node_next)); 2720 } 2721 2722 if (DINFOLYR & DI_ALL(di_all)->command) 2723 errno = ENXIO; 2724 else 2725 errno = ENOTSUP; 2726 2727 return (DI_LNODE_NIL); 2728 } 2729 2730 di_link_t 2731 di_link_next_by_node(di_node_t node, di_link_t link, uint_t endpoint) 2732 { 2733 struct di_all *di_all; 2734 2735 /* 2736 * paranoid error checking 2737 */ 2738 if ((node == DI_NODE_NIL) || 2739 ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { 2740 errno = EINVAL; 2741 return (DI_LINK_NIL); 2742 } 2743 2744 di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self); 2745 2746 if (endpoint == DI_LINK_SRC) { 2747 if (link == DI_LINK_NIL) { 2748 if (DI_NODE(node)->src_links != NULL) 2749 return (DI_LINK((caddr_t)di_all + 2750 DI_NODE(node)->src_links)); 2751 } else { 2752 if (DI_LINK(link)->src_node_next != NULL) 2753 return (DI_LINK((caddr_t)di_all + 2754 DI_LINK(link)->src_node_next)); 2755 } 2756 } else { 2757 if (link == DI_LINK_NIL) { 2758 if (DI_NODE(node)->tgt_links != NULL) 2759 return (DI_LINK((caddr_t)di_all + 2760 DI_NODE(node)->tgt_links)); 2761 } else { 2762 if (DI_LINK(link)->tgt_node_next != NULL) 2763 return (DI_LINK((caddr_t)di_all + 2764 DI_LINK(link)->tgt_node_next)); 2765 } 2766 } 2767 2768 if (DINFOLYR & DI_ALL(di_all)->command) 2769 errno = ENXIO; 2770 else 2771 errno = ENOTSUP; 2772 2773 return (DI_LINK_NIL); 2774 } 2775 2776 di_link_t 2777 di_link_next_by_lnode(di_lnode_t lnode, di_link_t link, uint_t endpoint) 2778 { 2779 struct di_all *di_all; 2780 2781 /* 2782 * paranoid error checking 2783 */ 2784 if ((lnode == DI_LNODE_NIL) || 2785 ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { 2786 errno = EINVAL; 2787 return (DI_LINK_NIL); 2788 } 2789 2790 di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self); 2791 2792 if (endpoint == DI_LINK_SRC) { 2793 if (link == DI_LINK_NIL) { 2794 if (DI_LNODE(lnode)->link_out == NULL) 2795 return (DI_LINK_NIL); 2796 return (DI_LINK((caddr_t)di_all + 2797 DI_LNODE(lnode)->link_out)); 2798 } else { 2799 if (DI_LINK(link)->src_link_next == NULL) 2800 return (DI_LINK_NIL); 2801 return (DI_LINK((caddr_t)di_all + 2802 DI_LINK(link)->src_link_next)); 2803 } 2804 } else { 2805 if (link == DI_LINK_NIL) { 2806 if (DI_LNODE(lnode)->link_in == NULL) 2807 return (DI_LINK_NIL); 2808 return (DI_LINK((caddr_t)di_all + 2809 DI_LNODE(lnode)->link_in)); 2810 } else { 2811 if (DI_LINK(link)->tgt_link_next == NULL) 2812 return (DI_LINK_NIL); 2813 return (DI_LINK((caddr_t)di_all + 2814 DI_LINK(link)->tgt_link_next)); 2815 } 2816 } 2817 /* NOTREACHED */ 2818 } 2819 2820 /* 2821 * Internal library function: 2822 * Invoke callback for each link data on the link list of first node 2823 * on node_list headp, and place children of first node on the list. 2824 * 2825 * This is similar to walk_one_node, except we only walk in child 2826 * first mode. 2827 */ 2828 static void 2829 walk_one_link(struct node_list **headp, uint_t ep, 2830 void *arg, int (*callback)(di_link_t link, void *arg)) 2831 { 2832 int action = DI_WALK_CONTINUE; 2833 di_link_t link = DI_LINK_NIL; 2834 di_node_t node = (*headp)->node; 2835 2836 while ((link = di_link_next_by_node(node, link, ep)) != DI_LINK_NIL) { 2837 action = callback(link, arg); 2838 if (action == DI_WALK_TERMINATE) { 2839 break; 2840 } 2841 } 2842 2843 update_node_list(action, DI_WALK_LINKGEN, headp); 2844 } 2845 2846 int 2847 di_walk_link(di_node_t root, uint_t flag, uint_t endpoint, void *arg, 2848 int (*link_callback)(di_link_t link, void *arg)) 2849 { 2850 struct node_list *head; /* node_list for tree walk */ 2851 2852 #ifdef DEBUG 2853 char *path = di_devfs_path(root); 2854 DPRINTF((DI_INFO, "walking %s link data under %s\n", 2855 (endpoint == DI_LINK_SRC) ? "src" : "tgt", path)); 2856 di_devfs_path_free(path); 2857 #endif 2858 2859 /* 2860 * paranoid error checking 2861 */ 2862 if ((root == DI_NODE_NIL) || (link_callback == NULL) || (flag != 0) || 2863 ((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) { 2864 errno = EINVAL; 2865 return (-1); 2866 } 2867 2868 if ((head = malloc(sizeof (struct node_list))) == NULL) { 2869 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 2870 return (-1); 2871 } 2872 2873 head->next = NULL; 2874 head->node = root; 2875 2876 DPRINTF((DI_INFO, "Start link data walking from node %s\n", 2877 di_node_name(root))); 2878 2879 while (head != NULL) 2880 walk_one_link(&head, endpoint, arg, link_callback); 2881 2882 return (0); 2883 } 2884 2885 /* 2886 * Internal library function: 2887 * Invoke callback for each link data on the link list of first node 2888 * on node_list headp, and place children of first node on the list. 2889 * 2890 * This is similar to walk_one_node, except we only walk in child 2891 * first mode. 2892 */ 2893 static void 2894 walk_one_lnode(struct node_list **headp, void *arg, 2895 int (*callback)(di_lnode_t lnode, void *arg)) 2896 { 2897 int action = DI_WALK_CONTINUE; 2898 di_lnode_t lnode = DI_LNODE_NIL; 2899 di_node_t node = (*headp)->node; 2900 2901 while ((lnode = di_lnode_next(node, lnode)) != DI_LNODE_NIL) { 2902 action = callback(lnode, arg); 2903 if (action == DI_WALK_TERMINATE) { 2904 break; 2905 } 2906 } 2907 2908 update_node_list(action, DI_WALK_LINKGEN, headp); 2909 } 2910 2911 int 2912 di_walk_lnode(di_node_t root, uint_t flag, void *arg, 2913 int (*lnode_callback)(di_lnode_t lnode, void *arg)) 2914 { 2915 struct node_list *head; /* node_list for tree walk */ 2916 2917 #ifdef DEBUG 2918 char *path = di_devfs_path(root); 2919 DPRINTF((DI_INFO, "walking lnode data under %s\n", path)); 2920 di_devfs_path_free(path); 2921 #endif 2922 2923 /* 2924 * paranoid error checking 2925 */ 2926 if ((root == DI_NODE_NIL) || (lnode_callback == NULL) || (flag != 0)) { 2927 errno = EINVAL; 2928 return (-1); 2929 } 2930 2931 if ((head = malloc(sizeof (struct node_list))) == NULL) { 2932 DPRINTF((DI_ERR, "malloc of node_list failed\n")); 2933 return (-1); 2934 } 2935 2936 head->next = NULL; 2937 head->node = root; 2938 2939 DPRINTF((DI_INFO, "Start lnode data walking from node %s\n", 2940 di_node_name(root))); 2941 2942 while (head != NULL) 2943 walk_one_lnode(&head, arg, lnode_callback); 2944 2945 return (0); 2946 } 2947 2948 di_node_t 2949 di_lookup_node(di_node_t root, char *path) 2950 { 2951 struct di_all *dap; 2952 di_node_t node; 2953 char copy[MAXPATHLEN]; 2954 char *slash, *pname, *paddr; 2955 2956 /* 2957 * Path must be absolute and musn't have duplicate slashes 2958 */ 2959 if (*path != '/' || strstr(path, "//")) { 2960 DPRINTF((DI_ERR, "Invalid path: %s\n", path)); 2961 return (DI_NODE_NIL); 2962 } 2963 2964 if (root == DI_NODE_NIL) { 2965 DPRINTF((DI_ERR, "root node is DI_NODE_NIL\n")); 2966 return (DI_NODE_NIL); 2967 } 2968 2969 dap = DI_ALL((caddr_t)root - DI_NODE(root)->self); 2970 if (strcmp(dap->root_path, "/") != 0) { 2971 DPRINTF((DI_ERR, "snapshot root not / : %s\n", dap->root_path)); 2972 return (DI_NODE_NIL); 2973 } 2974 2975 if (strlcpy(copy, path, sizeof (copy)) >= sizeof (copy)) { 2976 DPRINTF((DI_ERR, "path too long: %s\n", path)); 2977 return (DI_NODE_NIL); 2978 } 2979 2980 for (slash = copy, node = root; slash; ) { 2981 2982 /* 2983 * Handle path = "/" case as well as trailing '/' 2984 */ 2985 if (*(slash + 1) == '\0') 2986 break; 2987 2988 /* 2989 * More path-components exist. Deal with the next one 2990 */ 2991 pname = slash + 1; 2992 node = di_child_node(node); 2993 2994 if (slash = strchr(pname, '/')) 2995 *slash = '\0'; 2996 if (paddr = strchr(pname, '@')) 2997 *paddr++ = '\0'; 2998 2999 for (; node != DI_NODE_NIL; node = di_sibling_node(node)) { 3000 char *name, *baddr; 3001 3002 name = di_node_name(node); 3003 baddr = di_bus_addr(node); 3004 3005 if (strcmp(pname, name) != 0) 3006 continue; 3007 3008 /* 3009 * Mappings between a "path-address" and bus-addr 3010 * 3011 * paddr baddr 3012 * --------------------- 3013 * NULL NULL 3014 * NULL "" 3015 * "" N/A (invalid paddr) 3016 */ 3017 if (paddr && baddr && strcmp(paddr, baddr) == 0) 3018 break; 3019 if (paddr == NULL && (baddr == NULL || *baddr == '\0')) 3020 break; 3021 } 3022 3023 /* 3024 * No nodes in the sibling list or there was no match 3025 */ 3026 if (node == DI_NODE_NIL) { 3027 DPRINTF((DI_ERR, "%s@%s: no node\n", pname, paddr)); 3028 return (DI_NODE_NIL); 3029 } 3030 } 3031 3032 assert(node != DI_NODE_NIL); 3033 return (node); 3034 } 3035 3036 static char * 3037 msglevel2str(di_debug_t msglevel) 3038 { 3039 switch (msglevel) { 3040 case DI_ERR: 3041 return ("ERROR"); 3042 case DI_INFO: 3043 return ("Info"); 3044 case DI_TRACE: 3045 return ("Trace"); 3046 case DI_TRACE1: 3047 return ("Trace1"); 3048 case DI_TRACE2: 3049 return ("Trace2"); 3050 default: 3051 return ("UNKNOWN"); 3052 } 3053 } 3054 3055 void 3056 dprint(di_debug_t msglevel, const char *fmt, ...) 3057 { 3058 va_list ap; 3059 char *estr; 3060 3061 if (di_debug <= DI_QUIET) 3062 return; 3063 3064 if (di_debug < msglevel) 3065 return; 3066 3067 estr = msglevel2str(msglevel); 3068 3069 assert(estr); 3070 3071 va_start(ap, fmt); 3072 3073 (void) fprintf(stderr, "libdevinfo[%lu]: %s: ", 3074 (ulong_t)getpid(), estr); 3075 (void) vfprintf(stderr, fmt, ap); 3076 3077 va_end(ap); 3078 } 3079 3080 /* end of devinfo.c */ 3081