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 ®p);
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