xref: /onnv-gate/usr/src/cmd/devfsadm/cfg_link.c (revision 9430:637732b28916)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51258Smlf  * Common Development and Distribution License (the "License").
61258Smlf  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211258Smlf 
220Sstevel@tonic-gate /*
23*9430SRaymond.Chen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <devfsadm.h>
280Sstevel@tonic-gate #include <stdio.h>
290Sstevel@tonic-gate #include <strings.h>
300Sstevel@tonic-gate #include <stdlib.h>
312587Spjha #include <stdarg.h>
320Sstevel@tonic-gate #include <limits.h>
332587Spjha #include <unistd.h>
342587Spjha #include <config_admin.h>
352587Spjha #include <cfg_link.h>
362587Spjha #include <sys/types.h>
372587Spjha #include <sys/mkdev.h>
382587Spjha #include <sys/hotplug/pci/pcihp.h>
390Sstevel@tonic-gate 
402587Spjha #ifdef	DEBUG
412587Spjha #define	dprint(args)	devfsadm_errprint args
422587Spjha /*
432587Spjha  * for use in print routine arg list as a shorthand way to locate node via
442587Spjha  * "prtconf -D" to avoid messy and cluttered debugging code
452587Spjha  * don't forget the corresponding "%s%d" format
462587Spjha  */
472587Spjha #define	DRVINST(node)	di_driver_name(node), di_instance(node)
482587Spjha #else
492587Spjha #define	dprint(args)
502587Spjha #endif
510Sstevel@tonic-gate 
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static int	scsi_cfg_creat_cb(di_minor_t minor, di_node_t node);
540Sstevel@tonic-gate static int	sbd_cfg_creat_cb(di_minor_t minor, di_node_t node);
550Sstevel@tonic-gate static int	usb_cfg_creat_cb(di_minor_t minor, di_node_t node);
560Sstevel@tonic-gate static char	*get_roothub(const char *path, void *cb_arg);
570Sstevel@tonic-gate static int	pci_cfg_creat_cb(di_minor_t minor, di_node_t node);
580Sstevel@tonic-gate static int	ib_cfg_creat_cb(di_minor_t minor, di_node_t node);
591258Smlf static int	sata_cfg_creat_cb(di_minor_t minor, di_node_t node);
607302Sgdamore@opensolaris.org static int	sdcard_cfg_creat_cb(di_minor_t minor, di_node_t node);
610Sstevel@tonic-gate 
622587Spjha static di_node_t	pci_cfg_chassis_node(di_node_t, di_prom_handle_t);
632587Spjha static char 	*pci_cfg_slotname(di_node_t, di_prom_handle_t, minor_t);
642587Spjha static int	pci_cfg_ap_node(minor_t, di_node_t, di_prom_handle_t,
652587Spjha 		    char *, int, int);
662587Spjha static int	pci_cfg_iob_name(di_minor_t, di_node_t, di_prom_handle_t,
672587Spjha 		    char *, int);
682587Spjha static minor_t	pci_cfg_pcidev(di_node_t, di_prom_handle_t);
692587Spjha static int	pci_cfg_ap_path(di_minor_t, di_node_t, di_prom_handle_t,
702587Spjha 		    char *, int, char **);
712587Spjha static char 	*pci_cfg_info_data(char *);
722587Spjha static int	pci_cfg_is_ap_path(di_node_t, di_prom_handle_t);
732587Spjha static int	pci_cfg_ap_legacy(di_minor_t, di_node_t, di_prom_handle_t,
742587Spjha 		    char *, int);
752587Spjha static void	pci_cfg_rm_invalid_links(char *, char *);
762587Spjha static void	pci_cfg_rm_link(char *);
772587Spjha static void	pci_cfg_rm_all(char *);
782587Spjha static char	*pci_cfg_devpath(di_node_t, di_minor_t);
792587Spjha static di_node_t	pci_cfg_snapshot(di_node_t, di_minor_t,
802587Spjha 			    di_node_t *, di_minor_t *);
812587Spjha 
822587Spjha /* flag definitions for di_propall_*(); value "0" is always the default flag */
832587Spjha #define	DIPROP_PRI_NODE		0x0
842587Spjha #define	DIPROP_PRI_PROM		0x1
852587Spjha static int	di_propall_lookup_ints(di_prom_handle_t, int,
862587Spjha 		    dev_t, di_node_t, const char *, int **);
872587Spjha static int	di_propall_lookup_strings(di_prom_handle_t, int,
882587Spjha 		    dev_t, di_node_t, const char *, char **);
893793Spjha static int 	serid_printable(uint64_t *seridp);
903814Sjveta static int	di_propall_lookup_slot_names(di_prom_handle_t, int,
913814Sjveta 		    dev_t, di_node_t, di_slot_name_t **);
922587Spjha 
932587Spjha 
940Sstevel@tonic-gate /*
950Sstevel@tonic-gate  * NOTE: The CREATE_DEFER flag is private to this module.
960Sstevel@tonic-gate  *	 NOT to be used by other modules
970Sstevel@tonic-gate  */
980Sstevel@tonic-gate static devfsadm_create_t cfg_create_cbt[] = {
992587Spjha 	{ "attachment-point", DDI_NT_SCSI_ATTACHMENT_POINT, NULL,
1000Sstevel@tonic-gate 	    TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
1010Sstevel@tonic-gate 	},
1022587Spjha 	{ "attachment-point", DDI_NT_SBD_ATTACHMENT_POINT, NULL,
1030Sstevel@tonic-gate 	    TYPE_EXACT, ILEVEL_0, sbd_cfg_creat_cb
1040Sstevel@tonic-gate 	},
1052587Spjha 	{ "fc-attachment-point", DDI_NT_FC_ATTACHMENT_POINT, NULL,
1060Sstevel@tonic-gate 	    TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
1070Sstevel@tonic-gate 	},
1082587Spjha 	{ "attachment-point", DDI_NT_USB_ATTACHMENT_POINT, NULL,
1090Sstevel@tonic-gate 	    TYPE_EXACT, ILEVEL_0, usb_cfg_creat_cb
1100Sstevel@tonic-gate 	},
1112587Spjha 	{ "attachment-point", DDI_NT_PCI_ATTACHMENT_POINT, NULL,
1120Sstevel@tonic-gate 	    TYPE_EXACT, ILEVEL_0, pci_cfg_creat_cb
1130Sstevel@tonic-gate 	},
1142587Spjha 	{ "attachment-point", DDI_NT_IB_ATTACHMENT_POINT, NULL,
1150Sstevel@tonic-gate 	    TYPE_EXACT, ILEVEL_0, ib_cfg_creat_cb
1161258Smlf 	},
1172587Spjha 	{ "attachment-point", DDI_NT_SATA_ATTACHMENT_POINT, NULL,
1181258Smlf 	    TYPE_EXACT, ILEVEL_0, sata_cfg_creat_cb
1197302Sgdamore@opensolaris.org 	},
1207302Sgdamore@opensolaris.org 	{ "attachment-point", DDI_NT_SDCARD_ATTACHMENT_POINT, NULL,
1217302Sgdamore@opensolaris.org 	    TYPE_EXACT, ILEVEL_0, sdcard_cfg_creat_cb
1220Sstevel@tonic-gate 	}
1230Sstevel@tonic-gate };
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate DEVFSADM_CREATE_INIT_V0(cfg_create_cbt);
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate static devfsadm_remove_t cfg_remove_cbt[] = {
1280Sstevel@tonic-gate 	{ "attachment-point", SCSI_CFG_LINK_RE, RM_POST,
1290Sstevel@tonic-gate 	    ILEVEL_0, devfsadm_rm_all
1300Sstevel@tonic-gate 	},
1310Sstevel@tonic-gate 	{ "attachment-point", SBD_CFG_LINK_RE, RM_POST,
1320Sstevel@tonic-gate 	    ILEVEL_0, devfsadm_rm_all
1330Sstevel@tonic-gate 	},
1340Sstevel@tonic-gate 	{ "fc-attachment-point", SCSI_CFG_LINK_RE, RM_POST,
1350Sstevel@tonic-gate 	    ILEVEL_0, devfsadm_rm_all
1360Sstevel@tonic-gate 	},
1370Sstevel@tonic-gate 	{ "attachment-point", USB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
1380Sstevel@tonic-gate 	    ILEVEL_0, devfsadm_rm_all
1390Sstevel@tonic-gate 	},
1400Sstevel@tonic-gate 	{ "attachment-point", PCI_CFG_LINK_RE, RM_POST,
1410Sstevel@tonic-gate 	    ILEVEL_0, devfsadm_rm_all
1420Sstevel@tonic-gate 	},
1432587Spjha 	{ "attachment-point", PCI_CFG_PATH_LINK_RE, RM_POST|RM_HOT,
1442587Spjha 	    ILEVEL_0, pci_cfg_rm_all
1452587Spjha 	},
1460Sstevel@tonic-gate 	{ "attachment-point", IB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
1470Sstevel@tonic-gate 	    ILEVEL_0, devfsadm_rm_all
1481258Smlf 	},
1491258Smlf 	{ "attachment-point", SATA_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
1501258Smlf 	    ILEVEL_0, devfsadm_rm_all
1517302Sgdamore@opensolaris.org 	},
1527302Sgdamore@opensolaris.org 	{ "attachment-point", SDCARD_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
1537302Sgdamore@opensolaris.org 	    ILEVEL_0, devfsadm_rm_all
1547302Sgdamore@opensolaris.org 	},
1550Sstevel@tonic-gate };
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate DEVFSADM_REMOVE_INIT_V0(cfg_remove_cbt);
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate static int
scsi_cfg_creat_cb(di_minor_t minor,di_node_t node)1600Sstevel@tonic-gate scsi_cfg_creat_cb(di_minor_t minor, di_node_t node)
1610Sstevel@tonic-gate {
1620Sstevel@tonic-gate 	char path[PATH_MAX + 1];
1630Sstevel@tonic-gate 	char *c_num = NULL, *devfs_path, *mn;
1640Sstevel@tonic-gate 	devfsadm_enumerate_t rules[3] = {
1650Sstevel@tonic-gate 	    {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT},
1660Sstevel@tonic-gate 	    {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR},
1670Sstevel@tonic-gate 	    {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT}
1680Sstevel@tonic-gate 	};
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	mn = di_minor_name(minor);
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 	if ((devfs_path = di_devfs_path(node)) == NULL) {
1730Sstevel@tonic-gate 		return (DEVFSADM_CONTINUE);
1740Sstevel@tonic-gate 	}
1750Sstevel@tonic-gate 	(void) strcpy(path, devfs_path);
1760Sstevel@tonic-gate 	(void) strcat(path, ":");
1770Sstevel@tonic-gate 	(void) strcat(path, mn);
1780Sstevel@tonic-gate 	di_devfs_path_free(devfs_path);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	if (devfsadm_enumerate_int(path, 1, &c_num, rules, 3)
1810Sstevel@tonic-gate 	    == DEVFSADM_FAILURE) {
1820Sstevel@tonic-gate 		/*
1830Sstevel@tonic-gate 		 * Unlike the disks module we don't retry on failure.
1840Sstevel@tonic-gate 		 * If we have multiple "c" numbers for a single physical
1850Sstevel@tonic-gate 		 * controller due to bug 4045879, we will not assign a
1860Sstevel@tonic-gate 		 * c-number/symlink for the controller.
1870Sstevel@tonic-gate 		 */
1880Sstevel@tonic-gate 		return (DEVFSADM_CONTINUE);
1890Sstevel@tonic-gate 	}
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	(void) strcpy(path, CFG_DIRNAME);
1920Sstevel@tonic-gate 	(void) strcat(path, "/c");
1930Sstevel@tonic-gate 	(void) strcat(path, c_num);
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	free(c_num);
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	(void) devfsadm_mklink(path, node, minor, 0);
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	return (DEVFSADM_CONTINUE);
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate static int
sbd_cfg_creat_cb(di_minor_t minor,di_node_t node)2030Sstevel@tonic-gate sbd_cfg_creat_cb(di_minor_t minor, di_node_t node)
2040Sstevel@tonic-gate {
2050Sstevel@tonic-gate 	char path[PATH_MAX + 1];
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	(void) strcpy(path, CFG_DIRNAME);
2080Sstevel@tonic-gate 	(void) strcat(path, "/");
2090Sstevel@tonic-gate 	(void) strcat(path, di_minor_name(minor));
2100Sstevel@tonic-gate 	(void) devfsadm_mklink(path, node, minor, 0);
2110Sstevel@tonic-gate 	return (DEVFSADM_CONTINUE);
2120Sstevel@tonic-gate }
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate static int
usb_cfg_creat_cb(di_minor_t minor,di_node_t node)2160Sstevel@tonic-gate usb_cfg_creat_cb(di_minor_t minor, di_node_t node)
2170Sstevel@tonic-gate {
2180Sstevel@tonic-gate 	char *cp, path[PATH_MAX + 1];
2190Sstevel@tonic-gate 	devfsadm_enumerate_t rules[1] =
2200Sstevel@tonic-gate 		{"^cfg$/^usb([0-9]+)$", 1, MATCH_CALLBACK, NULL, get_roothub};
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	if ((cp = di_devfs_path(node)) == NULL) {
2230Sstevel@tonic-gate 		return (DEVFSADM_CONTINUE);
2240Sstevel@tonic-gate 	}
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
2270Sstevel@tonic-gate 	di_devfs_path_free(cp);
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	if (devfsadm_enumerate_int(path, 0, &cp, rules, 1)) {
2300Sstevel@tonic-gate 		return (DEVFSADM_CONTINUE);
2310Sstevel@tonic-gate 	}
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 	/* create usbN and the symlink */
2340Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s/usb%s/%s", CFG_DIRNAME, cp,
2350Sstevel@tonic-gate 	    di_minor_name(minor));
2360Sstevel@tonic-gate 	free(cp);
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	(void) devfsadm_mklink(path, node, minor, 0);
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	return (DEVFSADM_CONTINUE);
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 
2441258Smlf static int
sata_cfg_creat_cb(di_minor_t minor,di_node_t node)2451258Smlf sata_cfg_creat_cb(di_minor_t minor, di_node_t node)
2461258Smlf {
2471258Smlf 	char path[PATH_MAX + 1], l_path[PATH_MAX], *buf, *devfspath;
2481258Smlf 	char *minor_nm;
2491258Smlf 	devfsadm_enumerate_t rules[1] =
2501258Smlf 		{"^cfg$/^sata([0-9]+)$", 1, MATCH_ADDR};
2511258Smlf 
2521258Smlf 	minor_nm = di_minor_name(minor);
2531258Smlf 	if (minor_nm == NULL)
2541258Smlf 		return (DEVFSADM_CONTINUE);
2551258Smlf 
2561258Smlf 	devfspath = di_devfs_path(node);
2571258Smlf 	if (devfspath == NULL)
2581258Smlf 		return (DEVFSADM_CONTINUE);
2591258Smlf 
2601258Smlf 	(void) strlcpy(path, devfspath, sizeof (path));
2611258Smlf 	(void) strlcat(path, ":", sizeof (path));
2621258Smlf 	(void) strlcat(path, minor_nm, sizeof (path));
2631258Smlf 	di_devfs_path_free(devfspath);
2641258Smlf 
2651258Smlf 	/* build the physical path from the components */
2661258Smlf 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1) ==
2671258Smlf 	    DEVFSADM_FAILURE) {
2681258Smlf 		return (DEVFSADM_CONTINUE);
2691258Smlf 	}
2701258Smlf 
2711258Smlf 	(void) snprintf(l_path, sizeof (l_path), "%s/sata%s/%s", CFG_DIRNAME,
2727302Sgdamore@opensolaris.org 	    buf, minor_nm);
2731258Smlf 	free(buf);
2741258Smlf 
2751258Smlf 	(void) devfsadm_mklink(l_path, node, minor, 0);
2761258Smlf 
2771258Smlf 	return (DEVFSADM_CONTINUE);
2781258Smlf }
2791258Smlf 
2807302Sgdamore@opensolaris.org static int
sdcard_cfg_creat_cb(di_minor_t minor,di_node_t node)2817302Sgdamore@opensolaris.org sdcard_cfg_creat_cb(di_minor_t minor, di_node_t node)
2827302Sgdamore@opensolaris.org {
2837302Sgdamore@opensolaris.org 	char path[PATH_MAX +1], l_path[PATH_MAX], *buf, *devfspath;
2847302Sgdamore@opensolaris.org 	char *minor_nm;
2857302Sgdamore@opensolaris.org 	devfsadm_enumerate_t rules[1] =
2867302Sgdamore@opensolaris.org 	    {"^cfg$/^sdcard([0-9]+)$", 1, MATCH_ADDR};
2877302Sgdamore@opensolaris.org 
2887302Sgdamore@opensolaris.org 	minor_nm = di_minor_name(minor);
2897302Sgdamore@opensolaris.org 	if (minor_nm == NULL)
2907302Sgdamore@opensolaris.org 		return (DEVFSADM_CONTINUE);
2917302Sgdamore@opensolaris.org 
2927302Sgdamore@opensolaris.org 	devfspath = di_devfs_path(node);
2937302Sgdamore@opensolaris.org 	if (devfspath == NULL)
2947302Sgdamore@opensolaris.org 		return (DEVFSADM_CONTINUE);
2957302Sgdamore@opensolaris.org 
2967302Sgdamore@opensolaris.org 	(void) snprintf(path, sizeof (path), "%s:%s", devfspath, minor_nm);
2977302Sgdamore@opensolaris.org 	di_devfs_path_free(devfspath);
2987302Sgdamore@opensolaris.org 
2997302Sgdamore@opensolaris.org 	/* build the physical path from the components */
3007302Sgdamore@opensolaris.org 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1) ==
3017302Sgdamore@opensolaris.org 	    DEVFSADM_FAILURE) {
3027302Sgdamore@opensolaris.org 		return (DEVFSADM_CONTINUE);
3037302Sgdamore@opensolaris.org 	}
3047302Sgdamore@opensolaris.org 
3057302Sgdamore@opensolaris.org 	(void) snprintf(l_path, sizeof (l_path), "%s/sdcard%s/%s",
3067302Sgdamore@opensolaris.org 	    CFG_DIRNAME, buf, minor_nm);
3077302Sgdamore@opensolaris.org 	free(buf);
3087302Sgdamore@opensolaris.org 
3097302Sgdamore@opensolaris.org 	(void) devfsadm_mklink(l_path, node, minor, 0);
3107302Sgdamore@opensolaris.org 
3117302Sgdamore@opensolaris.org 	return (DEVFSADM_CONTINUE);
3127302Sgdamore@opensolaris.org }
3131258Smlf 
3140Sstevel@tonic-gate /*
3150Sstevel@tonic-gate  * get_roothub:
3160Sstevel@tonic-gate  *	figure out the root hub path to calculate /dev/cfg/usbN
3170Sstevel@tonic-gate  */
3180Sstevel@tonic-gate /* ARGSUSED */
3190Sstevel@tonic-gate static char *
get_roothub(const char * path,void * cb_arg)3200Sstevel@tonic-gate get_roothub(const char *path, void *cb_arg)
3210Sstevel@tonic-gate {
3220Sstevel@tonic-gate 	int  i, count = 0;
3230Sstevel@tonic-gate 	char *physpath, *cp;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	/* make a copy */
3260Sstevel@tonic-gate 	if ((physpath = strdup(path)) == NULL) {
3270Sstevel@tonic-gate 		return (NULL);
3280Sstevel@tonic-gate 	}
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	/*
3310Sstevel@tonic-gate 	 * physpath must always have a minor name component
3320Sstevel@tonic-gate 	 */
3330Sstevel@tonic-gate 	if ((cp = strrchr(physpath, ':')) == NULL) {
3340Sstevel@tonic-gate 		free(physpath);
3350Sstevel@tonic-gate 		return (NULL);
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate 	*cp++ = '\0';
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	/*
3400Sstevel@tonic-gate 	 * No '.' in the minor name indicates a roothub port.
3410Sstevel@tonic-gate 	 */
3420Sstevel@tonic-gate 	if (strchr(cp, '.') == NULL) {
3430Sstevel@tonic-gate 		/* roothub device */
3440Sstevel@tonic-gate 		return (physpath);
3450Sstevel@tonic-gate 	}
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	while (*cp) {
3480Sstevel@tonic-gate 		if (*cp == '.')
3490Sstevel@tonic-gate 			count++;
3500Sstevel@tonic-gate 		cp++;
3510Sstevel@tonic-gate 	}
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	/* Remove as many trailing path components as there are '.'s */
3540Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
3550Sstevel@tonic-gate 		if ((cp = strrchr(physpath, '/')) == NULL || (cp == physpath)) {
3560Sstevel@tonic-gate 			free(physpath);
3570Sstevel@tonic-gate 			return (NULL);
3580Sstevel@tonic-gate 		}
359*9430SRaymond.Chen@Sun.COM 		/*
360*9430SRaymond.Chen@Sun.COM 		 * Check if there is any usb_mid node in the middle
361*9430SRaymond.Chen@Sun.COM 		 * and remove the node as if there is an extra '.'
362*9430SRaymond.Chen@Sun.COM 		 */
363*9430SRaymond.Chen@Sun.COM 		if (strstr(cp, "miscellaneous") != NULL) {
364*9430SRaymond.Chen@Sun.COM 			count++;
365*9430SRaymond.Chen@Sun.COM 		}
3660Sstevel@tonic-gate 		*cp = '\0';
3670Sstevel@tonic-gate 	}
3680Sstevel@tonic-gate 
369*9430SRaymond.Chen@Sun.COM 	/* Remove the usb_mid node immediately before the trailing path */
370*9430SRaymond.Chen@Sun.COM 	if ((cp = strrchr(physpath, '/')) != NULL && (cp != physpath)) {
371*9430SRaymond.Chen@Sun.COM 		if (strstr(cp, "miscellaneous") != NULL) {
372*9430SRaymond.Chen@Sun.COM 			*cp = '\0';
373*9430SRaymond.Chen@Sun.COM 		}
374*9430SRaymond.Chen@Sun.COM 	}
375*9430SRaymond.Chen@Sun.COM 
3760Sstevel@tonic-gate 	return (physpath);
3770Sstevel@tonic-gate }
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate /*
3812587Spjha  * returns an allocted string containing the device path for <node> and
3822587Spjha  * <minor>
3832587Spjha  */
3842587Spjha static char *
pci_cfg_devpath(di_node_t node,di_minor_t minor)3852587Spjha pci_cfg_devpath(di_node_t node, di_minor_t minor)
3862587Spjha {
3872587Spjha 	char *path;
3882587Spjha 	char *bufp;
3892587Spjha 	char *minor_nm;
3902587Spjha 	int buflen;
3912587Spjha 
3922587Spjha 	path = di_devfs_path(node);
3932587Spjha 	minor_nm = di_minor_name(minor);
3942955Spjha 	buflen = snprintf(NULL, 0, "%s:%s", path, minor_nm) + 1;
3952587Spjha 
3962587Spjha 	bufp = malloc(sizeof (char) * buflen);
3972955Spjha 	if (bufp != NULL)
3982955Spjha 		(void) snprintf(bufp, buflen, "%s:%s", path, minor_nm);
3992587Spjha 
4002587Spjha 	di_devfs_path_free(path);
4012587Spjha 	return (bufp);
4022587Spjha }
4032587Spjha 
4042587Spjha 
4052587Spjha static int
di_propall_lookup_ints(di_prom_handle_t ph,int flags,dev_t dev,di_node_t node,const char * prop_name,int ** prop_data)4062587Spjha di_propall_lookup_ints(di_prom_handle_t ph, int flags,
4072587Spjha     dev_t dev, di_node_t node, const char *prop_name, int **prop_data)
4082587Spjha {
4092587Spjha 	int rv;
4102587Spjha 
4112587Spjha 	if (flags & DIPROP_PRI_PROM) {
4122587Spjha 		rv = di_prom_prop_lookup_ints(ph, node, prop_name, prop_data);
4132587Spjha 		if (rv < 0)
4142587Spjha 			rv = di_prop_lookup_ints(dev, node, prop_name,
4152587Spjha 			    prop_data);
4162587Spjha 	} else {
4172587Spjha 		rv = di_prop_lookup_ints(dev, node, prop_name, prop_data);
4182587Spjha 		if (rv < 0)
4192587Spjha 			rv = di_prom_prop_lookup_ints(ph, node, prop_name,
4202587Spjha 			    prop_data);
4212587Spjha 	}
4222587Spjha 	return (rv);
4232587Spjha }
4242587Spjha 
4252587Spjha 
4262587Spjha static int
di_propall_lookup_strings(di_prom_handle_t ph,int flags,dev_t dev,di_node_t node,const char * prop_name,char ** prop_data)4272587Spjha di_propall_lookup_strings(di_prom_handle_t ph, int flags,
4282587Spjha     dev_t dev, di_node_t node, const char *prop_name, char **prop_data)
4292587Spjha {
4302587Spjha 	int rv;
4312587Spjha 
4322587Spjha 	if (flags & DIPROP_PRI_PROM) {
4332587Spjha 		rv = di_prom_prop_lookup_strings(ph, node, prop_name,
4342587Spjha 		    prop_data);
4352587Spjha 		if (rv < 0)
4362587Spjha 			rv = di_prop_lookup_strings(dev, node, prop_name,
4372587Spjha 			    prop_data);
4382587Spjha 	} else {
4392587Spjha 		rv = di_prop_lookup_strings(dev, node, prop_name, prop_data);
4402587Spjha 		if (rv < 0)
4412587Spjha 			rv = di_prom_prop_lookup_strings(ph, node, prop_name,
4422587Spjha 			    prop_data);
4432587Spjha 	}
4442587Spjha 	return (rv);
4452587Spjha }
4462587Spjha 
4472587Spjha 
4482587Spjha static di_node_t
pci_cfg_chassis_node(di_node_t node,di_prom_handle_t ph)4492587Spjha pci_cfg_chassis_node(di_node_t node, di_prom_handle_t ph)
4502587Spjha {
4512587Spjha 	di_node_t curnode = node;
4522587Spjha 	int *firstchas;
4532587Spjha 
4542587Spjha 	do {
4552587Spjha 		if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, curnode,
4563814Sjveta 		    DI_PROP_FIRST_CHAS, &firstchas) >= 0)
4572587Spjha 			return (curnode);
4582587Spjha 	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
4592587Spjha 
4602587Spjha 	return (DI_NODE_NIL);
4612587Spjha }
4622587Spjha 
4632587Spjha 
4643814Sjveta static int
di_propall_lookup_slot_names(di_prom_handle_t ph,int flags,dev_t dev,di_node_t node,di_slot_name_t ** prop_data)4653814Sjveta di_propall_lookup_slot_names(di_prom_handle_t ph, int flags,
4663814Sjveta     dev_t dev, di_node_t node, di_slot_name_t **prop_data)
4673814Sjveta {
4683814Sjveta 	int rv;
4693814Sjveta 
4703814Sjveta 	if (flags & DIPROP_PRI_PROM) {
4713814Sjveta 		rv = di_prom_prop_lookup_slot_names(ph, node, prop_data);
4723814Sjveta 		if (rv < 0)
4733814Sjveta 			rv = di_prop_lookup_slot_names(dev, node, prop_data);
4743814Sjveta 	} else {
4753814Sjveta 		rv = di_prop_lookup_slot_names(dev, node, prop_data);
4763814Sjveta 		if (rv < 0)
4773814Sjveta 			rv = di_prom_prop_lookup_slot_names(ph, node,
4783814Sjveta 			    prop_data);
4793814Sjveta 	}
4803814Sjveta 	return (rv);
4813814Sjveta }
4823814Sjveta 
4832587Spjha /*
4843814Sjveta  * returns an allocated string containing the slot name for the slot with
4853814Sjveta  * device number <pci_dev> on bus <node>
4862587Spjha  */
4872587Spjha static char *
pci_cfg_slotname(di_node_t node,di_prom_handle_t ph,minor_t pci_dev)4882587Spjha pci_cfg_slotname(di_node_t node, di_prom_handle_t ph, minor_t pci_dev)
4892587Spjha {
4902587Spjha #ifdef	DEBUG
4912587Spjha 	char *fnm = "pci_cfg_slotname";
4922587Spjha #endif
4933814Sjveta 	int i, count;
4943814Sjveta 	char *name = NULL;
4953814Sjveta 	di_slot_name_t *slot_names = NULL;
4962587Spjha 
4973814Sjveta 	count = di_propall_lookup_slot_names(ph, 0, DDI_DEV_T_ANY, node,
4983814Sjveta 	    &slot_names);
4993814Sjveta 	if (count < 0)
5002587Spjha 		return (NULL);
5012587Spjha 
5023814Sjveta 	for (i = 0; i < count; i++) {
5033814Sjveta 		if (slot_names[i].num == (int)pci_dev) {
5043814Sjveta 			name = strdup(slot_names[i].name);
5053814Sjveta 			break;
5063814Sjveta 		}
5072587Spjha 	}
5083814Sjveta #ifdef	DEBUG
5093814Sjveta 	if (name == NULL)
5103814Sjveta 		dprint(("%s: slot w/ pci_dev %d not found in %s for %s%d\n",
5113814Sjveta 		    fnm, (int)pci_dev, DI_PROP_SLOT_NAMES, DRVINST(node)));
5123814Sjveta #endif
5133814Sjveta 	if (count > 0)
5143814Sjveta 		di_slot_names_free(count, slot_names);
5153814Sjveta 	return (name);
5162587Spjha }
5172587Spjha 
5182587Spjha 
5192587Spjha /*
5202587Spjha  * returns non-zero if we can return a valid attachment point name for <node>,
5212587Spjha  * for its slot identified by child pci device number <pci_dev>, through <buf>
5222587Spjha  *
5232587Spjha  * prioritized naming scheme:
5243814Sjveta  *	1) <DI_PROP_SLOT_NAMES property>    (see pci_cfg_slotname())
5253814Sjveta  *	2) <device-type><DI_PROP_PHYS_SLOT property>
5262587Spjha  *	3) <drv name><drv inst>.<device-type><pci_dev>
5272587Spjha  *
5283814Sjveta  * where <device-type> is derived from the DI_PROP_DEV_TYPE property:
5292587Spjha  *	if its value is "pciex" then <device-type> is "pcie"
5302587Spjha  *	else the raw value is used
5312587Spjha  *
5322587Spjha  * if <flags> contains APNODE_DEFNAME, then scheme (3) is used
5332587Spjha  */
5342587Spjha static int
pci_cfg_ap_node(minor_t pci_dev,di_node_t node,di_prom_handle_t ph,char * buf,int bufsz,int flags)5352587Spjha pci_cfg_ap_node(minor_t pci_dev, di_node_t node, di_prom_handle_t ph,
5362587Spjha     char *buf, int bufsz, int flags)
5372587Spjha {
5382587Spjha 	int *nump;
5392587Spjha 	int rv;
5402587Spjha 	char *str, *devtype;
5412587Spjha 
5422587Spjha 	rv = di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, node,
5433814Sjveta 	    DI_PROP_DEV_TYPE, &devtype);
5442587Spjha 	if (rv < 1)
5452587Spjha 		return (0);
5462587Spjha 
5472587Spjha 	if (strcmp(devtype, PROPVAL_PCIEX) == 0)
5482587Spjha 		devtype = DEVTYPE_PCIE;
5492587Spjha 
5502587Spjha 	if (flags & APNODE_DEFNAME)
5512587Spjha 		goto DEF;
5522587Spjha 
5532587Spjha 	str = pci_cfg_slotname(node, ph, pci_dev);
5542587Spjha 	if (str != NULL) {
5552587Spjha 		(void) strlcpy(buf, str, bufsz);
5563814Sjveta 		free(str);
5572587Spjha 		return (1);
5582587Spjha 	}
5592587Spjha 
5603814Sjveta 	if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node,
5613814Sjveta 	    DI_PROP_PHYS_SLOT, &nump) > 0) {
5622587Spjha 		if (*nump > 0) {
5632587Spjha 			(void) snprintf(buf, bufsz, "%s%d", devtype, *nump);
5642587Spjha 			return (1);
5652587Spjha 		}
5662587Spjha 	}
5672587Spjha DEF:
5682587Spjha 	(void) snprintf(buf, bufsz, "%s%d.%s%d",
5692587Spjha 	    di_driver_name(node), di_instance(node), devtype, pci_dev);
5702587Spjha 
5712587Spjha 	return (1);
5722587Spjha }
5732587Spjha 
5742587Spjha 
5752587Spjha /*
5762587Spjha  * returns non-zero if we can return a valid expansion chassis name for <node>
5772587Spjha  * through <buf>
5782587Spjha  *
5792587Spjha  * prioritized naming scheme:
5803814Sjveta  *	1) <IOB_PRE string><DI_PROP_SERID property: sun specific portion>
5813814Sjveta  *	2) <IOB_PRE string><full DI_PROP_SERID property in hex>
5822587Spjha  *	3) <IOB_PRE string>
5832587Spjha  *
5843814Sjveta  * DI_PROP_SERID encoding <64-bit int: msb ... lsb>:
5853485Spjha  * <24 bits: IEEE company id><40 bits: serial number>
5862587Spjha  *
5872587Spjha  * sun encoding of 40 bit serial number:
5883793Spjha  * first byte = device type indicator
5892587Spjha  * next 4 bytes = 4 ascii characters
5903793Spjha  *
5913793Spjha  * In the unlikely event that serial id contains non-printable characters
5923793Spjha  * the full 64 bit raw hex string will be used for the attachment point.
5932587Spjha  */
5942587Spjha /*ARGSUSED*/
5952587Spjha static int
pci_cfg_iob_name(di_minor_t minor,di_node_t node,di_prom_handle_t ph,char * buf,int bufsz)5962587Spjha pci_cfg_iob_name(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
5972587Spjha     char *buf, int bufsz)
5982587Spjha {
5992587Spjha 	int64_t *seridp;
6003485Spjha 	uint64_t serid;
6013793Spjha 	char *idstr;
6022587Spjha 
6033814Sjveta 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, DI_PROP_SERID,
6042587Spjha 	    &seridp) < 1) {
6052587Spjha 		(void) strlcpy(buf, IOB_PRE, bufsz);
6062587Spjha 		return (1);
6072587Spjha 	}
6083793Spjha 
6093485Spjha 	serid = (uint64_t)*seridp;
6102587Spjha 
6113793Spjha 	if ((serid >> 40) != (uint64_t)IEEE_SUN_ID ||
6127302Sgdamore@opensolaris.org 	    !serid_printable(&serid)) {
6132587Spjha 		(void) snprintf(buf, bufsz, "%s%llx", IOB_PRE, serid);
6142587Spjha 		return (1);
6152587Spjha 	}
6162587Spjha 
6173485Spjha 	/*
6183793Spjha 	 * the serial id is constructed from lower 40 bits of the serialid
6193793Spjha 	 * property and is represented by 5 ascii characters. The first
6203793Spjha 	 * character indicates if the IO Box is PCIe or PCI-X.
6213485Spjha 	 */
6223485Spjha 
6233793Spjha 	serid <<= 24;
6243793Spjha 	idstr = (char *)&serid;
6253793Spjha 	idstr[sizeof (serid) -1] = '\0';
6263793Spjha 
6273793Spjha 	(void) snprintf(buf, bufsz, "%s%s", IOB_PRE, idstr);
6283485Spjha 
6292587Spjha 	return (1);
6302587Spjha }
6312587Spjha 
6322587Spjha 
6332955Spjha /*
6342955Spjha  * returns the pci device number for <node> if found, else returns PCIDEV_NIL
6352955Spjha  */
6362587Spjha static minor_t
pci_cfg_pcidev(di_node_t node,di_prom_handle_t ph)6372587Spjha pci_cfg_pcidev(di_node_t node, di_prom_handle_t ph)
6382587Spjha {
6392587Spjha 	int rv;
6402587Spjha 	int *regp;
6412587Spjha 
6423814Sjveta 	rv = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, DI_PROP_REG,
6432587Spjha 	    &regp);
6442587Spjha 
6452587Spjha 	if (rv < 1) {
6462587Spjha 		dprint(("pci_cfg_pcidev: property %s not found "
6473814Sjveta 		    "for %s%d\n", DI_PROP_REG, DRVINST(node)));
6483155Sjveta 		return (PCIDEV_NIL);
6492587Spjha 	}
6502587Spjha 
6512587Spjha 	return (REG_PCIDEV(regp));
6522587Spjha }
6532587Spjha 
6542587Spjha 
6552587Spjha /*
6562587Spjha  * returns non-zero when it can successfully return an attachment point
6572587Spjha  * through <ap_path> whose length is less than <ap_pathsz>; returns the full
6582587Spjha  * path of the AP through <pathret> which may be larger than <ap_pathsz>.
6592587Spjha  * Callers need to free <pathret>.  If it cannot return the full path through
6602587Spjha  * <pathret> it will be set to NULL
6612587Spjha  *
6622587Spjha  * The ap path reflects a subset of the device path from an onboard host slot
6632587Spjha  * up to <node>.  We traverse up the device tree starting from <node>, naming
6642587Spjha  * each component using pci_cfg_ap_node().  If we detect that a certain
6652587Spjha  * segment is contained within an expansion chassis, then we skip any bus
6662587Spjha  * nodes in between our current node and the topmost node of the chassis,
6673814Sjveta  * which is identified by the DI_PROP_FIRST_CHAS property, and prepend the name
6682587Spjha  * of the expansion chassis as given by pci_cfg_iob_name()
6692587Spjha  *
6702587Spjha  * This scheme is always used for <pathret>.  If however, the size of
6712587Spjha  * <pathret> is greater than <ap_pathsz> then only the default name as given
6722587Spjha  * by pci_cfg_ap_node() for <node> will be used
6730Sstevel@tonic-gate  */
6740Sstevel@tonic-gate static int
pci_cfg_ap_path(di_minor_t minor,di_node_t node,di_prom_handle_t ph,char * ap_path,int ap_pathsz,char ** pathret)6752587Spjha pci_cfg_ap_path(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
6762587Spjha     char *ap_path, int ap_pathsz, char **pathret)
6770Sstevel@tonic-gate {
6782587Spjha #ifdef	DEBUG
6792587Spjha 	char *fnm = "pci_cfg_ap_path";
6802587Spjha #endif
6812587Spjha #define	seplen		(sizeof (AP_PATH_SEP) - 1)
6822587Spjha #define	iob_pre_len	(sizeof (IOB_PRE) - 1)
6832587Spjha #define	ap_path_iob_sep_len	(sizeof (AP_PATH_IOB_SEP) - 1)
6842587Spjha 
6852587Spjha 	char *bufptr;
6862587Spjha 	char buf[MAXPATHLEN];
6872587Spjha 	char pathbuf[MAXPATHLEN];
6882587Spjha 	int bufsz;
6892587Spjha 	char *pathptr;
6902587Spjha 	char *pathend = NULL;
6912587Spjha 	int len;
6922587Spjha 	int rv = 0;
6932587Spjha 	int chasflag = 0;
6942587Spjha 	di_node_t curnode = node;
6952587Spjha 	di_node_t chasnode = DI_NODE_NIL;
6962587Spjha 	minor_t pci_dev;
6972587Spjha 
6982587Spjha 	buf[0] = '\0';
6992587Spjha 	pathbuf[0] = '\0';
7002587Spjha 	pathptr = &pathbuf[sizeof (pathbuf) - 1];
7012587Spjha 	*pathptr = '\0';
7022587Spjha 
7032587Spjha 	/*
7042587Spjha 	 * as we traverse up the device tree, we prepend components of our
7052587Spjha 	 * path inside pathbuf, using pathptr and decrementing
7062587Spjha 	 */
7072587Spjha 	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
7082587Spjha 	do {
7092587Spjha 		bufptr = buf;
7102587Spjha 		bufsz = sizeof (buf);
7112587Spjha 
7122587Spjha 		chasnode = pci_cfg_chassis_node(curnode, ph);
7132587Spjha 		if (chasnode != DI_NODE_NIL) {
7142587Spjha 			rv = pci_cfg_iob_name(minor, chasnode, ph,
7152587Spjha 			    bufptr, bufsz);
7162587Spjha 			if (rv == 0) {
7172587Spjha 				dprint(("%s: cannot create iob name "
7182587Spjha 				    "for %s%d\n", fnm, DRVINST(node)));
7192587Spjha 				*pathptr = '\0';
7202587Spjha 				goto OUT;
7212587Spjha 			}
7222587Spjha 
7232587Spjha 			(void) strncat(bufptr, AP_PATH_IOB_SEP, bufsz);
7242587Spjha 			len = strlen(bufptr);
7252587Spjha 			bufptr += len;
7262587Spjha 			bufsz -= len - 1;
7270Sstevel@tonic-gate 
7282587Spjha 			/* set chasflag when the leaf node is within an iob */
7292587Spjha 			if ((curnode == node) != NULL)
7302587Spjha 				chasflag = 1;
7312587Spjha 		}
7322587Spjha 		rv = pci_cfg_ap_node(pci_dev, curnode, ph, bufptr, bufsz, 0);
7332587Spjha 		if (rv == 0) {
7342587Spjha 			dprint(("%s: cannot create ap node name "
7352587Spjha 			    "for %s%d\n", fnm, DRVINST(node)));
7362587Spjha 			*pathptr = '\0';
7372587Spjha 			goto OUT;
7382587Spjha 		}
7392587Spjha 
7402587Spjha 		/*
7412587Spjha 		 * if we can't fit the entire path in our pathbuf, then use
7422587Spjha 		 * the default short name and nullify pathptr; also, since
7432587Spjha 		 * we prepend in the buffer, we must avoid adding a null char
7442587Spjha 		 */
7452587Spjha 		if (curnode != node) {
7462587Spjha 			pathptr -= seplen;
7472587Spjha 			if (pathptr < pathbuf) {
7482587Spjha 				pathptr = pathbuf;
7492587Spjha 				*pathptr = '\0';
7502587Spjha 				goto DEF;
7512587Spjha 			}
7522587Spjha 			(void) memcpy(pathptr, AP_PATH_SEP, seplen);
7532587Spjha 		}
7542587Spjha 		len = strlen(buf);
7552587Spjha 		pathptr -= len;
7562587Spjha 		if (pathptr < pathbuf) {
7572587Spjha 			pathptr = pathbuf;
7582587Spjha 			*pathptr = '\0';
7592587Spjha 			goto DEF;
7602587Spjha 		}
7612587Spjha 		(void) memcpy(pathptr, buf, len);
7620Sstevel@tonic-gate 
7632587Spjha 		/* remember the leaf component */
7642587Spjha 		if (curnode == node)
7652587Spjha 			pathend = pathptr;
7662587Spjha 
7672587Spjha 		/*
7682587Spjha 		 * go no further than the hosts' onboard slots
7692587Spjha 		 */
7702587Spjha 		if (chasnode == DI_NODE_NIL)
7712587Spjha 			break;
7722587Spjha 		curnode = chasnode;
7732587Spjha 
7742587Spjha 		/*
7752587Spjha 		 * the pci device number of the current node is used to
7762587Spjha 		 * identify which slot of the parent's bus (next iteration)
7772587Spjha 		 * the current node is on
7782587Spjha 		 */
7792587Spjha 		pci_dev = pci_cfg_pcidev(curnode, ph);
7802955Spjha 		if (pci_dev == PCIDEV_NIL) {
7812955Spjha 			dprint(("%s: cannot obtain pci device number "
7822955Spjha 			    "for %s%d\n", fnm, DRVINST(node)));
7832955Spjha 			*pathptr = '\0';
7842955Spjha 			goto OUT;
7852955Spjha 		}
7862587Spjha 	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
7872587Spjha 
7882587Spjha 	pathbuf[sizeof (pathbuf) - 1] = '\0';
7892587Spjha 	if (strlen(pathptr) < ap_pathsz) {
7902587Spjha 		(void) strlcpy(ap_path, pathptr, ap_pathsz);
7912587Spjha 		rv = 1;
7922587Spjha 		goto OUT;
7932587Spjha 	}
7942587Spjha 
7952587Spjha DEF:
7962587Spjha 	/*
7972587Spjha 	 * when our name won't fit <ap_pathsz> we use the endpoint/leaf
7982587Spjha 	 * <node>'s name ONLY IF it has a serialid# which will make the apid
7992587Spjha 	 * globally unique
8002587Spjha 	 */
8012587Spjha 	if (chasflag && pathend != NULL) {
8022587Spjha 		if ((strncmp(pathend + iob_pre_len, AP_PATH_IOB_SEP,
8032587Spjha 		    ap_path_iob_sep_len) != 0) &&
8042587Spjha 		    (strlen(pathend) < ap_pathsz)) {
8052587Spjha 			(void) strlcpy(ap_path, pathend, ap_pathsz);
8062587Spjha 			rv = 1;
8072587Spjha 			goto OUT;
8080Sstevel@tonic-gate 		}
8090Sstevel@tonic-gate 	}
8102587Spjha 
8112587Spjha 	/*
8122587Spjha 	 * if our name still won't fit <ap_pathsz>, then use the leaf <node>'s
8132587Spjha 	 * default name
8142587Spjha 	 */
8153303Sjveta 	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
8162587Spjha 	rv = pci_cfg_ap_node(pci_dev, node, ph, buf, bufsz, APNODE_DEFNAME);
8172587Spjha 	if (rv == 0) {
8182587Spjha 		dprint(("%s: cannot create default ap node name for %s%d\n",
8192587Spjha 		    fnm, DRVINST(node)));
8202587Spjha 		*pathptr = '\0';
8212587Spjha 		goto OUT;
8222587Spjha 	}
8232587Spjha 	if (strlen(buf) < ap_pathsz) {
8242587Spjha 		(void) strlcpy(ap_path, buf, ap_pathsz);
8252587Spjha 		rv = 1;
8262587Spjha 		goto OUT;
8272587Spjha 	}
8282587Spjha 
8292587Spjha 	/*
8302587Spjha 	 * in this case, cfgadm goes through an expensive process to generate
8312587Spjha 	 * a purely dynamic logical apid: the framework will look through
8322587Spjha 	 * the device tree for attachment point minor nodes and will invoke
8332587Spjha 	 * each plugin responsible for that attachment point class, and if
8342587Spjha 	 * the plugin returns a logical apid that matches the queried apid
8352587Spjha 	 * or matches the default apid generated by the cfgadm framework for
8362587Spjha 	 * that driver/class (occurs when plugin returns an empty logical apid)
8372587Spjha 	 * then that is what it will use
8382587Spjha 	 *
8392587Spjha 	 * it is doubly expensive because the cfgadm pci plugin itself will
8402587Spjha 	 * also search the entire device tree in the absence of a link
8412587Spjha 	 */
8422587Spjha 	rv = 0;
8432587Spjha 	dprint(("%s: cannot create apid for %s%d within length of %d\n",
8442587Spjha 	    fnm, DRVINST(node), ap_pathsz));
8452587Spjha 
8462587Spjha OUT:
8472587Spjha 	ap_path[ap_pathsz - 1] = '\0';
8482587Spjha 	*pathret = (*pathptr == '\0') ? NULL : strdup(pathptr);
8492587Spjha 	return (rv);
8502587Spjha 
8512587Spjha #undef	seplen
8522587Spjha #undef	iob_pre_len
8532587Spjha #undef	ap_path_iob_sep_len
8542587Spjha }
8552587Spjha 
8562587Spjha 
8572587Spjha /*
8583814Sjveta  * the DI_PROP_AP_NAMES property contains the first integer section of the
8592587Spjha  * ieee1275 "slot-names" property and functions as a bitmask; see comment for
8602587Spjha  * pci_cfg_slotname()
8612587Spjha  *
8622587Spjha  * we use the name of the attachment point minor node if its pci device
8633814Sjveta  * number (encoded in the minor number) is allowed by DI_PROP_AP_NAMES
8642587Spjha  *
8652587Spjha  * returns non-zero if we return a valid attachment point through <path>
8662587Spjha  */
8672587Spjha static int
pci_cfg_ap_legacy(di_minor_t minor,di_node_t node,di_prom_handle_t ph,char * ap_path,int ap_pathsz)8682587Spjha pci_cfg_ap_legacy(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
8692587Spjha     char *ap_path, int ap_pathsz)
8702587Spjha {
8712587Spjha 	minor_t pci_dev;
8722587Spjha 	int *anp;
8732587Spjha 
8743814Sjveta 	if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, DI_PROP_AP_NAMES,
8752587Spjha 	    &anp) < 1)
8762587Spjha 		return (0);
8772587Spjha 
8782587Spjha 	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
8792587Spjha 	if ((*anp & (1 << pci_dev)) == 0)
8802587Spjha 		return (0);
8812587Spjha 
8822587Spjha 	(void) strlcpy(ap_path, di_minor_name(minor), ap_pathsz);
8832587Spjha 	return (1);
8842587Spjha }
8852587Spjha 
8862587Spjha 
8872587Spjha /*
8882587Spjha  * determine if <node> qualifies for a path style apid
8892587Spjha  */
8902587Spjha static int
pci_cfg_is_ap_path(di_node_t node,di_prom_handle_t ph)8912587Spjha pci_cfg_is_ap_path(di_node_t node, di_prom_handle_t ph)
8922587Spjha {
8932587Spjha 	char *devtype;
8942587Spjha 	di_node_t curnode = node;
8952587Spjha 
8962587Spjha 	do {
8972587Spjha 		if (di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, curnode,
8983814Sjveta 		    DI_PROP_DEV_TYPE, &devtype) > 0)
8992587Spjha 			if (strcmp(devtype, PROPVAL_PCIEX) == 0)
9002587Spjha 				return (1);
9012587Spjha 	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
9022587Spjha 
9032587Spjha 	return (0);
9042587Spjha }
9052587Spjha 
9062587Spjha 
9072587Spjha /*
9082587Spjha  * takes a full path as returned by <pathret> from pci_cfg_ap_path() and
9092587Spjha  * returns an allocated string intendend to be stored in a devlink info (dli)
9102587Spjha  * file
9112587Spjha  *
9122587Spjha  * data format: "Location: <transformed path>"
9132587Spjha  * where <transformed path> is <path> with occurrances of AP_PATH_SEP
9142587Spjha  * replaced by "/"
9152587Spjha  */
9162587Spjha static char *
pci_cfg_info_data(char * path)9172587Spjha pci_cfg_info_data(char *path)
9182587Spjha {
9192587Spjha #define	head	"Location: "
9202587Spjha #define	headlen	(sizeof (head) - 1)
9212587Spjha #define	seplen	(sizeof (AP_PATH_SEP) - 1)
9222587Spjha 
9232587Spjha 	char *sep, *prev, *np;
9242587Spjha 	char *newpath;
9252587Spjha 	int pathlen = strlen(path);
9262587Spjha 	int len;
9272587Spjha 
9282587Spjha 	newpath = malloc(sizeof (char) * (headlen + pathlen + 1));
9292587Spjha 	np = newpath;
9302587Spjha 	(void) strcpy(np, head);
9312587Spjha 	np += headlen;
9322587Spjha 
9332587Spjha 	prev = path;
9342587Spjha 	while ((sep = strstr(prev, AP_PATH_SEP)) != NULL) {
9352587Spjha 		len = sep - prev;
9362587Spjha 		(void) memcpy(np, prev, len);
9372587Spjha 		np += len;
9382587Spjha 		*np++ = '/';
9392587Spjha 		prev = sep + seplen;
9402587Spjha 	}
9412587Spjha 	(void) strcpy(np, prev);
9422587Spjha 	return (newpath);
9432587Spjha 
9442587Spjha #undef	head
9452587Spjha #undef	headlen
9462587Spjha #undef	seplen
9472587Spjha }
9482587Spjha 
9492587Spjha 
9502587Spjha static void
pci_cfg_rm_link(char * file)9512587Spjha pci_cfg_rm_link(char *file)
9522587Spjha {
9532587Spjha 	char *dlipath;
9542587Spjha 
9552587Spjha 	dlipath = di_dli_name(file);
9562587Spjha 	(void) unlink(dlipath);
9572587Spjha 
9582587Spjha 	devfsadm_rm_all(file);
9592587Spjha 	free(dlipath);
9602587Spjha }
9612587Spjha 
9622587Spjha /*
9632587Spjha  * removes all registered devlinks to physical path <physpath> except for
9642587Spjha  * the devlink <valid> if not NULL;
9652587Spjha  * <physpath> must include the minor node
9662587Spjha  */
9672587Spjha static void
pci_cfg_rm_invalid_links(char * physpath,char * valid)9682587Spjha pci_cfg_rm_invalid_links(char *physpath, char *valid)
9692587Spjha {
9702587Spjha 	char **dnp;
9712587Spjha 	char *cp, *vcp;
9722587Spjha 	int i, dnlen;
9732587Spjha 
9742587Spjha 	dnp = devfsadm_lookup_dev_names(physpath, NULL, &dnlen);
9752587Spjha 	if (dnp == NULL)
9762587Spjha 		return;
9772587Spjha 
9782587Spjha 	if (valid != NULL) {
9792587Spjha 		if (strncmp(valid, DEV "/", DEV_LEN + 1) == 0)
9802587Spjha 			vcp = valid + DEV_LEN + 1;
9812587Spjha 		else
9822587Spjha 			vcp = valid;
9832587Spjha 	}
9842587Spjha 
9852587Spjha 	for (i = 0; i < dnlen; i++) {
9862587Spjha 		if (strncmp(dnp[i], DEV "/", DEV_LEN + 1) == 0)
9872587Spjha 			cp = dnp[i] + DEV_LEN + 1;
9882587Spjha 		else
9892587Spjha 			cp = dnp[i];
9902587Spjha 
9912587Spjha 		if (valid != NULL) {
9922587Spjha 			if (strcmp(vcp, cp) == 0)
9932587Spjha 				continue;
9942587Spjha 		}
9952587Spjha 		pci_cfg_rm_link(cp);
9962587Spjha 	}
9972587Spjha 	devfsadm_free_dev_names(dnp, dnlen);
9982587Spjha }
9992587Spjha 
10000Sstevel@tonic-gate 
10012587Spjha /*
10022587Spjha  * takes a complete devinfo snapshot and returns the root node;
10032587Spjha  * callers must do a di_fini() on the returned node;
10042587Spjha  * if the snapshot failed, DI_NODE_NIL is returned instead
10052587Spjha  *
10062587Spjha  * if <pci_node> is not DI_NODE_NIL, it will search for the same devinfo node
10072587Spjha  * in the new snapshot and return it through <ret_node> if it is found,
10082587Spjha  * else DI_NODE_NIL is returned instead
10092587Spjha  *
10102587Spjha  * in addition, if <pci_minor> is not DI_MINOR_NIL, it will also return
10112587Spjha  * the matching minor in the new snapshot through <ret_minor> if it is found,
10122587Spjha  * else DI_MINOR_NIL is returned instead
10132587Spjha  */
10142587Spjha static di_node_t
pci_cfg_snapshot(di_node_t pci_node,di_minor_t pci_minor,di_node_t * ret_node,di_minor_t * ret_minor)10152587Spjha pci_cfg_snapshot(di_node_t pci_node, di_minor_t pci_minor,
10162587Spjha     di_node_t *ret_node, di_minor_t *ret_minor)
10172587Spjha {
10182587Spjha 	di_node_t root_node;
10192587Spjha 	di_node_t node;
10202587Spjha 	di_minor_t minor;
10212587Spjha 	int pci_inst;
10222587Spjha 	dev_t pci_devt;
10232587Spjha 
10242587Spjha 	*ret_node = DI_NODE_NIL;
10252587Spjha 	*ret_minor = DI_MINOR_NIL;
10262587Spjha 
10272587Spjha 	root_node = di_init("/", DINFOCPYALL);
10282587Spjha 	if (root_node == DI_NODE_NIL)
10292587Spjha 		return (DI_NODE_NIL);
10302587Spjha 
10312587Spjha 	/*
10322587Spjha 	 * narrow down search by driver, then instance, then minor
10332587Spjha 	 */
10342587Spjha 	if (pci_node == DI_NODE_NIL)
10352587Spjha 		return (root_node);
10362587Spjha 
10372587Spjha 	pci_inst = di_instance(pci_node);
10382587Spjha 	node = di_drv_first_node(di_driver_name(pci_node), root_node);
10392587Spjha 	do {
10402587Spjha 		if (pci_inst == di_instance(node)) {
10412587Spjha 			*ret_node = node;
10422587Spjha 			break;
10432587Spjha 		}
10442587Spjha 	} while ((node = di_drv_next_node(node)) != DI_NODE_NIL);
10452587Spjha 
10462587Spjha 	if (node == DI_NODE_NIL)
10472587Spjha 		return (root_node);
10482587Spjha 
10492587Spjha 	/*
10502587Spjha 	 * found node, now search minors
10512587Spjha 	 */
10522587Spjha 	if (pci_minor == DI_MINOR_NIL)
10532587Spjha 		return (root_node);
10542587Spjha 
10552587Spjha 	pci_devt = di_minor_devt(pci_minor);
10562587Spjha 	minor = DI_MINOR_NIL;
10572587Spjha 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
10582587Spjha 		if (pci_devt == di_minor_devt(minor)) {
10592587Spjha 			*ret_minor = minor;
10602587Spjha 			break;
10612587Spjha 		}
10622587Spjha 	}
10632587Spjha 	return (root_node);
10642587Spjha }
10652587Spjha 
10662587Spjha 
10672587Spjha static int
pci_cfg_creat_cb(di_minor_t pci_minor,di_node_t pci_node)10682587Spjha pci_cfg_creat_cb(di_minor_t pci_minor, di_node_t pci_node)
10692587Spjha {
10702587Spjha #ifdef	DEBUG
10712587Spjha 	char *fnm = "pci_cfg_creat_cb";
10722587Spjha #endif
10732587Spjha #define	ap_pathsz	(sizeof (ap_path))
10742587Spjha 
10752587Spjha 	char ap_path[CFGA_LOG_EXT_LEN];
10762587Spjha 	char linkbuf[MAXPATHLEN];
10772587Spjha 	char *fullpath = NULL;
10782587Spjha 	char *pathinfo = NULL;
10792587Spjha 	char *devpath = NULL;
10802955Spjha 	int rv, fd = -1;
10812587Spjha 	size_t sz;
10822587Spjha 	di_prom_handle_t ph;
10832587Spjha 	di_node_t node;
10842587Spjha 	di_node_t root_node = DI_NODE_NIL;
10852587Spjha 	di_minor_t minor;
10862587Spjha 
10872587Spjha 	ph = di_prom_init();
10882587Spjha 	if (ph == DI_PROM_HANDLE_NIL) {
10892587Spjha 		dprint(("%s: di_prom_init() failed for %s%d\n",
10902587Spjha 		    fnm, DRVINST(pci_node)));
10912587Spjha 		goto OUT;
10922587Spjha 	}
10932587Spjha 
10942587Spjha 	/*
10952587Spjha 	 * Since incoming nodes from hotplug events are from snapshots that
10962587Spjha 	 * do NOT contain parent/ancestor data, we must retake our own
10972587Spjha 	 * snapshot and search for the target node
10982587Spjha 	 */
10992587Spjha 	root_node = pci_cfg_snapshot(pci_node, pci_minor, &node, &minor);
11002587Spjha 	if (root_node == DI_NODE_NIL || node == DI_NODE_NIL ||
11012587Spjha 	    minor == DI_MINOR_NIL) {
11022587Spjha 		dprint(("%s: devinfo snapshot or search failed for %s%d\n",
11032587Spjha 		    fnm, DRVINST(pci_node)));
11042587Spjha 		goto OUT;
11052587Spjha 	}
11062587Spjha 
11072587Spjha 	if (pci_cfg_is_ap_path(node, ph)) {
11082587Spjha 		rv = pci_cfg_ap_path(minor, node, ph, ap_path, ap_pathsz,
11092587Spjha 		    &fullpath);
11102587Spjha 		if (rv == 0)
11112587Spjha 			goto OUT;
11122587Spjha 
11132587Spjha 		(void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
11142587Spjha 		    CFG_DIRNAME, ap_path);
11152587Spjha 
11162587Spjha 		/*
11172587Spjha 		 * We must remove existing links because we may have invalid
11182587Spjha 		 * apids that are valid links.  Since these are not dangling,
11192587Spjha 		 * devfsadm will not invoke the remove callback on them.
11202587Spjha 		 *
11212587Spjha 		 * What are "invalid apids with valid links"?  Consider swapping
11222587Spjha 		 * an attachment point bus with another while the system is
11232587Spjha 		 * down, on the same device path bound to the same drivers
11242587Spjha 		 * but with the new AP bus having different properties
11252587Spjha 		 * (e.g. serialid#).  If the previous apid is not removed,
11262587Spjha 		 * there will now be two different links pointing to the same
11272587Spjha 		 * attachment point, but only one reflects the correct
11282587Spjha 		 * logical apid
11292587Spjha 		 */
11302587Spjha 		devpath = pci_cfg_devpath(node, minor);
11312587Spjha 		if (devpath == NULL)
11322587Spjha 			goto OUT;
11332587Spjha 		pci_cfg_rm_invalid_links(devpath, linkbuf);
11342587Spjha 		free(devpath);
11352587Spjha 
11362587Spjha 		(void) devfsadm_mklink(linkbuf, node, minor, 0);
11372587Spjha 
11382587Spjha 		/*
11392587Spjha 		 * we store the full logical path of the attachment point for
11402587Spjha 		 * cfgadm to display in its info field which is useful when
11412587Spjha 		 * the full logical path exceeds the size limit for logical
11422587Spjha 		 * apids (CFGA_LOG_EXT_LEN)
11432587Spjha 		 *
11442587Spjha 		 * for the cfgadm pci plugin to do the same would be expensive
11452587Spjha 		 * (i.e. devinfo snapshot + top down exhaustive minor search +
11462587Spjha 		 * equivalent of pci_cfg_ap_path() on every invocation)
11472587Spjha 		 *
11482587Spjha 		 * note that if we do not create a link (pci_cfg_ap_path() is
11492587Spjha 		 * not successful), that is what cfgadm will do anyways to
11502587Spjha 		 * create a purely dynamic apid
11512587Spjha 		 */
11522587Spjha 		pathinfo = pci_cfg_info_data(fullpath);
11532587Spjha 		fd = di_dli_openw(linkbuf);
11542587Spjha 		if (fd < 0)
11552587Spjha 			goto OUT;
11562587Spjha 
11572587Spjha 		sz = strlen(pathinfo) + 1;
11582587Spjha 		rv = write(fd, pathinfo, sz);
11592587Spjha 		if (rv < sz) {
11602587Spjha 			dprint(("%s: could not write full pathinfo to dli "
11612587Spjha 			    "file for %s%d\n", fnm, DRVINST(node)));
11622587Spjha 			goto OUT;
11632587Spjha 		}
11642587Spjha 		di_dli_close(fd);
11652587Spjha 	} else {
11662587Spjha 		rv = pci_cfg_ap_legacy(minor, node, ph, ap_path,
11672587Spjha 		    ap_pathsz);
11682587Spjha 		if (rv == 0)
11692587Spjha 			goto OUT;
11702587Spjha 
11712587Spjha 		(void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
11722587Spjha 		    CFG_DIRNAME, ap_path);
11732587Spjha 		(void) devfsadm_mklink(linkbuf, node, minor, 0);
11742587Spjha 	}
11752587Spjha 
11762587Spjha OUT:
11772955Spjha 	if (fd >= 0)
11782955Spjha 		di_dli_close(fd);
11792587Spjha 	if (fullpath != NULL)
11802587Spjha 		free(fullpath);
11812587Spjha 	if (pathinfo != NULL)
11822587Spjha 		free(pathinfo);
11832587Spjha 	if (ph != DI_PROM_HANDLE_NIL)
11842587Spjha 		di_prom_fini(ph);
11852587Spjha 	if (root_node != DI_NODE_NIL)
11862587Spjha 		di_fini(root_node);
11870Sstevel@tonic-gate 	return (DEVFSADM_CONTINUE);
11882587Spjha 
11892587Spjha #undef	ap_pathsz
11902587Spjha }
11912587Spjha 
11922587Spjha 
11932587Spjha static void
pci_cfg_rm_all(char * file)11942587Spjha pci_cfg_rm_all(char *file)
11952587Spjha {
11962587Spjha 	pci_cfg_rm_link(file);
11970Sstevel@tonic-gate }
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate /*
12010Sstevel@tonic-gate  * ib_cfg_creat_cb() creates two types of links
12020Sstevel@tonic-gate  * One for the fabric as /dev/cfg/ib
12030Sstevel@tonic-gate  * Another for each HCA seen in the fabric as /dev/cfg/hca:<HCA-GUID>
12040Sstevel@tonic-gate  */
12050Sstevel@tonic-gate static int
ib_cfg_creat_cb(di_minor_t minor,di_node_t node)12060Sstevel@tonic-gate ib_cfg_creat_cb(di_minor_t minor, di_node_t node)
12070Sstevel@tonic-gate {
12080Sstevel@tonic-gate 	char	*cp;
12090Sstevel@tonic-gate 	char	path[PATH_MAX + 1];
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate 	if ((cp = di_devfs_path(node)) == NULL) {
12120Sstevel@tonic-gate 		return (DEVFSADM_CONTINUE);
12130Sstevel@tonic-gate 	}
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
12160Sstevel@tonic-gate 	di_devfs_path_free(cp);
12170Sstevel@tonic-gate 
12180Sstevel@tonic-gate 	/* create fabric or hca:GUID and the symlink */
12190Sstevel@tonic-gate 	if (strstr(path, "ib:fabric") != NULL) {
12200Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s/ib", CFG_DIRNAME);
12210Sstevel@tonic-gate 	} else {
12220Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s/hca:%s", CFG_DIRNAME,
12230Sstevel@tonic-gate 		    di_minor_name(minor));
12240Sstevel@tonic-gate 	}
12250Sstevel@tonic-gate 
12260Sstevel@tonic-gate 	(void) devfsadm_mklink(path, node, minor, 0);
12270Sstevel@tonic-gate 	return (DEVFSADM_CONTINUE);
12280Sstevel@tonic-gate }
12293793Spjha 
12303793Spjha /*
12313793Spjha  * This function verifies if the serial id is printable.
12323793Spjha  */
12333793Spjha 
12343793Spjha static int
serid_printable(uint64_t * seridp)12353793Spjha serid_printable(uint64_t *seridp)
12363793Spjha {
12373793Spjha 
12383793Spjha 	char *ptr;
12393793Spjha 	int i = 0;
12403793Spjha 
12413793Spjha 	for (ptr = (char *)seridp+3; i < 5; ptr++, i++)
12423793Spjha 		if (*ptr < 0x21 || *ptr >= 0x7f)
12433793Spjha 			return (0);
12443793Spjha 
12453793Spjha 	return (1);
12463793Spjha 
12473793Spjha }
1248