17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 566f9d5cbSmlf * Common Development and Distribution License (the "License"). 666f9d5cbSmlf * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 2166f9d5cbSmlf 227c478bd9Sstevel@tonic-gate /* 23*3c4226f9Spjha * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <devfsadm.h> 307c478bd9Sstevel@tonic-gate #include <stdio.h> 317c478bd9Sstevel@tonic-gate #include <strings.h> 327c478bd9Sstevel@tonic-gate #include <stdlib.h> 33*3c4226f9Spjha #include <stdarg.h> 347c478bd9Sstevel@tonic-gate #include <limits.h> 35*3c4226f9Spjha #include <unistd.h> 36*3c4226f9Spjha #include <config_admin.h> 37*3c4226f9Spjha #include <cfg_link.h> 38*3c4226f9Spjha #include <sys/types.h> 39*3c4226f9Spjha #include <sys/mkdev.h> 40*3c4226f9Spjha #include <sys/hotplug/pci/pcihp.h> 417c478bd9Sstevel@tonic-gate 42*3c4226f9Spjha #ifdef DEBUG 43*3c4226f9Spjha #define dprint(args) devfsadm_errprint args 44*3c4226f9Spjha /* 45*3c4226f9Spjha * for use in print routine arg list as a shorthand way to locate node via 46*3c4226f9Spjha * "prtconf -D" to avoid messy and cluttered debugging code 47*3c4226f9Spjha * don't forget the corresponding "%s%d" format 48*3c4226f9Spjha */ 49*3c4226f9Spjha #define DRVINST(node) di_driver_name(node), di_instance(node) 50*3c4226f9Spjha #else 51*3c4226f9Spjha #define dprint(args) 52*3c4226f9Spjha #endif 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate 557c478bd9Sstevel@tonic-gate static int scsi_cfg_creat_cb(di_minor_t minor, di_node_t node); 567c478bd9Sstevel@tonic-gate static int sbd_cfg_creat_cb(di_minor_t minor, di_node_t node); 577c478bd9Sstevel@tonic-gate static int usb_cfg_creat_cb(di_minor_t minor, di_node_t node); 587c478bd9Sstevel@tonic-gate static char *get_roothub(const char *path, void *cb_arg); 597c478bd9Sstevel@tonic-gate static int pci_cfg_creat_cb(di_minor_t minor, di_node_t node); 607c478bd9Sstevel@tonic-gate static int ib_cfg_creat_cb(di_minor_t minor, di_node_t node); 6166f9d5cbSmlf static int sata_cfg_creat_cb(di_minor_t minor, di_node_t node); 627c478bd9Sstevel@tonic-gate 63*3c4226f9Spjha static di_node_t pci_cfg_chassis_node(di_node_t, di_prom_handle_t); 64*3c4226f9Spjha static char *pci_cfg_slotname(di_node_t, di_prom_handle_t, minor_t); 65*3c4226f9Spjha static int pci_cfg_ap_node(minor_t, di_node_t, di_prom_handle_t, 66*3c4226f9Spjha char *, int, int); 67*3c4226f9Spjha static int pci_cfg_iob_name(di_minor_t, di_node_t, di_prom_handle_t, 68*3c4226f9Spjha char *, int); 69*3c4226f9Spjha static minor_t pci_cfg_pcidev(di_node_t, di_prom_handle_t); 70*3c4226f9Spjha static int pci_cfg_ap_path(di_minor_t, di_node_t, di_prom_handle_t, 71*3c4226f9Spjha char *, int, char **); 72*3c4226f9Spjha static char *pci_cfg_info_data(char *); 73*3c4226f9Spjha static int pci_cfg_is_ap_path(di_node_t, di_prom_handle_t); 74*3c4226f9Spjha static int pci_cfg_ap_legacy(di_minor_t, di_node_t, di_prom_handle_t, 75*3c4226f9Spjha char *, int); 76*3c4226f9Spjha static void pci_cfg_rm_invalid_links(char *, char *); 77*3c4226f9Spjha static void pci_cfg_rm_link(char *); 78*3c4226f9Spjha static void pci_cfg_rm_all(char *); 79*3c4226f9Spjha static char *pci_cfg_devpath(di_node_t, di_minor_t); 80*3c4226f9Spjha static di_node_t pci_cfg_snapshot(di_node_t, di_minor_t, 81*3c4226f9Spjha di_node_t *, di_minor_t *); 82*3c4226f9Spjha 83*3c4226f9Spjha /* flag definitions for di_propall_*(); value "0" is always the default flag */ 84*3c4226f9Spjha #define DIPROP_PRI_NODE 0x0 85*3c4226f9Spjha #define DIPROP_PRI_PROM 0x1 86*3c4226f9Spjha static int di_propall_lookup_ints(di_prom_handle_t, int, 87*3c4226f9Spjha dev_t, di_node_t, const char *, int **); 88*3c4226f9Spjha static int di_propall_lookup_strings(di_prom_handle_t, int, 89*3c4226f9Spjha dev_t, di_node_t, const char *, char **); 90*3c4226f9Spjha 91*3c4226f9Spjha 927c478bd9Sstevel@tonic-gate /* 937c478bd9Sstevel@tonic-gate * NOTE: The CREATE_DEFER flag is private to this module. 947c478bd9Sstevel@tonic-gate * NOT to be used by other modules 957c478bd9Sstevel@tonic-gate */ 967c478bd9Sstevel@tonic-gate static devfsadm_create_t cfg_create_cbt[] = { 97*3c4226f9Spjha { "attachment-point", DDI_NT_SCSI_ATTACHMENT_POINT, NULL, 987c478bd9Sstevel@tonic-gate TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb 997c478bd9Sstevel@tonic-gate }, 100*3c4226f9Spjha { "attachment-point", DDI_NT_SBD_ATTACHMENT_POINT, NULL, 1017c478bd9Sstevel@tonic-gate TYPE_EXACT, ILEVEL_0, sbd_cfg_creat_cb 1027c478bd9Sstevel@tonic-gate }, 103*3c4226f9Spjha { "fc-attachment-point", DDI_NT_FC_ATTACHMENT_POINT, NULL, 1047c478bd9Sstevel@tonic-gate TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb 1057c478bd9Sstevel@tonic-gate }, 106*3c4226f9Spjha { "attachment-point", DDI_NT_USB_ATTACHMENT_POINT, NULL, 1077c478bd9Sstevel@tonic-gate TYPE_EXACT, ILEVEL_0, usb_cfg_creat_cb 1087c478bd9Sstevel@tonic-gate }, 109*3c4226f9Spjha { "attachment-point", DDI_NT_PCI_ATTACHMENT_POINT, NULL, 1107c478bd9Sstevel@tonic-gate TYPE_EXACT, ILEVEL_0, pci_cfg_creat_cb 1117c478bd9Sstevel@tonic-gate }, 112*3c4226f9Spjha { "attachment-point", DDI_NT_IB_ATTACHMENT_POINT, NULL, 1137c478bd9Sstevel@tonic-gate TYPE_EXACT, ILEVEL_0, ib_cfg_creat_cb 11466f9d5cbSmlf }, 115*3c4226f9Spjha { "attachment-point", DDI_NT_SATA_ATTACHMENT_POINT, NULL, 11666f9d5cbSmlf TYPE_EXACT, ILEVEL_0, sata_cfg_creat_cb 1177c478bd9Sstevel@tonic-gate } 1187c478bd9Sstevel@tonic-gate }; 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate DEVFSADM_CREATE_INIT_V0(cfg_create_cbt); 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate static devfsadm_remove_t cfg_remove_cbt[] = { 1237c478bd9Sstevel@tonic-gate { "attachment-point", SCSI_CFG_LINK_RE, RM_POST, 1247c478bd9Sstevel@tonic-gate ILEVEL_0, devfsadm_rm_all 1257c478bd9Sstevel@tonic-gate }, 1267c478bd9Sstevel@tonic-gate { "attachment-point", SBD_CFG_LINK_RE, RM_POST, 1277c478bd9Sstevel@tonic-gate ILEVEL_0, devfsadm_rm_all 1287c478bd9Sstevel@tonic-gate }, 1297c478bd9Sstevel@tonic-gate { "fc-attachment-point", SCSI_CFG_LINK_RE, RM_POST, 1307c478bd9Sstevel@tonic-gate ILEVEL_0, devfsadm_rm_all 1317c478bd9Sstevel@tonic-gate }, 1327c478bd9Sstevel@tonic-gate { "attachment-point", USB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS, 1337c478bd9Sstevel@tonic-gate ILEVEL_0, devfsadm_rm_all 1347c478bd9Sstevel@tonic-gate }, 1357c478bd9Sstevel@tonic-gate { "attachment-point", PCI_CFG_LINK_RE, RM_POST, 1367c478bd9Sstevel@tonic-gate ILEVEL_0, devfsadm_rm_all 1377c478bd9Sstevel@tonic-gate }, 138*3c4226f9Spjha { "attachment-point", PCI_CFG_PATH_LINK_RE, RM_POST|RM_HOT, 139*3c4226f9Spjha ILEVEL_0, pci_cfg_rm_all 140*3c4226f9Spjha }, 1417c478bd9Sstevel@tonic-gate { "attachment-point", IB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS, 1427c478bd9Sstevel@tonic-gate ILEVEL_0, devfsadm_rm_all 14366f9d5cbSmlf }, 14466f9d5cbSmlf { "attachment-point", SATA_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS, 14566f9d5cbSmlf ILEVEL_0, devfsadm_rm_all 1467c478bd9Sstevel@tonic-gate } 1477c478bd9Sstevel@tonic-gate }; 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate DEVFSADM_REMOVE_INIT_V0(cfg_remove_cbt); 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate static int 1527c478bd9Sstevel@tonic-gate scsi_cfg_creat_cb(di_minor_t minor, di_node_t node) 1537c478bd9Sstevel@tonic-gate { 1547c478bd9Sstevel@tonic-gate char path[PATH_MAX + 1]; 1557c478bd9Sstevel@tonic-gate char *c_num = NULL, *devfs_path, *mn; 1567c478bd9Sstevel@tonic-gate devfsadm_enumerate_t rules[3] = { 1577c478bd9Sstevel@tonic-gate {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT}, 1587c478bd9Sstevel@tonic-gate {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR}, 1597c478bd9Sstevel@tonic-gate {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT} 1607c478bd9Sstevel@tonic-gate }; 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate mn = di_minor_name(minor); 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate if ((devfs_path = di_devfs_path(node)) == NULL) { 1657c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 1667c478bd9Sstevel@tonic-gate } 1677c478bd9Sstevel@tonic-gate (void) strcpy(path, devfs_path); 1687c478bd9Sstevel@tonic-gate (void) strcat(path, ":"); 1697c478bd9Sstevel@tonic-gate (void) strcat(path, mn); 1707c478bd9Sstevel@tonic-gate di_devfs_path_free(devfs_path); 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate if (devfsadm_enumerate_int(path, 1, &c_num, rules, 3) 1737c478bd9Sstevel@tonic-gate == DEVFSADM_FAILURE) { 1747c478bd9Sstevel@tonic-gate /* 1757c478bd9Sstevel@tonic-gate * Unlike the disks module we don't retry on failure. 1767c478bd9Sstevel@tonic-gate * If we have multiple "c" numbers for a single physical 1777c478bd9Sstevel@tonic-gate * controller due to bug 4045879, we will not assign a 1787c478bd9Sstevel@tonic-gate * c-number/symlink for the controller. 1797c478bd9Sstevel@tonic-gate */ 1807c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 1817c478bd9Sstevel@tonic-gate } 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate (void) strcpy(path, CFG_DIRNAME); 1847c478bd9Sstevel@tonic-gate (void) strcat(path, "/c"); 1857c478bd9Sstevel@tonic-gate (void) strcat(path, c_num); 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate free(c_num); 1887c478bd9Sstevel@tonic-gate 1897c478bd9Sstevel@tonic-gate (void) devfsadm_mklink(path, node, minor, 0); 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 1927c478bd9Sstevel@tonic-gate } 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate static int 1957c478bd9Sstevel@tonic-gate sbd_cfg_creat_cb(di_minor_t minor, di_node_t node) 1967c478bd9Sstevel@tonic-gate { 1977c478bd9Sstevel@tonic-gate char path[PATH_MAX + 1]; 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate (void) strcpy(path, CFG_DIRNAME); 2007c478bd9Sstevel@tonic-gate (void) strcat(path, "/"); 2017c478bd9Sstevel@tonic-gate (void) strcat(path, di_minor_name(minor)); 2027c478bd9Sstevel@tonic-gate (void) devfsadm_mklink(path, node, minor, 0); 2037c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 2047c478bd9Sstevel@tonic-gate } 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate static int 2087c478bd9Sstevel@tonic-gate usb_cfg_creat_cb(di_minor_t minor, di_node_t node) 2097c478bd9Sstevel@tonic-gate { 2107c478bd9Sstevel@tonic-gate char *cp, path[PATH_MAX + 1]; 2117c478bd9Sstevel@tonic-gate devfsadm_enumerate_t rules[1] = 2127c478bd9Sstevel@tonic-gate {"^cfg$/^usb([0-9]+)$", 1, MATCH_CALLBACK, NULL, get_roothub}; 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate if ((cp = di_devfs_path(node)) == NULL) { 2157c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor)); 2197c478bd9Sstevel@tonic-gate di_devfs_path_free(cp); 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate if (devfsadm_enumerate_int(path, 0, &cp, rules, 1)) { 2227c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate /* create usbN and the symlink */ 2267c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s/usb%s/%s", CFG_DIRNAME, cp, 2277c478bd9Sstevel@tonic-gate di_minor_name(minor)); 2287c478bd9Sstevel@tonic-gate free(cp); 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate (void) devfsadm_mklink(path, node, minor, 0); 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 2337c478bd9Sstevel@tonic-gate } 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate 23666f9d5cbSmlf static int 23766f9d5cbSmlf sata_cfg_creat_cb(di_minor_t minor, di_node_t node) 23866f9d5cbSmlf { 23966f9d5cbSmlf char path[PATH_MAX + 1], l_path[PATH_MAX], *buf, *devfspath; 24066f9d5cbSmlf char *minor_nm; 24166f9d5cbSmlf devfsadm_enumerate_t rules[1] = 24266f9d5cbSmlf {"^cfg$/^sata([0-9]+)$", 1, MATCH_ADDR}; 24366f9d5cbSmlf 24466f9d5cbSmlf minor_nm = di_minor_name(minor); 24566f9d5cbSmlf if (minor_nm == NULL) 24666f9d5cbSmlf return (DEVFSADM_CONTINUE); 24766f9d5cbSmlf 24866f9d5cbSmlf devfspath = di_devfs_path(node); 24966f9d5cbSmlf if (devfspath == NULL) 25066f9d5cbSmlf return (DEVFSADM_CONTINUE); 25166f9d5cbSmlf 25266f9d5cbSmlf (void) strlcpy(path, devfspath, sizeof (path)); 25366f9d5cbSmlf (void) strlcat(path, ":", sizeof (path)); 25466f9d5cbSmlf (void) strlcat(path, minor_nm, sizeof (path)); 25566f9d5cbSmlf di_devfs_path_free(devfspath); 25666f9d5cbSmlf 25766f9d5cbSmlf /* build the physical path from the components */ 25866f9d5cbSmlf if (devfsadm_enumerate_int(path, 0, &buf, rules, 1) == 25966f9d5cbSmlf DEVFSADM_FAILURE) { 26066f9d5cbSmlf return (DEVFSADM_CONTINUE); 26166f9d5cbSmlf } 26266f9d5cbSmlf 26366f9d5cbSmlf (void) snprintf(l_path, sizeof (l_path), "%s/sata%s/%s", CFG_DIRNAME, 26466f9d5cbSmlf buf, minor_nm); 26566f9d5cbSmlf free(buf); 26666f9d5cbSmlf 26766f9d5cbSmlf (void) devfsadm_mklink(l_path, node, minor, 0); 26866f9d5cbSmlf 26966f9d5cbSmlf return (DEVFSADM_CONTINUE); 27066f9d5cbSmlf } 27166f9d5cbSmlf 27266f9d5cbSmlf 2737c478bd9Sstevel@tonic-gate /* 2747c478bd9Sstevel@tonic-gate * get_roothub: 2757c478bd9Sstevel@tonic-gate * figure out the root hub path to calculate /dev/cfg/usbN 2767c478bd9Sstevel@tonic-gate */ 2777c478bd9Sstevel@tonic-gate /* ARGSUSED */ 2787c478bd9Sstevel@tonic-gate static char * 2797c478bd9Sstevel@tonic-gate get_roothub(const char *path, void *cb_arg) 2807c478bd9Sstevel@tonic-gate { 2817c478bd9Sstevel@tonic-gate int i, count = 0; 2827c478bd9Sstevel@tonic-gate char *physpath, *cp; 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate /* make a copy */ 2857c478bd9Sstevel@tonic-gate if ((physpath = strdup(path)) == NULL) { 2867c478bd9Sstevel@tonic-gate return (NULL); 2877c478bd9Sstevel@tonic-gate } 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate /* 2907c478bd9Sstevel@tonic-gate * physpath must always have a minor name component 2917c478bd9Sstevel@tonic-gate */ 2927c478bd9Sstevel@tonic-gate if ((cp = strrchr(physpath, ':')) == NULL) { 2937c478bd9Sstevel@tonic-gate free(physpath); 2947c478bd9Sstevel@tonic-gate return (NULL); 2957c478bd9Sstevel@tonic-gate } 2967c478bd9Sstevel@tonic-gate *cp++ = '\0'; 2977c478bd9Sstevel@tonic-gate 2987c478bd9Sstevel@tonic-gate /* 2997c478bd9Sstevel@tonic-gate * No '.' in the minor name indicates a roothub port. 3007c478bd9Sstevel@tonic-gate */ 3017c478bd9Sstevel@tonic-gate if (strchr(cp, '.') == NULL) { 3027c478bd9Sstevel@tonic-gate /* roothub device */ 3037c478bd9Sstevel@tonic-gate return (physpath); 3047c478bd9Sstevel@tonic-gate } 3057c478bd9Sstevel@tonic-gate 3067c478bd9Sstevel@tonic-gate while (*cp) { 3077c478bd9Sstevel@tonic-gate if (*cp == '.') 3087c478bd9Sstevel@tonic-gate count++; 3097c478bd9Sstevel@tonic-gate cp++; 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate /* Remove as many trailing path components as there are '.'s */ 3137c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) { 3147c478bd9Sstevel@tonic-gate if ((cp = strrchr(physpath, '/')) == NULL || (cp == physpath)) { 3157c478bd9Sstevel@tonic-gate free(physpath); 3167c478bd9Sstevel@tonic-gate return (NULL); 3177c478bd9Sstevel@tonic-gate } 3187c478bd9Sstevel@tonic-gate *cp = '\0'; 3197c478bd9Sstevel@tonic-gate } 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate return (physpath); 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate 3257c478bd9Sstevel@tonic-gate /* 326*3c4226f9Spjha * returns an allocted string containing the device path for <node> and 327*3c4226f9Spjha * <minor> 328*3c4226f9Spjha */ 329*3c4226f9Spjha static char * 330*3c4226f9Spjha pci_cfg_devpath(di_node_t node, di_minor_t minor) 331*3c4226f9Spjha { 332*3c4226f9Spjha char *path; 333*3c4226f9Spjha char *bufp; 334*3c4226f9Spjha char *minor_nm; 335*3c4226f9Spjha int buflen; 336*3c4226f9Spjha 337*3c4226f9Spjha path = di_devfs_path(node); 338*3c4226f9Spjha minor_nm = di_minor_name(minor); 339*3c4226f9Spjha buflen = snprintf(NULL, 0, "%s:%s", path, minor_nm); 340*3c4226f9Spjha 341*3c4226f9Spjha bufp = malloc(sizeof (char) * buflen); 342*3c4226f9Spjha if (bufp == NULL) 343*3c4226f9Spjha goto OUT; 344*3c4226f9Spjha (void) snprintf(bufp, buflen, "%s:%s", path, minor_nm); 345*3c4226f9Spjha 346*3c4226f9Spjha OUT: 347*3c4226f9Spjha di_devfs_path_free(path); 348*3c4226f9Spjha return (bufp); 349*3c4226f9Spjha } 350*3c4226f9Spjha 351*3c4226f9Spjha 352*3c4226f9Spjha static int 353*3c4226f9Spjha di_propall_lookup_ints(di_prom_handle_t ph, int flags, 354*3c4226f9Spjha dev_t dev, di_node_t node, const char *prop_name, int **prop_data) 355*3c4226f9Spjha { 356*3c4226f9Spjha int rv; 357*3c4226f9Spjha 358*3c4226f9Spjha if (flags & DIPROP_PRI_PROM) { 359*3c4226f9Spjha rv = di_prom_prop_lookup_ints(ph, node, prop_name, prop_data); 360*3c4226f9Spjha if (rv < 0) 361*3c4226f9Spjha rv = di_prop_lookup_ints(dev, node, prop_name, 362*3c4226f9Spjha prop_data); 363*3c4226f9Spjha } else { 364*3c4226f9Spjha rv = di_prop_lookup_ints(dev, node, prop_name, prop_data); 365*3c4226f9Spjha if (rv < 0) 366*3c4226f9Spjha rv = di_prom_prop_lookup_ints(ph, node, prop_name, 367*3c4226f9Spjha prop_data); 368*3c4226f9Spjha } 369*3c4226f9Spjha return (rv); 370*3c4226f9Spjha } 371*3c4226f9Spjha 372*3c4226f9Spjha 373*3c4226f9Spjha static int 374*3c4226f9Spjha di_propall_lookup_strings(di_prom_handle_t ph, int flags, 375*3c4226f9Spjha dev_t dev, di_node_t node, const char *prop_name, char **prop_data) 376*3c4226f9Spjha { 377*3c4226f9Spjha int rv; 378*3c4226f9Spjha 379*3c4226f9Spjha if (flags & DIPROP_PRI_PROM) { 380*3c4226f9Spjha rv = di_prom_prop_lookup_strings(ph, node, prop_name, 381*3c4226f9Spjha prop_data); 382*3c4226f9Spjha if (rv < 0) 383*3c4226f9Spjha rv = di_prop_lookup_strings(dev, node, prop_name, 384*3c4226f9Spjha prop_data); 385*3c4226f9Spjha } else { 386*3c4226f9Spjha rv = di_prop_lookup_strings(dev, node, prop_name, prop_data); 387*3c4226f9Spjha if (rv < 0) 388*3c4226f9Spjha rv = di_prom_prop_lookup_strings(ph, node, prop_name, 389*3c4226f9Spjha prop_data); 390*3c4226f9Spjha } 391*3c4226f9Spjha return (rv); 392*3c4226f9Spjha } 393*3c4226f9Spjha 394*3c4226f9Spjha 395*3c4226f9Spjha static di_node_t 396*3c4226f9Spjha pci_cfg_chassis_node(di_node_t node, di_prom_handle_t ph) 397*3c4226f9Spjha { 398*3c4226f9Spjha di_node_t curnode = node; 399*3c4226f9Spjha int *firstchas; 400*3c4226f9Spjha 401*3c4226f9Spjha do { 402*3c4226f9Spjha if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, curnode, 403*3c4226f9Spjha PROP_FIRST_CHAS, &firstchas) >= 0) 404*3c4226f9Spjha return (curnode); 405*3c4226f9Spjha } while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL); 406*3c4226f9Spjha 407*3c4226f9Spjha return (DI_NODE_NIL); 408*3c4226f9Spjha } 409*3c4226f9Spjha 410*3c4226f9Spjha 411*3c4226f9Spjha /* 412*3c4226f9Spjha * yet another redundant common routine to: 413*3c4226f9Spjha * decode the ieee1275 "slot-names" property and returns the string matching 414*3c4226f9Spjha * the pci device number <pci_dev>, if any. 415*3c4226f9Spjha * 416*3c4226f9Spjha * callers must NOT free the returned string 417*3c4226f9Spjha * 418*3c4226f9Spjha * "slot-names" format: [int][string1][string2]...[stringN] 419*3c4226f9Spjha * - each bit position in [int] represent a pci device number 420*3c4226f9Spjha * - [string1]...[stringN] are concatenated null-terminated strings 421*3c4226f9Spjha * - the number of bits set in [int] == the number of strings that follow 422*3c4226f9Spjha * - each bit that is set corresponds to a string in the following segment 423*3c4226f9Spjha */ 424*3c4226f9Spjha static char * 425*3c4226f9Spjha pci_cfg_slotname(di_node_t node, di_prom_handle_t ph, minor_t pci_dev) 426*3c4226f9Spjha { 427*3c4226f9Spjha #ifdef DEBUG 428*3c4226f9Spjha char *fnm = "pci_cfg_slotname"; 429*3c4226f9Spjha #endif 430*3c4226f9Spjha int *snp; 431*3c4226f9Spjha int snlen; 432*3c4226f9Spjha int snentlen = sizeof (int); 433*3c4226f9Spjha int i, max, len, place, curplace; 434*3c4226f9Spjha char *str; 435*3c4226f9Spjha 436*3c4226f9Spjha snlen = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, 437*3c4226f9Spjha PROP_SLOT_NAMES, &snp); 438*3c4226f9Spjha if (snlen < 1) 439*3c4226f9Spjha return (NULL); 440*3c4226f9Spjha if ((snp[0] & (1 << pci_dev)) == 0) 441*3c4226f9Spjha return (NULL); 442*3c4226f9Spjha 443*3c4226f9Spjha /* 444*3c4226f9Spjha * pci device number must be less than the amount of bits in the first 445*3c4226f9Spjha * [int] component of slot-names 446*3c4226f9Spjha */ 447*3c4226f9Spjha if (pci_dev >= snentlen * 8) { 448*3c4226f9Spjha dprint(("%s: pci_dev out of range for %s%d\n", 449*3c4226f9Spjha fnm, DRVINST(node))); 450*3c4226f9Spjha return (NULL); 451*3c4226f9Spjha } 452*3c4226f9Spjha 453*3c4226f9Spjha place = 0; 454*3c4226f9Spjha for (i = 0; i < pci_dev; i++) { 455*3c4226f9Spjha if (snp[0] & (1 << i)) 456*3c4226f9Spjha place++; 457*3c4226f9Spjha } 458*3c4226f9Spjha 459*3c4226f9Spjha max = (snlen * snentlen) - snentlen; 460*3c4226f9Spjha str = (char *)&snp[1]; 461*3c4226f9Spjha i = 0; 462*3c4226f9Spjha curplace = 0; 463*3c4226f9Spjha while (i < max && curplace < place) { 464*3c4226f9Spjha len = strlen(str); 465*3c4226f9Spjha if (len <= 0) 466*3c4226f9Spjha break; 467*3c4226f9Spjha str += len + 1; 468*3c4226f9Spjha i += len + 1; 469*3c4226f9Spjha curplace++; 470*3c4226f9Spjha } 471*3c4226f9Spjha /* the following condition indicates a badly formed slot-names */ 472*3c4226f9Spjha if (i >= max || *str == '\0') { 473*3c4226f9Spjha dprint(("%s: badly formed slot-names for %s%d\n", 474*3c4226f9Spjha fnm, DRVINST(node))); 475*3c4226f9Spjha str = NULL; 476*3c4226f9Spjha } 477*3c4226f9Spjha return (str); 478*3c4226f9Spjha } 479*3c4226f9Spjha 480*3c4226f9Spjha 481*3c4226f9Spjha /* 482*3c4226f9Spjha * returns non-zero if we can return a valid attachment point name for <node>, 483*3c4226f9Spjha * for its slot identified by child pci device number <pci_dev>, through <buf> 484*3c4226f9Spjha * 485*3c4226f9Spjha * prioritized naming scheme: 486*3c4226f9Spjha * 1) <PROP_SLOT_NAMES property> (see pci_cfg_slotname()) 487*3c4226f9Spjha * 2) <device-type><PROP_PHYS_SLOT property> 488*3c4226f9Spjha * 3) <drv name><drv inst>.<device-type><pci_dev> 489*3c4226f9Spjha * 490*3c4226f9Spjha * where <device-type> is derived from the PROP_DEV_TYPE property: 491*3c4226f9Spjha * if its value is "pciex" then <device-type> is "pcie" 492*3c4226f9Spjha * else the raw value is used 493*3c4226f9Spjha * 494*3c4226f9Spjha * if <flags> contains APNODE_DEFNAME, then scheme (3) is used 4957c478bd9Sstevel@tonic-gate */ 4967c478bd9Sstevel@tonic-gate static int 497*3c4226f9Spjha pci_cfg_ap_node(minor_t pci_dev, di_node_t node, di_prom_handle_t ph, 498*3c4226f9Spjha char *buf, int bufsz, int flags) 4997c478bd9Sstevel@tonic-gate { 500*3c4226f9Spjha int *nump; 501*3c4226f9Spjha int rv; 502*3c4226f9Spjha char *str, *devtype; 503*3c4226f9Spjha 504*3c4226f9Spjha rv = di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, node, 505*3c4226f9Spjha PROP_DEV_TYPE, &devtype); 506*3c4226f9Spjha if (rv < 1) 507*3c4226f9Spjha return (0); 508*3c4226f9Spjha 509*3c4226f9Spjha if (strcmp(devtype, PROPVAL_PCIEX) == 0) 510*3c4226f9Spjha devtype = DEVTYPE_PCIE; 511*3c4226f9Spjha 512*3c4226f9Spjha if (flags & APNODE_DEFNAME) 513*3c4226f9Spjha goto DEF; 514*3c4226f9Spjha 515*3c4226f9Spjha str = pci_cfg_slotname(node, ph, pci_dev); 516*3c4226f9Spjha if (str != NULL) { 517*3c4226f9Spjha (void) strlcpy(buf, str, bufsz); 518*3c4226f9Spjha return (1); 519*3c4226f9Spjha } 520*3c4226f9Spjha 521*3c4226f9Spjha if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_PHYS_SLOT, 522*3c4226f9Spjha &nump) > 0) { 523*3c4226f9Spjha if (*nump > 0) { 524*3c4226f9Spjha (void) snprintf(buf, bufsz, "%s%d", devtype, *nump); 525*3c4226f9Spjha return (1); 526*3c4226f9Spjha } 527*3c4226f9Spjha } 528*3c4226f9Spjha DEF: 529*3c4226f9Spjha (void) snprintf(buf, bufsz, "%s%d.%s%d", 530*3c4226f9Spjha di_driver_name(node), di_instance(node), devtype, pci_dev); 531*3c4226f9Spjha 532*3c4226f9Spjha return (1); 533*3c4226f9Spjha } 534*3c4226f9Spjha 535*3c4226f9Spjha 536*3c4226f9Spjha /* 537*3c4226f9Spjha * returns non-zero if we can return a valid expansion chassis name for <node> 538*3c4226f9Spjha * through <buf> 539*3c4226f9Spjha * 540*3c4226f9Spjha * prioritized naming scheme: 541*3c4226f9Spjha * 1) <IOB_PRE string><PROP_SERID property: sun specific portion> 542*3c4226f9Spjha * 2) <IOB_PRE string><full PROP_SERID property in hex> 543*3c4226f9Spjha * 3) <IOB_PRE string> 544*3c4226f9Spjha * 545*3c4226f9Spjha * PROP_SERID encoding <64-bit int: msb ... lsb>: 546*3c4226f9Spjha * <24 bits: vendor id><40 bits: serial number> 547*3c4226f9Spjha * 548*3c4226f9Spjha * sun encoding of 40 bit serial number: 549*3c4226f9Spjha * first byte = device type indicator (ignored in naming scheme) 550*3c4226f9Spjha * next 4 bytes = 4 ascii characters 551*3c4226f9Spjha */ 552*3c4226f9Spjha /*ARGSUSED*/ 553*3c4226f9Spjha static int 554*3c4226f9Spjha pci_cfg_iob_name(di_minor_t minor, di_node_t node, di_prom_handle_t ph, 555*3c4226f9Spjha char *buf, int bufsz) 556*3c4226f9Spjha { 557*3c4226f9Spjha int64_t *seridp; 558*3c4226f9Spjha int64_t serid; 559*3c4226f9Spjha char *idstr; 560*3c4226f9Spjha 561*3c4226f9Spjha if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, PROP_SERID, 562*3c4226f9Spjha &seridp) < 1) { 563*3c4226f9Spjha (void) strlcpy(buf, IOB_PRE, bufsz); 564*3c4226f9Spjha return (1); 565*3c4226f9Spjha } 566*3c4226f9Spjha serid = *seridp; 567*3c4226f9Spjha 568*3c4226f9Spjha if (serid >> 40 != VENDID_SUN) { 569*3c4226f9Spjha (void) snprintf(buf, bufsz, "%s%llx", IOB_PRE, serid); 570*3c4226f9Spjha return (1); 571*3c4226f9Spjha } 572*3c4226f9Spjha 573*3c4226f9Spjha serid &= SIZE2MASK64(40); 574*3c4226f9Spjha idstr = (char *)&serid; 575*3c4226f9Spjha idstr[sizeof (serid) - 1] = '\0'; 576*3c4226f9Spjha /* skip device type indicator */ 577*3c4226f9Spjha idstr++; 578*3c4226f9Spjha (void) snprintf(buf, bufsz, "%s%s", IOB_PRE, idstr); 579*3c4226f9Spjha return (1); 580*3c4226f9Spjha } 581*3c4226f9Spjha 582*3c4226f9Spjha 583*3c4226f9Spjha static minor_t 584*3c4226f9Spjha pci_cfg_pcidev(di_node_t node, di_prom_handle_t ph) 585*3c4226f9Spjha { 586*3c4226f9Spjha int rv; 587*3c4226f9Spjha int *regp; 588*3c4226f9Spjha 589*3c4226f9Spjha rv = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_REG, 590*3c4226f9Spjha ®p); 591*3c4226f9Spjha 592*3c4226f9Spjha if (rv < 1) { 593*3c4226f9Spjha dprint(("pci_cfg_pcidev: property %s not found " 594*3c4226f9Spjha "for %s%d\n", PROP_REG, DRVINST(node))); 595*3c4226f9Spjha return (rv); 596*3c4226f9Spjha } 597*3c4226f9Spjha 598*3c4226f9Spjha return (REG_PCIDEV(regp)); 599*3c4226f9Spjha } 600*3c4226f9Spjha 601*3c4226f9Spjha 602*3c4226f9Spjha /* 603*3c4226f9Spjha * returns non-zero when it can successfully return an attachment point 604*3c4226f9Spjha * through <ap_path> whose length is less than <ap_pathsz>; returns the full 605*3c4226f9Spjha * path of the AP through <pathret> which may be larger than <ap_pathsz>. 606*3c4226f9Spjha * Callers need to free <pathret>. If it cannot return the full path through 607*3c4226f9Spjha * <pathret> it will be set to NULL 608*3c4226f9Spjha * 609*3c4226f9Spjha * The ap path reflects a subset of the device path from an onboard host slot 610*3c4226f9Spjha * up to <node>. We traverse up the device tree starting from <node>, naming 611*3c4226f9Spjha * each component using pci_cfg_ap_node(). If we detect that a certain 612*3c4226f9Spjha * segment is contained within an expansion chassis, then we skip any bus 613*3c4226f9Spjha * nodes in between our current node and the topmost node of the chassis, 614*3c4226f9Spjha * which is identified by the PROP_FIRST_CHAS property, and prepend the name 615*3c4226f9Spjha * of the expansion chassis as given by pci_cfg_iob_name() 616*3c4226f9Spjha * 617*3c4226f9Spjha * This scheme is always used for <pathret>. If however, the size of 618*3c4226f9Spjha * <pathret> is greater than <ap_pathsz> then only the default name as given 619*3c4226f9Spjha * by pci_cfg_ap_node() for <node> will be used 620*3c4226f9Spjha */ 621*3c4226f9Spjha static int 622*3c4226f9Spjha pci_cfg_ap_path(di_minor_t minor, di_node_t node, di_prom_handle_t ph, 623*3c4226f9Spjha char *ap_path, int ap_pathsz, char **pathret) 624*3c4226f9Spjha { 625*3c4226f9Spjha #ifdef DEBUG 626*3c4226f9Spjha char *fnm = "pci_cfg_ap_path"; 627*3c4226f9Spjha #endif 628*3c4226f9Spjha #define seplen (sizeof (AP_PATH_SEP) - 1) 629*3c4226f9Spjha #define iob_pre_len (sizeof (IOB_PRE) - 1) 630*3c4226f9Spjha #define ap_path_iob_sep_len (sizeof (AP_PATH_IOB_SEP) - 1) 631*3c4226f9Spjha 632*3c4226f9Spjha char *bufptr; 633*3c4226f9Spjha char buf[MAXPATHLEN]; 634*3c4226f9Spjha char pathbuf[MAXPATHLEN]; 635*3c4226f9Spjha int bufsz; 636*3c4226f9Spjha char *pathptr; 637*3c4226f9Spjha char *pathend = NULL; 638*3c4226f9Spjha int len; 639*3c4226f9Spjha int rv = 0; 640*3c4226f9Spjha int chasflag = 0; 641*3c4226f9Spjha di_node_t curnode = node; 642*3c4226f9Spjha di_node_t chasnode = DI_NODE_NIL; 6437c478bd9Sstevel@tonic-gate minor_t pci_dev; 6447c478bd9Sstevel@tonic-gate 645*3c4226f9Spjha buf[0] = '\0'; 646*3c4226f9Spjha pathbuf[0] = '\0'; 647*3c4226f9Spjha pathptr = &pathbuf[sizeof (pathbuf) - 1]; 648*3c4226f9Spjha *pathptr = '\0'; 6497c478bd9Sstevel@tonic-gate 650*3c4226f9Spjha /* 651*3c4226f9Spjha * as we traverse up the device tree, we prepend components of our 652*3c4226f9Spjha * path inside pathbuf, using pathptr and decrementing 653*3c4226f9Spjha */ 654*3c4226f9Spjha pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor)); 655*3c4226f9Spjha do { 656*3c4226f9Spjha bufptr = buf; 657*3c4226f9Spjha bufsz = sizeof (buf); 658*3c4226f9Spjha 659*3c4226f9Spjha chasnode = pci_cfg_chassis_node(curnode, ph); 660*3c4226f9Spjha if (chasnode != DI_NODE_NIL) { 661*3c4226f9Spjha rv = pci_cfg_iob_name(minor, chasnode, ph, 662*3c4226f9Spjha bufptr, bufsz); 663*3c4226f9Spjha if (rv == 0) { 664*3c4226f9Spjha dprint(("%s: cannot create iob name " 665*3c4226f9Spjha "for %s%d\n", fnm, DRVINST(node))); 666*3c4226f9Spjha *pathptr = '\0'; 667*3c4226f9Spjha goto OUT; 668*3c4226f9Spjha } 669*3c4226f9Spjha 670*3c4226f9Spjha (void) strncat(bufptr, AP_PATH_IOB_SEP, bufsz); 671*3c4226f9Spjha len = strlen(bufptr); 672*3c4226f9Spjha bufptr += len; 673*3c4226f9Spjha bufsz -= len - 1; 674*3c4226f9Spjha 675*3c4226f9Spjha /* set chasflag when the leaf node is within an iob */ 676*3c4226f9Spjha if ((curnode == node) != NULL) 677*3c4226f9Spjha chasflag = 1; 678*3c4226f9Spjha } 679*3c4226f9Spjha rv = pci_cfg_ap_node(pci_dev, curnode, ph, bufptr, bufsz, 0); 680*3c4226f9Spjha if (rv == 0) { 681*3c4226f9Spjha dprint(("%s: cannot create ap node name " 682*3c4226f9Spjha "for %s%d\n", fnm, DRVINST(node))); 683*3c4226f9Spjha *pathptr = '\0'; 684*3c4226f9Spjha goto OUT; 685*3c4226f9Spjha } 686*3c4226f9Spjha 687*3c4226f9Spjha /* 688*3c4226f9Spjha * if we can't fit the entire path in our pathbuf, then use 689*3c4226f9Spjha * the default short name and nullify pathptr; also, since 690*3c4226f9Spjha * we prepend in the buffer, we must avoid adding a null char 691*3c4226f9Spjha */ 692*3c4226f9Spjha if (curnode != node) { 693*3c4226f9Spjha pathptr -= seplen; 694*3c4226f9Spjha if (pathptr < pathbuf) { 695*3c4226f9Spjha pathptr = pathbuf; 696*3c4226f9Spjha *pathptr = '\0'; 697*3c4226f9Spjha goto DEF; 698*3c4226f9Spjha } 699*3c4226f9Spjha (void) memcpy(pathptr, AP_PATH_SEP, seplen); 700*3c4226f9Spjha } 701*3c4226f9Spjha len = strlen(buf); 702*3c4226f9Spjha pathptr -= len; 703*3c4226f9Spjha if (pathptr < pathbuf) { 704*3c4226f9Spjha pathptr = pathbuf; 705*3c4226f9Spjha *pathptr = '\0'; 706*3c4226f9Spjha goto DEF; 707*3c4226f9Spjha } 708*3c4226f9Spjha (void) memcpy(pathptr, buf, len); 709*3c4226f9Spjha 710*3c4226f9Spjha /* remember the leaf component */ 711*3c4226f9Spjha if (curnode == node) 712*3c4226f9Spjha pathend = pathptr; 713*3c4226f9Spjha 714*3c4226f9Spjha /* 715*3c4226f9Spjha * go no further than the hosts' onboard slots 716*3c4226f9Spjha */ 717*3c4226f9Spjha if (chasnode == DI_NODE_NIL) 718*3c4226f9Spjha break; 719*3c4226f9Spjha curnode = chasnode; 720*3c4226f9Spjha 721*3c4226f9Spjha /* 722*3c4226f9Spjha * the pci device number of the current node is used to 723*3c4226f9Spjha * identify which slot of the parent's bus (next iteration) 724*3c4226f9Spjha * the current node is on 725*3c4226f9Spjha */ 726*3c4226f9Spjha pci_dev = pci_cfg_pcidev(curnode, ph); 727*3c4226f9Spjha } while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL); 728*3c4226f9Spjha 729*3c4226f9Spjha pathbuf[sizeof (pathbuf) - 1] = '\0'; 730*3c4226f9Spjha if (strlen(pathptr) < ap_pathsz) { 731*3c4226f9Spjha (void) strlcpy(ap_path, pathptr, ap_pathsz); 732*3c4226f9Spjha rv = 1; 733*3c4226f9Spjha goto OUT; 734*3c4226f9Spjha } 735*3c4226f9Spjha 736*3c4226f9Spjha DEF: 737*3c4226f9Spjha /* 738*3c4226f9Spjha * when our name won't fit <ap_pathsz> we use the endpoint/leaf 739*3c4226f9Spjha * <node>'s name ONLY IF it has a serialid# which will make the apid 740*3c4226f9Spjha * globally unique 741*3c4226f9Spjha */ 742*3c4226f9Spjha if (chasflag && pathend != NULL) { 743*3c4226f9Spjha if ((strncmp(pathend + iob_pre_len, AP_PATH_IOB_SEP, 744*3c4226f9Spjha ap_path_iob_sep_len) != 0) && 745*3c4226f9Spjha (strlen(pathend) < ap_pathsz)) { 746*3c4226f9Spjha (void) strlcpy(ap_path, pathend, ap_pathsz); 747*3c4226f9Spjha rv = 1; 748*3c4226f9Spjha goto OUT; 7497c478bd9Sstevel@tonic-gate } 7507c478bd9Sstevel@tonic-gate } 7517c478bd9Sstevel@tonic-gate 752*3c4226f9Spjha /* 753*3c4226f9Spjha * if our name still won't fit <ap_pathsz>, then use the leaf <node>'s 754*3c4226f9Spjha * default name 755*3c4226f9Spjha */ 756*3c4226f9Spjha rv = pci_cfg_ap_node(pci_dev, node, ph, buf, bufsz, APNODE_DEFNAME); 757*3c4226f9Spjha if (rv == 0) { 758*3c4226f9Spjha dprint(("%s: cannot create default ap node name for %s%d\n", 759*3c4226f9Spjha fnm, DRVINST(node))); 760*3c4226f9Spjha *pathptr = '\0'; 761*3c4226f9Spjha goto OUT; 762*3c4226f9Spjha } 763*3c4226f9Spjha if (strlen(buf) < ap_pathsz) { 764*3c4226f9Spjha (void) strlcpy(ap_path, buf, ap_pathsz); 765*3c4226f9Spjha rv = 1; 766*3c4226f9Spjha goto OUT; 767*3c4226f9Spjha } 768*3c4226f9Spjha 769*3c4226f9Spjha /* 770*3c4226f9Spjha * in this case, cfgadm goes through an expensive process to generate 771*3c4226f9Spjha * a purely dynamic logical apid: the framework will look through 772*3c4226f9Spjha * the device tree for attachment point minor nodes and will invoke 773*3c4226f9Spjha * each plugin responsible for that attachment point class, and if 774*3c4226f9Spjha * the plugin returns a logical apid that matches the queried apid 775*3c4226f9Spjha * or matches the default apid generated by the cfgadm framework for 776*3c4226f9Spjha * that driver/class (occurs when plugin returns an empty logical apid) 777*3c4226f9Spjha * then that is what it will use 778*3c4226f9Spjha * 779*3c4226f9Spjha * it is doubly expensive because the cfgadm pci plugin itself will 780*3c4226f9Spjha * also search the entire device tree in the absence of a link 781*3c4226f9Spjha */ 782*3c4226f9Spjha rv = 0; 783*3c4226f9Spjha dprint(("%s: cannot create apid for %s%d within length of %d\n", 784*3c4226f9Spjha fnm, DRVINST(node), ap_pathsz)); 785*3c4226f9Spjha 786*3c4226f9Spjha OUT: 787*3c4226f9Spjha ap_path[ap_pathsz - 1] = '\0'; 788*3c4226f9Spjha *pathret = (*pathptr == '\0') ? NULL : strdup(pathptr); 789*3c4226f9Spjha return (rv); 790*3c4226f9Spjha 791*3c4226f9Spjha #undef seplen 792*3c4226f9Spjha #undef iob_pre_len 793*3c4226f9Spjha #undef ap_path_iob_sep_len 794*3c4226f9Spjha } 795*3c4226f9Spjha 796*3c4226f9Spjha 797*3c4226f9Spjha /* 798*3c4226f9Spjha * the PROP_AP_NAMES property contains the first integer section of the 799*3c4226f9Spjha * ieee1275 "slot-names" property and functions as a bitmask; see comment for 800*3c4226f9Spjha * pci_cfg_slotname() 801*3c4226f9Spjha * 802*3c4226f9Spjha * we use the name of the attachment point minor node if its pci device 803*3c4226f9Spjha * number (encoded in the minor number) is allowed by PROP_AP_NAMES 804*3c4226f9Spjha * 805*3c4226f9Spjha * returns non-zero if we return a valid attachment point through <path> 806*3c4226f9Spjha */ 807*3c4226f9Spjha static int 808*3c4226f9Spjha pci_cfg_ap_legacy(di_minor_t minor, di_node_t node, di_prom_handle_t ph, 809*3c4226f9Spjha char *ap_path, int ap_pathsz) 810*3c4226f9Spjha { 811*3c4226f9Spjha minor_t pci_dev; 812*3c4226f9Spjha int *anp; 813*3c4226f9Spjha 814*3c4226f9Spjha if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_AP_NAMES, 815*3c4226f9Spjha &anp) < 1) 816*3c4226f9Spjha return (0); 817*3c4226f9Spjha 818*3c4226f9Spjha pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor)); 819*3c4226f9Spjha if ((*anp & (1 << pci_dev)) == 0) 820*3c4226f9Spjha return (0); 821*3c4226f9Spjha 822*3c4226f9Spjha (void) strlcpy(ap_path, di_minor_name(minor), ap_pathsz); 823*3c4226f9Spjha return (1); 824*3c4226f9Spjha } 825*3c4226f9Spjha 826*3c4226f9Spjha 827*3c4226f9Spjha /* 828*3c4226f9Spjha * determine if <node> qualifies for a path style apid 829*3c4226f9Spjha */ 830*3c4226f9Spjha static int 831*3c4226f9Spjha pci_cfg_is_ap_path(di_node_t node, di_prom_handle_t ph) 832*3c4226f9Spjha { 833*3c4226f9Spjha char *devtype; 834*3c4226f9Spjha di_node_t curnode = node; 835*3c4226f9Spjha 836*3c4226f9Spjha do { 837*3c4226f9Spjha if (di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, curnode, 838*3c4226f9Spjha PROP_DEV_TYPE, &devtype) > 0) 839*3c4226f9Spjha if (strcmp(devtype, PROPVAL_PCIEX) == 0) 840*3c4226f9Spjha return (1); 841*3c4226f9Spjha } while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL); 842*3c4226f9Spjha 843*3c4226f9Spjha return (0); 844*3c4226f9Spjha } 845*3c4226f9Spjha 846*3c4226f9Spjha 847*3c4226f9Spjha /* 848*3c4226f9Spjha * takes a full path as returned by <pathret> from pci_cfg_ap_path() and 849*3c4226f9Spjha * returns an allocated string intendend to be stored in a devlink info (dli) 850*3c4226f9Spjha * file 851*3c4226f9Spjha * 852*3c4226f9Spjha * data format: "Location: <transformed path>" 853*3c4226f9Spjha * where <transformed path> is <path> with occurrances of AP_PATH_SEP 854*3c4226f9Spjha * replaced by "/" 855*3c4226f9Spjha */ 856*3c4226f9Spjha static char * 857*3c4226f9Spjha pci_cfg_info_data(char *path) 858*3c4226f9Spjha { 859*3c4226f9Spjha #define head "Location: " 860*3c4226f9Spjha #define headlen (sizeof (head) - 1) 861*3c4226f9Spjha #define seplen (sizeof (AP_PATH_SEP) - 1) 862*3c4226f9Spjha 863*3c4226f9Spjha char *sep, *prev, *np; 864*3c4226f9Spjha char *newpath; 865*3c4226f9Spjha int pathlen = strlen(path); 866*3c4226f9Spjha int len; 867*3c4226f9Spjha 868*3c4226f9Spjha newpath = malloc(sizeof (char) * (headlen + pathlen + 1)); 869*3c4226f9Spjha np = newpath; 870*3c4226f9Spjha (void) strcpy(np, head); 871*3c4226f9Spjha np += headlen; 872*3c4226f9Spjha 873*3c4226f9Spjha prev = path; 874*3c4226f9Spjha while ((sep = strstr(prev, AP_PATH_SEP)) != NULL) { 875*3c4226f9Spjha len = sep - prev; 876*3c4226f9Spjha (void) memcpy(np, prev, len); 877*3c4226f9Spjha np += len; 878*3c4226f9Spjha *np++ = '/'; 879*3c4226f9Spjha prev = sep + seplen; 880*3c4226f9Spjha } 881*3c4226f9Spjha (void) strcpy(np, prev); 882*3c4226f9Spjha return (newpath); 883*3c4226f9Spjha 884*3c4226f9Spjha #undef head 885*3c4226f9Spjha #undef headlen 886*3c4226f9Spjha #undef seplen 887*3c4226f9Spjha } 888*3c4226f9Spjha 889*3c4226f9Spjha 890*3c4226f9Spjha static void 891*3c4226f9Spjha pci_cfg_rm_link(char *file) 892*3c4226f9Spjha { 893*3c4226f9Spjha char *dlipath; 894*3c4226f9Spjha 895*3c4226f9Spjha dlipath = di_dli_name(file); 896*3c4226f9Spjha (void) unlink(dlipath); 897*3c4226f9Spjha 898*3c4226f9Spjha devfsadm_rm_all(file); 899*3c4226f9Spjha free(dlipath); 900*3c4226f9Spjha } 901*3c4226f9Spjha 902*3c4226f9Spjha /* 903*3c4226f9Spjha * removes all registered devlinks to physical path <physpath> except for 904*3c4226f9Spjha * the devlink <valid> if not NULL; 905*3c4226f9Spjha * <physpath> must include the minor node 906*3c4226f9Spjha */ 907*3c4226f9Spjha static void 908*3c4226f9Spjha pci_cfg_rm_invalid_links(char *physpath, char *valid) 909*3c4226f9Spjha { 910*3c4226f9Spjha char **dnp; 911*3c4226f9Spjha char *cp, *vcp; 912*3c4226f9Spjha int i, dnlen; 913*3c4226f9Spjha 914*3c4226f9Spjha dnp = devfsadm_lookup_dev_names(physpath, NULL, &dnlen); 915*3c4226f9Spjha if (dnp == NULL) 916*3c4226f9Spjha return; 917*3c4226f9Spjha 918*3c4226f9Spjha if (valid != NULL) { 919*3c4226f9Spjha if (strncmp(valid, DEV "/", DEV_LEN + 1) == 0) 920*3c4226f9Spjha vcp = valid + DEV_LEN + 1; 921*3c4226f9Spjha else 922*3c4226f9Spjha vcp = valid; 923*3c4226f9Spjha } 924*3c4226f9Spjha 925*3c4226f9Spjha for (i = 0; i < dnlen; i++) { 926*3c4226f9Spjha if (strncmp(dnp[i], DEV "/", DEV_LEN + 1) == 0) 927*3c4226f9Spjha cp = dnp[i] + DEV_LEN + 1; 928*3c4226f9Spjha else 929*3c4226f9Spjha cp = dnp[i]; 930*3c4226f9Spjha 931*3c4226f9Spjha if (valid != NULL) { 932*3c4226f9Spjha if (strcmp(vcp, cp) == 0) 933*3c4226f9Spjha continue; 934*3c4226f9Spjha } 935*3c4226f9Spjha pci_cfg_rm_link(cp); 936*3c4226f9Spjha } 937*3c4226f9Spjha devfsadm_free_dev_names(dnp, dnlen); 938*3c4226f9Spjha } 939*3c4226f9Spjha 940*3c4226f9Spjha 941*3c4226f9Spjha /* 942*3c4226f9Spjha * takes a complete devinfo snapshot and returns the root node; 943*3c4226f9Spjha * callers must do a di_fini() on the returned node; 944*3c4226f9Spjha * if the snapshot failed, DI_NODE_NIL is returned instead 945*3c4226f9Spjha * 946*3c4226f9Spjha * if <pci_node> is not DI_NODE_NIL, it will search for the same devinfo node 947*3c4226f9Spjha * in the new snapshot and return it through <ret_node> if it is found, 948*3c4226f9Spjha * else DI_NODE_NIL is returned instead 949*3c4226f9Spjha * 950*3c4226f9Spjha * in addition, if <pci_minor> is not DI_MINOR_NIL, it will also return 951*3c4226f9Spjha * the matching minor in the new snapshot through <ret_minor> if it is found, 952*3c4226f9Spjha * else DI_MINOR_NIL is returned instead 953*3c4226f9Spjha */ 954*3c4226f9Spjha static di_node_t 955*3c4226f9Spjha pci_cfg_snapshot(di_node_t pci_node, di_minor_t pci_minor, 956*3c4226f9Spjha di_node_t *ret_node, di_minor_t *ret_minor) 957*3c4226f9Spjha { 958*3c4226f9Spjha di_node_t root_node; 959*3c4226f9Spjha di_node_t node; 960*3c4226f9Spjha di_minor_t minor; 961*3c4226f9Spjha int pci_inst; 962*3c4226f9Spjha dev_t pci_devt; 963*3c4226f9Spjha 964*3c4226f9Spjha *ret_node = DI_NODE_NIL; 965*3c4226f9Spjha *ret_minor = DI_MINOR_NIL; 966*3c4226f9Spjha 967*3c4226f9Spjha root_node = di_init("/", DINFOCPYALL); 968*3c4226f9Spjha if (root_node == DI_NODE_NIL) 969*3c4226f9Spjha return (DI_NODE_NIL); 970*3c4226f9Spjha 971*3c4226f9Spjha /* 972*3c4226f9Spjha * narrow down search by driver, then instance, then minor 973*3c4226f9Spjha */ 974*3c4226f9Spjha if (pci_node == DI_NODE_NIL) 975*3c4226f9Spjha return (root_node); 976*3c4226f9Spjha 977*3c4226f9Spjha pci_inst = di_instance(pci_node); 978*3c4226f9Spjha node = di_drv_first_node(di_driver_name(pci_node), root_node); 979*3c4226f9Spjha do { 980*3c4226f9Spjha if (pci_inst == di_instance(node)) { 981*3c4226f9Spjha *ret_node = node; 982*3c4226f9Spjha break; 983*3c4226f9Spjha } 984*3c4226f9Spjha } while ((node = di_drv_next_node(node)) != DI_NODE_NIL); 985*3c4226f9Spjha 986*3c4226f9Spjha if (node == DI_NODE_NIL) 987*3c4226f9Spjha return (root_node); 988*3c4226f9Spjha 989*3c4226f9Spjha /* 990*3c4226f9Spjha * found node, now search minors 991*3c4226f9Spjha */ 992*3c4226f9Spjha if (pci_minor == DI_MINOR_NIL) 993*3c4226f9Spjha return (root_node); 994*3c4226f9Spjha 995*3c4226f9Spjha pci_devt = di_minor_devt(pci_minor); 996*3c4226f9Spjha minor = DI_MINOR_NIL; 997*3c4226f9Spjha while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 998*3c4226f9Spjha if (pci_devt == di_minor_devt(minor)) { 999*3c4226f9Spjha *ret_minor = minor; 1000*3c4226f9Spjha break; 1001*3c4226f9Spjha } 1002*3c4226f9Spjha } 1003*3c4226f9Spjha return (root_node); 1004*3c4226f9Spjha } 1005*3c4226f9Spjha 1006*3c4226f9Spjha 1007*3c4226f9Spjha static int 1008*3c4226f9Spjha pci_cfg_creat_cb(di_minor_t pci_minor, di_node_t pci_node) 1009*3c4226f9Spjha { 1010*3c4226f9Spjha #ifdef DEBUG 1011*3c4226f9Spjha char *fnm = "pci_cfg_creat_cb"; 1012*3c4226f9Spjha #endif 1013*3c4226f9Spjha #define ap_pathsz (sizeof (ap_path)) 1014*3c4226f9Spjha 1015*3c4226f9Spjha char ap_path[CFGA_LOG_EXT_LEN]; 1016*3c4226f9Spjha char linkbuf[MAXPATHLEN]; 1017*3c4226f9Spjha char *fullpath = NULL; 1018*3c4226f9Spjha char *pathinfo = NULL; 1019*3c4226f9Spjha char *devpath = NULL; 1020*3c4226f9Spjha int rv, fd; 1021*3c4226f9Spjha size_t sz; 1022*3c4226f9Spjha di_prom_handle_t ph; 1023*3c4226f9Spjha di_node_t node; 1024*3c4226f9Spjha di_node_t root_node = DI_NODE_NIL; 1025*3c4226f9Spjha di_minor_t minor; 1026*3c4226f9Spjha 1027*3c4226f9Spjha ph = di_prom_init(); 1028*3c4226f9Spjha if (ph == DI_PROM_HANDLE_NIL) { 1029*3c4226f9Spjha dprint(("%s: di_prom_init() failed for %s%d\n", 1030*3c4226f9Spjha fnm, DRVINST(pci_node))); 1031*3c4226f9Spjha goto OUT; 1032*3c4226f9Spjha } 1033*3c4226f9Spjha 1034*3c4226f9Spjha /* 1035*3c4226f9Spjha * Since incoming nodes from hotplug events are from snapshots that 1036*3c4226f9Spjha * do NOT contain parent/ancestor data, we must retake our own 1037*3c4226f9Spjha * snapshot and search for the target node 1038*3c4226f9Spjha */ 1039*3c4226f9Spjha root_node = pci_cfg_snapshot(pci_node, pci_minor, &node, &minor); 1040*3c4226f9Spjha if (root_node == DI_NODE_NIL || node == DI_NODE_NIL || 1041*3c4226f9Spjha minor == DI_MINOR_NIL) { 1042*3c4226f9Spjha dprint(("%s: devinfo snapshot or search failed for %s%d\n", 1043*3c4226f9Spjha fnm, DRVINST(pci_node))); 1044*3c4226f9Spjha goto OUT; 1045*3c4226f9Spjha } 1046*3c4226f9Spjha 1047*3c4226f9Spjha if (pci_cfg_is_ap_path(node, ph)) { 1048*3c4226f9Spjha rv = pci_cfg_ap_path(minor, node, ph, ap_path, ap_pathsz, 1049*3c4226f9Spjha &fullpath); 1050*3c4226f9Spjha if (rv == 0) 1051*3c4226f9Spjha goto OUT; 1052*3c4226f9Spjha 1053*3c4226f9Spjha (void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s", 1054*3c4226f9Spjha CFG_DIRNAME, ap_path); 1055*3c4226f9Spjha 1056*3c4226f9Spjha /* 1057*3c4226f9Spjha * We must remove existing links because we may have invalid 1058*3c4226f9Spjha * apids that are valid links. Since these are not dangling, 1059*3c4226f9Spjha * devfsadm will not invoke the remove callback on them. 1060*3c4226f9Spjha * 1061*3c4226f9Spjha * What are "invalid apids with valid links"? Consider swapping 1062*3c4226f9Spjha * an attachment point bus with another while the system is 1063*3c4226f9Spjha * down, on the same device path bound to the same drivers 1064*3c4226f9Spjha * but with the new AP bus having different properties 1065*3c4226f9Spjha * (e.g. serialid#). If the previous apid is not removed, 1066*3c4226f9Spjha * there will now be two different links pointing to the same 1067*3c4226f9Spjha * attachment point, but only one reflects the correct 1068*3c4226f9Spjha * logical apid 1069*3c4226f9Spjha */ 1070*3c4226f9Spjha devpath = pci_cfg_devpath(node, minor); 1071*3c4226f9Spjha if (devpath == NULL) 1072*3c4226f9Spjha goto OUT; 1073*3c4226f9Spjha pci_cfg_rm_invalid_links(devpath, linkbuf); 1074*3c4226f9Spjha free(devpath); 1075*3c4226f9Spjha 1076*3c4226f9Spjha (void) devfsadm_mklink(linkbuf, node, minor, 0); 1077*3c4226f9Spjha 1078*3c4226f9Spjha /* 1079*3c4226f9Spjha * we store the full logical path of the attachment point for 1080*3c4226f9Spjha * cfgadm to display in its info field which is useful when 1081*3c4226f9Spjha * the full logical path exceeds the size limit for logical 1082*3c4226f9Spjha * apids (CFGA_LOG_EXT_LEN) 1083*3c4226f9Spjha * 1084*3c4226f9Spjha * for the cfgadm pci plugin to do the same would be expensive 1085*3c4226f9Spjha * (i.e. devinfo snapshot + top down exhaustive minor search + 1086*3c4226f9Spjha * equivalent of pci_cfg_ap_path() on every invocation) 1087*3c4226f9Spjha * 1088*3c4226f9Spjha * note that if we do not create a link (pci_cfg_ap_path() is 1089*3c4226f9Spjha * not successful), that is what cfgadm will do anyways to 1090*3c4226f9Spjha * create a purely dynamic apid 1091*3c4226f9Spjha */ 1092*3c4226f9Spjha pathinfo = pci_cfg_info_data(fullpath); 1093*3c4226f9Spjha fd = di_dli_openw(linkbuf); 1094*3c4226f9Spjha if (fd < 0) 1095*3c4226f9Spjha goto OUT; 1096*3c4226f9Spjha 1097*3c4226f9Spjha sz = strlen(pathinfo) + 1; 1098*3c4226f9Spjha rv = write(fd, pathinfo, sz); 1099*3c4226f9Spjha if (rv < sz) { 1100*3c4226f9Spjha dprint(("%s: could not write full pathinfo to dli " 1101*3c4226f9Spjha "file for %s%d\n", fnm, DRVINST(node))); 1102*3c4226f9Spjha goto OUT; 1103*3c4226f9Spjha } 1104*3c4226f9Spjha di_dli_close(fd); 1105*3c4226f9Spjha } else { 1106*3c4226f9Spjha rv = pci_cfg_ap_legacy(minor, node, ph, ap_path, 1107*3c4226f9Spjha ap_pathsz); 1108*3c4226f9Spjha if (rv == 0) 1109*3c4226f9Spjha goto OUT; 1110*3c4226f9Spjha 1111*3c4226f9Spjha (void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s", 1112*3c4226f9Spjha CFG_DIRNAME, ap_path); 1113*3c4226f9Spjha (void) devfsadm_mklink(linkbuf, node, minor, 0); 1114*3c4226f9Spjha } 1115*3c4226f9Spjha 1116*3c4226f9Spjha OUT: 1117*3c4226f9Spjha if (fullpath != NULL) 1118*3c4226f9Spjha free(fullpath); 1119*3c4226f9Spjha if (pathinfo != NULL) 1120*3c4226f9Spjha free(pathinfo); 1121*3c4226f9Spjha if (ph != DI_PROM_HANDLE_NIL) 1122*3c4226f9Spjha di_prom_fini(ph); 1123*3c4226f9Spjha if (root_node != DI_NODE_NIL) 1124*3c4226f9Spjha di_fini(root_node); 11257c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 1126*3c4226f9Spjha 1127*3c4226f9Spjha #undef ap_pathsz 1128*3c4226f9Spjha } 1129*3c4226f9Spjha 1130*3c4226f9Spjha 1131*3c4226f9Spjha static void 1132*3c4226f9Spjha pci_cfg_rm_all(char *file) 1133*3c4226f9Spjha { 1134*3c4226f9Spjha pci_cfg_rm_link(file); 11357c478bd9Sstevel@tonic-gate } 11367c478bd9Sstevel@tonic-gate 11377c478bd9Sstevel@tonic-gate 11387c478bd9Sstevel@tonic-gate /* 11397c478bd9Sstevel@tonic-gate * ib_cfg_creat_cb() creates two types of links 11407c478bd9Sstevel@tonic-gate * One for the fabric as /dev/cfg/ib 11417c478bd9Sstevel@tonic-gate * Another for each HCA seen in the fabric as /dev/cfg/hca:<HCA-GUID> 11427c478bd9Sstevel@tonic-gate */ 11437c478bd9Sstevel@tonic-gate static int 11447c478bd9Sstevel@tonic-gate ib_cfg_creat_cb(di_minor_t minor, di_node_t node) 11457c478bd9Sstevel@tonic-gate { 11467c478bd9Sstevel@tonic-gate char *cp; 11477c478bd9Sstevel@tonic-gate char path[PATH_MAX + 1]; 11487c478bd9Sstevel@tonic-gate 11497c478bd9Sstevel@tonic-gate if ((cp = di_devfs_path(node)) == NULL) { 11507c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 11517c478bd9Sstevel@tonic-gate } 11527c478bd9Sstevel@tonic-gate 11537c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor)); 11547c478bd9Sstevel@tonic-gate di_devfs_path_free(cp); 11557c478bd9Sstevel@tonic-gate 11567c478bd9Sstevel@tonic-gate /* create fabric or hca:GUID and the symlink */ 11577c478bd9Sstevel@tonic-gate if (strstr(path, "ib:fabric") != NULL) { 11587c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s/ib", CFG_DIRNAME); 11597c478bd9Sstevel@tonic-gate } else { 11607c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s/hca:%s", CFG_DIRNAME, 11617c478bd9Sstevel@tonic-gate di_minor_name(minor)); 11627c478bd9Sstevel@tonic-gate } 11637c478bd9Sstevel@tonic-gate 11647c478bd9Sstevel@tonic-gate (void) devfsadm_mklink(path, node, minor, 0); 11657c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 11667c478bd9Sstevel@tonic-gate } 1167