summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpjha <none@none>2006-08-21 20:11:18 -0700
committerpjha <none@none>2006-08-21 20:11:18 -0700
commit3c4226f98775d47a05fa88f9f72479f1a250eaa5 (patch)
tree2268348f795a468ab405cce9cd8121f280ba5a7b
parent3247dfed200428da80007948dc664da0d2b81bac (diff)
downloadillumos-gate-3c4226f98775d47a05fa88f9f72479f1a250eaa5.tar.gz
6425514 Invalid slot number message on FF2 could be hardware programming issue
6436776 Link Up/Down events should be expected events during Oberon hotplug operations 6439953 Identical Slot names in different segments can cause a breakage in ApId scheme 6460150 Booting OPL system with build 46 panics w/ MONDO 62 for DMC/PEC 6460200 OPL Hotplug should use existing PCI Cap Library 6460204 PCI Cap Library should avoid name space collision for pci_config_size_t
-rw-r--r--usr/src/cmd/devfsadm/cfg_link.c898
-rw-r--r--usr/src/cmd/devfsadm/cfg_link.h100
-rw-r--r--usr/src/cmd/devfsadm/devfsadm.c54
-rw-r--r--usr/src/cmd/devfsadm/devfsadm.h10
-rw-r--r--usr/src/cmd/devfsadm/devfsadm_impl.h9
-rw-r--r--usr/src/lib/cfgadm_plugins/pci/common/cfga.c77
-rw-r--r--usr/src/lib/libdevinfo/Makefile.com4
-rw-r--r--usr/src/lib/libdevinfo/devinfo_dli.c158
-rw-r--r--usr/src/lib/libdevinfo/libdevinfo.h17
-rw-r--r--usr/src/lib/libdevinfo/mapfile-vers4
-rw-r--r--usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c9
-rw-r--r--usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c3
-rw-r--r--usr/src/uts/common/io/pci_cap.c4
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcihp.h9
-rw-r--r--usr/src/uts/common/sys/pci_cap.h6
-rw-r--r--usr/src/uts/sun4/io/px/px.c14
-rw-r--r--usr/src/uts/sun4/io/px/px_pci.c248
-rw-r--r--usr/src/uts/sun4u/io/px/px_hlib.c134
18 files changed, 1467 insertions, 291 deletions
diff --git a/usr/src/cmd/devfsadm/cfg_link.c b/usr/src/cmd/devfsadm/cfg_link.c
index 0951eb46b8..2cf064c5d1 100644
--- a/usr/src/cmd/devfsadm/cfg_link.c
+++ b/usr/src/cmd/devfsadm/cfg_link.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -30,16 +30,27 @@
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <limits.h>
+#include <unistd.h>
+#include <config_admin.h>
+#include <cfg_link.h>
+#include <sys/types.h>
+#include <sys/mkdev.h>
+#include <sys/hotplug/pci/pcihp.h>
-#define SCSI_CFG_LINK_RE "^cfg/c[0-9]+$"
-#define SBD_CFG_LINK_RE "^cfg/((((N[0-9]+[.])?(SB|IB))?[0-9]+)|[abcd])$"
-#define USB_CFG_LINK_RE "^cfg/((usb[0-9]+)/([0-9]+)([.]([0-9])+)*)$"
-#define PCI_CFG_LINK_RE "^cfg/[:alnum:]$"
-#define IB_CFG_LINK_RE "^cfg/(hca[0-9A-F]+)$"
-#define SATA_CFG_LINK_RE "^cfg/((sata[0-9]+)/([0-9]+)([.]([0-9])+)*)$"
+#ifdef DEBUG
+#define dprint(args) devfsadm_errprint args
+/*
+ * for use in print routine arg list as a shorthand way to locate node via
+ * "prtconf -D" to avoid messy and cluttered debugging code
+ * don't forget the corresponding "%s%d" format
+ */
+#define DRVINST(node) di_driver_name(node), di_instance(node)
+#else
+#define dprint(args)
+#endif
-#define CFG_DIRNAME "cfg"
static int scsi_cfg_creat_cb(di_minor_t minor, di_node_t node);
static int sbd_cfg_creat_cb(di_minor_t minor, di_node_t node);
@@ -49,30 +60,59 @@ static int pci_cfg_creat_cb(di_minor_t minor, di_node_t node);
static int ib_cfg_creat_cb(di_minor_t minor, di_node_t node);
static int sata_cfg_creat_cb(di_minor_t minor, di_node_t node);
+static di_node_t pci_cfg_chassis_node(di_node_t, di_prom_handle_t);
+static char *pci_cfg_slotname(di_node_t, di_prom_handle_t, minor_t);
+static int pci_cfg_ap_node(minor_t, di_node_t, di_prom_handle_t,
+ char *, int, int);
+static int pci_cfg_iob_name(di_minor_t, di_node_t, di_prom_handle_t,
+ char *, int);
+static minor_t pci_cfg_pcidev(di_node_t, di_prom_handle_t);
+static int pci_cfg_ap_path(di_minor_t, di_node_t, di_prom_handle_t,
+ char *, int, char **);
+static char *pci_cfg_info_data(char *);
+static int pci_cfg_is_ap_path(di_node_t, di_prom_handle_t);
+static int pci_cfg_ap_legacy(di_minor_t, di_node_t, di_prom_handle_t,
+ char *, int);
+static void pci_cfg_rm_invalid_links(char *, char *);
+static void pci_cfg_rm_link(char *);
+static void pci_cfg_rm_all(char *);
+static char *pci_cfg_devpath(di_node_t, di_minor_t);
+static di_node_t pci_cfg_snapshot(di_node_t, di_minor_t,
+ di_node_t *, di_minor_t *);
+
+/* flag definitions for di_propall_*(); value "0" is always the default flag */
+#define DIPROP_PRI_NODE 0x0
+#define DIPROP_PRI_PROM 0x1
+static int di_propall_lookup_ints(di_prom_handle_t, int,
+ dev_t, di_node_t, const char *, int **);
+static int di_propall_lookup_strings(di_prom_handle_t, int,
+ dev_t, di_node_t, const char *, char **);
+
+
/*
* NOTE: The CREATE_DEFER flag is private to this module.
* NOT to be used by other modules
*/
static devfsadm_create_t cfg_create_cbt[] = {
- { "attachment-point", "ddi_ctl:attachment_point:scsi", NULL,
+ { "attachment-point", DDI_NT_SCSI_ATTACHMENT_POINT, NULL,
TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
},
- { "attachment-point", "ddi_ctl:attachment_point:sbd", NULL,
+ { "attachment-point", DDI_NT_SBD_ATTACHMENT_POINT, NULL,
TYPE_EXACT, ILEVEL_0, sbd_cfg_creat_cb
},
- { "fc-attachment-point", "ddi_ctl:attachment_point:fc", NULL,
+ { "fc-attachment-point", DDI_NT_FC_ATTACHMENT_POINT, NULL,
TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
},
- { "attachment-point", "ddi_ctl:attachment_point:usb", NULL,
+ { "attachment-point", DDI_NT_USB_ATTACHMENT_POINT, NULL,
TYPE_EXACT, ILEVEL_0, usb_cfg_creat_cb
},
- { "attachment-point", "ddi_ctl:attachment_point:pci", NULL,
+ { "attachment-point", DDI_NT_PCI_ATTACHMENT_POINT, NULL,
TYPE_EXACT, ILEVEL_0, pci_cfg_creat_cb
},
- { "attachment-point", "ddi_ctl:attachment_point:ib", NULL,
+ { "attachment-point", DDI_NT_IB_ATTACHMENT_POINT, NULL,
TYPE_EXACT, ILEVEL_0, ib_cfg_creat_cb
},
- { "attachment-point", "ddi_ctl:attachment_point:sata", NULL,
+ { "attachment-point", DDI_NT_SATA_ATTACHMENT_POINT, NULL,
TYPE_EXACT, ILEVEL_0, sata_cfg_creat_cb
}
};
@@ -95,6 +135,9 @@ static devfsadm_remove_t cfg_remove_cbt[] = {
{ "attachment-point", PCI_CFG_LINK_RE, RM_POST,
ILEVEL_0, devfsadm_rm_all
},
+ { "attachment-point", PCI_CFG_PATH_LINK_RE, RM_POST|RM_HOT,
+ ILEVEL_0, pci_cfg_rm_all
+ },
{ "attachment-point", IB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
ILEVEL_0, devfsadm_rm_all
},
@@ -280,36 +323,815 @@ get_roothub(const char *path, void *cb_arg)
/*
- * pci_cfg_creat_cb() search the <device mask> data from
- * "slot-names" PROM property for the match device number,
- * then create device link with the right slot label.
+ * returns an allocted string containing the device path for <node> and
+ * <minor>
+ */
+static char *
+pci_cfg_devpath(di_node_t node, di_minor_t minor)
+{
+ char *path;
+ char *bufp;
+ char *minor_nm;
+ int buflen;
+
+ path = di_devfs_path(node);
+ minor_nm = di_minor_name(minor);
+ buflen = snprintf(NULL, 0, "%s:%s", path, minor_nm);
+
+ bufp = malloc(sizeof (char) * buflen);
+ if (bufp == NULL)
+ goto OUT;
+ (void) snprintf(bufp, buflen, "%s:%s", path, minor_nm);
+
+OUT:
+ di_devfs_path_free(path);
+ return (bufp);
+}
+
+
+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)
+{
+ int rv;
+
+ if (flags & DIPROP_PRI_PROM) {
+ rv = di_prom_prop_lookup_ints(ph, node, prop_name, prop_data);
+ if (rv < 0)
+ rv = di_prop_lookup_ints(dev, node, prop_name,
+ prop_data);
+ } else {
+ rv = di_prop_lookup_ints(dev, node, prop_name, prop_data);
+ if (rv < 0)
+ rv = di_prom_prop_lookup_ints(ph, node, prop_name,
+ prop_data);
+ }
+ return (rv);
+}
+
+
+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)
+{
+ int rv;
+
+ if (flags & DIPROP_PRI_PROM) {
+ rv = di_prom_prop_lookup_strings(ph, node, prop_name,
+ prop_data);
+ if (rv < 0)
+ rv = di_prop_lookup_strings(dev, node, prop_name,
+ prop_data);
+ } else {
+ rv = di_prop_lookup_strings(dev, node, prop_name, prop_data);
+ if (rv < 0)
+ rv = di_prom_prop_lookup_strings(ph, node, prop_name,
+ prop_data);
+ }
+ return (rv);
+}
+
+
+static di_node_t
+pci_cfg_chassis_node(di_node_t node, di_prom_handle_t ph)
+{
+ di_node_t curnode = node;
+ int *firstchas;
+
+ do {
+ if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, curnode,
+ PROP_FIRST_CHAS, &firstchas) >= 0)
+ return (curnode);
+ } while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
+
+ return (DI_NODE_NIL);
+}
+
+
+/*
+ * yet another redundant common routine to:
+ * decode the ieee1275 "slot-names" property and returns the string matching
+ * the pci device number <pci_dev>, if any.
+ *
+ * callers must NOT free the returned string
+ *
+ * "slot-names" format: [int][string1][string2]...[stringN]
+ * - each bit position in [int] represent a pci device number
+ * - [string1]...[stringN] are concatenated null-terminated strings
+ * - the number of bits set in [int] == the number of strings that follow
+ * - each bit that is set corresponds to a string in the following segment
+ */
+static char *
+pci_cfg_slotname(di_node_t node, di_prom_handle_t ph, minor_t pci_dev)
+{
+#ifdef DEBUG
+ char *fnm = "pci_cfg_slotname";
+#endif
+ int *snp;
+ int snlen;
+ int snentlen = sizeof (int);
+ int i, max, len, place, curplace;
+ char *str;
+
+ snlen = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node,
+ PROP_SLOT_NAMES, &snp);
+ if (snlen < 1)
+ return (NULL);
+ if ((snp[0] & (1 << pci_dev)) == 0)
+ return (NULL);
+
+ /*
+ * pci device number must be less than the amount of bits in the first
+ * [int] component of slot-names
+ */
+ if (pci_dev >= snentlen * 8) {
+ dprint(("%s: pci_dev out of range for %s%d\n",
+ fnm, DRVINST(node)));
+ return (NULL);
+ }
+
+ place = 0;
+ for (i = 0; i < pci_dev; i++) {
+ if (snp[0] & (1 << i))
+ place++;
+ }
+
+ max = (snlen * snentlen) - snentlen;
+ str = (char *)&snp[1];
+ i = 0;
+ curplace = 0;
+ while (i < max && curplace < place) {
+ len = strlen(str);
+ if (len <= 0)
+ break;
+ str += len + 1;
+ i += len + 1;
+ curplace++;
+ }
+ /* the following condition indicates a badly formed slot-names */
+ if (i >= max || *str == '\0') {
+ dprint(("%s: badly formed slot-names for %s%d\n",
+ fnm, DRVINST(node)));
+ str = NULL;
+ }
+ return (str);
+}
+
+
+/*
+ * returns non-zero if we can return a valid attachment point name for <node>,
+ * for its slot identified by child pci device number <pci_dev>, through <buf>
+ *
+ * prioritized naming scheme:
+ * 1) <PROP_SLOT_NAMES property> (see pci_cfg_slotname())
+ * 2) <device-type><PROP_PHYS_SLOT property>
+ * 3) <drv name><drv inst>.<device-type><pci_dev>
+ *
+ * where <device-type> is derived from the PROP_DEV_TYPE property:
+ * if its value is "pciex" then <device-type> is "pcie"
+ * else the raw value is used
+ *
+ * if <flags> contains APNODE_DEFNAME, then scheme (3) is used
+ */
+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)
+{
+ int *nump;
+ int rv;
+ char *str, *devtype;
+
+ rv = di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, node,
+ PROP_DEV_TYPE, &devtype);
+ if (rv < 1)
+ return (0);
+
+ if (strcmp(devtype, PROPVAL_PCIEX) == 0)
+ devtype = DEVTYPE_PCIE;
+
+ if (flags & APNODE_DEFNAME)
+ goto DEF;
+
+ str = pci_cfg_slotname(node, ph, pci_dev);
+ if (str != NULL) {
+ (void) strlcpy(buf, str, bufsz);
+ return (1);
+ }
+
+ if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_PHYS_SLOT,
+ &nump) > 0) {
+ if (*nump > 0) {
+ (void) snprintf(buf, bufsz, "%s%d", devtype, *nump);
+ return (1);
+ }
+ }
+DEF:
+ (void) snprintf(buf, bufsz, "%s%d.%s%d",
+ di_driver_name(node), di_instance(node), devtype, pci_dev);
+
+ return (1);
+}
+
+
+/*
+ * returns non-zero if we can return a valid expansion chassis name for <node>
+ * through <buf>
+ *
+ * prioritized naming scheme:
+ * 1) <IOB_PRE string><PROP_SERID property: sun specific portion>
+ * 2) <IOB_PRE string><full PROP_SERID property in hex>
+ * 3) <IOB_PRE string>
+ *
+ * PROP_SERID encoding <64-bit int: msb ... lsb>:
+ * <24 bits: vendor id><40 bits: serial number>
+ *
+ * sun encoding of 40 bit serial number:
+ * first byte = device type indicator (ignored in naming scheme)
+ * next 4 bytes = 4 ascii characters
+ */
+/*ARGSUSED*/
+static int
+pci_cfg_iob_name(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
+ char *buf, int bufsz)
+{
+ int64_t *seridp;
+ int64_t serid;
+ char *idstr;
+
+ if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, PROP_SERID,
+ &seridp) < 1) {
+ (void) strlcpy(buf, IOB_PRE, bufsz);
+ return (1);
+ }
+ serid = *seridp;
+
+ if (serid >> 40 != VENDID_SUN) {
+ (void) snprintf(buf, bufsz, "%s%llx", IOB_PRE, serid);
+ return (1);
+ }
+
+ serid &= SIZE2MASK64(40);
+ idstr = (char *)&serid;
+ idstr[sizeof (serid) - 1] = '\0';
+ /* skip device type indicator */
+ idstr++;
+ (void) snprintf(buf, bufsz, "%s%s", IOB_PRE, idstr);
+ return (1);
+}
+
+
+static minor_t
+pci_cfg_pcidev(di_node_t node, di_prom_handle_t ph)
+{
+ int rv;
+ int *regp;
+
+ rv = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_REG,
+ &regp);
+
+ if (rv < 1) {
+ dprint(("pci_cfg_pcidev: property %s not found "
+ "for %s%d\n", PROP_REG, DRVINST(node)));
+ return (rv);
+ }
+
+ return (REG_PCIDEV(regp));
+}
+
+
+/*
+ * returns non-zero when it can successfully return an attachment point
+ * through <ap_path> whose length is less than <ap_pathsz>; returns the full
+ * path of the AP through <pathret> which may be larger than <ap_pathsz>.
+ * Callers need to free <pathret>. If it cannot return the full path through
+ * <pathret> it will be set to NULL
+ *
+ * The ap path reflects a subset of the device path from an onboard host slot
+ * up to <node>. We traverse up the device tree starting from <node>, naming
+ * each component using pci_cfg_ap_node(). If we detect that a certain
+ * segment is contained within an expansion chassis, then we skip any bus
+ * nodes in between our current node and the topmost node of the chassis,
+ * which is identified by the PROP_FIRST_CHAS property, and prepend the name
+ * of the expansion chassis as given by pci_cfg_iob_name()
+ *
+ * This scheme is always used for <pathret>. If however, the size of
+ * <pathret> is greater than <ap_pathsz> then only the default name as given
+ * by pci_cfg_ap_node() for <node> will be used
+ */
+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)
+{
+#ifdef DEBUG
+ char *fnm = "pci_cfg_ap_path";
+#endif
+#define seplen (sizeof (AP_PATH_SEP) - 1)
+#define iob_pre_len (sizeof (IOB_PRE) - 1)
+#define ap_path_iob_sep_len (sizeof (AP_PATH_IOB_SEP) - 1)
+
+ char *bufptr;
+ char buf[MAXPATHLEN];
+ char pathbuf[MAXPATHLEN];
+ int bufsz;
+ char *pathptr;
+ char *pathend = NULL;
+ int len;
+ int rv = 0;
+ int chasflag = 0;
+ di_node_t curnode = node;
+ di_node_t chasnode = DI_NODE_NIL;
+ minor_t pci_dev;
+
+ buf[0] = '\0';
+ pathbuf[0] = '\0';
+ pathptr = &pathbuf[sizeof (pathbuf) - 1];
+ *pathptr = '\0';
+
+ /*
+ * as we traverse up the device tree, we prepend components of our
+ * path inside pathbuf, using pathptr and decrementing
+ */
+ pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
+ do {
+ bufptr = buf;
+ bufsz = sizeof (buf);
+
+ chasnode = pci_cfg_chassis_node(curnode, ph);
+ if (chasnode != DI_NODE_NIL) {
+ rv = pci_cfg_iob_name(minor, chasnode, ph,
+ bufptr, bufsz);
+ if (rv == 0) {
+ dprint(("%s: cannot create iob name "
+ "for %s%d\n", fnm, DRVINST(node)));
+ *pathptr = '\0';
+ goto OUT;
+ }
+
+ (void) strncat(bufptr, AP_PATH_IOB_SEP, bufsz);
+ len = strlen(bufptr);
+ bufptr += len;
+ bufsz -= len - 1;
+
+ /* set chasflag when the leaf node is within an iob */
+ if ((curnode == node) != NULL)
+ chasflag = 1;
+ }
+ rv = pci_cfg_ap_node(pci_dev, curnode, ph, bufptr, bufsz, 0);
+ if (rv == 0) {
+ dprint(("%s: cannot create ap node name "
+ "for %s%d\n", fnm, DRVINST(node)));
+ *pathptr = '\0';
+ goto OUT;
+ }
+
+ /*
+ * if we can't fit the entire path in our pathbuf, then use
+ * the default short name and nullify pathptr; also, since
+ * we prepend in the buffer, we must avoid adding a null char
+ */
+ if (curnode != node) {
+ pathptr -= seplen;
+ if (pathptr < pathbuf) {
+ pathptr = pathbuf;
+ *pathptr = '\0';
+ goto DEF;
+ }
+ (void) memcpy(pathptr, AP_PATH_SEP, seplen);
+ }
+ len = strlen(buf);
+ pathptr -= len;
+ if (pathptr < pathbuf) {
+ pathptr = pathbuf;
+ *pathptr = '\0';
+ goto DEF;
+ }
+ (void) memcpy(pathptr, buf, len);
+
+ /* remember the leaf component */
+ if (curnode == node)
+ pathend = pathptr;
+
+ /*
+ * go no further than the hosts' onboard slots
+ */
+ if (chasnode == DI_NODE_NIL)
+ break;
+ curnode = chasnode;
+
+ /*
+ * the pci device number of the current node is used to
+ * identify which slot of the parent's bus (next iteration)
+ * the current node is on
+ */
+ pci_dev = pci_cfg_pcidev(curnode, ph);
+ } while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
+
+ pathbuf[sizeof (pathbuf) - 1] = '\0';
+ if (strlen(pathptr) < ap_pathsz) {
+ (void) strlcpy(ap_path, pathptr, ap_pathsz);
+ rv = 1;
+ goto OUT;
+ }
+
+DEF:
+ /*
+ * when our name won't fit <ap_pathsz> we use the endpoint/leaf
+ * <node>'s name ONLY IF it has a serialid# which will make the apid
+ * globally unique
+ */
+ if (chasflag && pathend != NULL) {
+ if ((strncmp(pathend + iob_pre_len, AP_PATH_IOB_SEP,
+ ap_path_iob_sep_len) != 0) &&
+ (strlen(pathend) < ap_pathsz)) {
+ (void) strlcpy(ap_path, pathend, ap_pathsz);
+ rv = 1;
+ goto OUT;
+ }
+ }
+
+ /*
+ * if our name still won't fit <ap_pathsz>, then use the leaf <node>'s
+ * default name
+ */
+ rv = pci_cfg_ap_node(pci_dev, node, ph, buf, bufsz, APNODE_DEFNAME);
+ if (rv == 0) {
+ dprint(("%s: cannot create default ap node name for %s%d\n",
+ fnm, DRVINST(node)));
+ *pathptr = '\0';
+ goto OUT;
+ }
+ if (strlen(buf) < ap_pathsz) {
+ (void) strlcpy(ap_path, buf, ap_pathsz);
+ rv = 1;
+ goto OUT;
+ }
+
+ /*
+ * in this case, cfgadm goes through an expensive process to generate
+ * a purely dynamic logical apid: the framework will look through
+ * the device tree for attachment point minor nodes and will invoke
+ * each plugin responsible for that attachment point class, and if
+ * the plugin returns a logical apid that matches the queried apid
+ * or matches the default apid generated by the cfgadm framework for
+ * that driver/class (occurs when plugin returns an empty logical apid)
+ * then that is what it will use
+ *
+ * it is doubly expensive because the cfgadm pci plugin itself will
+ * also search the entire device tree in the absence of a link
+ */
+ rv = 0;
+ dprint(("%s: cannot create apid for %s%d within length of %d\n",
+ fnm, DRVINST(node), ap_pathsz));
+
+OUT:
+ ap_path[ap_pathsz - 1] = '\0';
+ *pathret = (*pathptr == '\0') ? NULL : strdup(pathptr);
+ return (rv);
+
+#undef seplen
+#undef iob_pre_len
+#undef ap_path_iob_sep_len
+}
+
+
+/*
+ * the PROP_AP_NAMES property contains the first integer section of the
+ * ieee1275 "slot-names" property and functions as a bitmask; see comment for
+ * pci_cfg_slotname()
+ *
+ * we use the name of the attachment point minor node if its pci device
+ * number (encoded in the minor number) is allowed by PROP_AP_NAMES
+ *
+ * returns non-zero if we return a valid attachment point through <path>
+ */
+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)
+{
+ minor_t pci_dev;
+ int *anp;
+
+ if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_AP_NAMES,
+ &anp) < 1)
+ return (0);
+
+ pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
+ if ((*anp & (1 << pci_dev)) == 0)
+ return (0);
+
+ (void) strlcpy(ap_path, di_minor_name(minor), ap_pathsz);
+ return (1);
+}
+
+
+/*
+ * determine if <node> qualifies for a path style apid
+ */
+static int
+pci_cfg_is_ap_path(di_node_t node, di_prom_handle_t ph)
+{
+ char *devtype;
+ di_node_t curnode = node;
+
+ do {
+ if (di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, curnode,
+ PROP_DEV_TYPE, &devtype) > 0)
+ if (strcmp(devtype, PROPVAL_PCIEX) == 0)
+ return (1);
+ } while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
+
+ return (0);
+}
+
+
+/*
+ * takes a full path as returned by <pathret> from pci_cfg_ap_path() and
+ * returns an allocated string intendend to be stored in a devlink info (dli)
+ * file
+ *
+ * data format: "Location: <transformed path>"
+ * where <transformed path> is <path> with occurrances of AP_PATH_SEP
+ * replaced by "/"
+ */
+static char *
+pci_cfg_info_data(char *path)
+{
+#define head "Location: "
+#define headlen (sizeof (head) - 1)
+#define seplen (sizeof (AP_PATH_SEP) - 1)
+
+ char *sep, *prev, *np;
+ char *newpath;
+ int pathlen = strlen(path);
+ int len;
+
+ newpath = malloc(sizeof (char) * (headlen + pathlen + 1));
+ np = newpath;
+ (void) strcpy(np, head);
+ np += headlen;
+
+ prev = path;
+ while ((sep = strstr(prev, AP_PATH_SEP)) != NULL) {
+ len = sep - prev;
+ (void) memcpy(np, prev, len);
+ np += len;
+ *np++ = '/';
+ prev = sep + seplen;
+ }
+ (void) strcpy(np, prev);
+ return (newpath);
+
+#undef head
+#undef headlen
+#undef seplen
+}
+
+
+static void
+pci_cfg_rm_link(char *file)
+{
+ char *dlipath;
+
+ dlipath = di_dli_name(file);
+ (void) unlink(dlipath);
+
+ devfsadm_rm_all(file);
+ free(dlipath);
+}
+
+/*
+ * removes all registered devlinks to physical path <physpath> except for
+ * the devlink <valid> if not NULL;
+ * <physpath> must include the minor node
*/
+static void
+pci_cfg_rm_invalid_links(char *physpath, char *valid)
+{
+ char **dnp;
+ char *cp, *vcp;
+ int i, dnlen;
+
+ dnp = devfsadm_lookup_dev_names(physpath, NULL, &dnlen);
+ if (dnp == NULL)
+ return;
+
+ if (valid != NULL) {
+ if (strncmp(valid, DEV "/", DEV_LEN + 1) == 0)
+ vcp = valid + DEV_LEN + 1;
+ else
+ vcp = valid;
+ }
+
+ for (i = 0; i < dnlen; i++) {
+ if (strncmp(dnp[i], DEV "/", DEV_LEN + 1) == 0)
+ cp = dnp[i] + DEV_LEN + 1;
+ else
+ cp = dnp[i];
+
+ if (valid != NULL) {
+ if (strcmp(vcp, cp) == 0)
+ continue;
+ }
+ pci_cfg_rm_link(cp);
+ }
+ devfsadm_free_dev_names(dnp, dnlen);
+}
+
+
+/*
+ * takes a complete devinfo snapshot and returns the root node;
+ * callers must do a di_fini() on the returned node;
+ * if the snapshot failed, DI_NODE_NIL is returned instead
+ *
+ * if <pci_node> is not DI_NODE_NIL, it will search for the same devinfo node
+ * in the new snapshot and return it through <ret_node> if it is found,
+ * else DI_NODE_NIL is returned instead
+ *
+ * in addition, if <pci_minor> is not DI_MINOR_NIL, it will also return
+ * the matching minor in the new snapshot through <ret_minor> if it is found,
+ * else DI_MINOR_NIL is returned instead
+ */
+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)
+{
+ di_node_t root_node;
+ di_node_t node;
+ di_minor_t minor;
+ int pci_inst;
+ dev_t pci_devt;
+
+ *ret_node = DI_NODE_NIL;
+ *ret_minor = DI_MINOR_NIL;
+
+ root_node = di_init("/", DINFOCPYALL);
+ if (root_node == DI_NODE_NIL)
+ return (DI_NODE_NIL);
+
+ /*
+ * narrow down search by driver, then instance, then minor
+ */
+ if (pci_node == DI_NODE_NIL)
+ return (root_node);
+
+ pci_inst = di_instance(pci_node);
+ node = di_drv_first_node(di_driver_name(pci_node), root_node);
+ do {
+ if (pci_inst == di_instance(node)) {
+ *ret_node = node;
+ break;
+ }
+ } while ((node = di_drv_next_node(node)) != DI_NODE_NIL);
+
+ if (node == DI_NODE_NIL)
+ return (root_node);
+
+ /*
+ * found node, now search minors
+ */
+ if (pci_minor == DI_MINOR_NIL)
+ return (root_node);
+
+ pci_devt = di_minor_devt(pci_minor);
+ minor = DI_MINOR_NIL;
+ while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
+ if (pci_devt == di_minor_devt(minor)) {
+ *ret_minor = minor;
+ break;
+ }
+ }
+ return (root_node);
+}
+
+
static int
-pci_cfg_creat_cb(di_minor_t minor, di_node_t node)
+pci_cfg_creat_cb(di_minor_t pci_minor, di_node_t pci_node)
{
- char *minor_name, *dev_path;
- char path[PATH_MAX + 1];
- int *devlink_flags;
- minor_t pci_dev;
- di_node_t dev_node;
-
- minor_name = di_minor_name(minor);
- pci_dev = (minor->dev_minor) & 0xFF;
-
- dev_path = di_devfs_path(node);
- dev_node = di_init(dev_path, DINFOCPYALL);
- if ((di_prop_lookup_ints(DDI_DEV_T_ANY, dev_node,
- "ap-names", &devlink_flags)) > 0) {
- if ((*devlink_flags) & (1 << pci_dev)) {
- (void) snprintf(path, sizeof (path), "%s/%s",
- CFG_DIRNAME, minor_name);
- (void) devfsadm_mklink(path, node, minor, 0);
+#ifdef DEBUG
+ char *fnm = "pci_cfg_creat_cb";
+#endif
+#define ap_pathsz (sizeof (ap_path))
+
+ char ap_path[CFGA_LOG_EXT_LEN];
+ char linkbuf[MAXPATHLEN];
+ char *fullpath = NULL;
+ char *pathinfo = NULL;
+ char *devpath = NULL;
+ int rv, fd;
+ size_t sz;
+ di_prom_handle_t ph;
+ di_node_t node;
+ di_node_t root_node = DI_NODE_NIL;
+ di_minor_t minor;
+
+ ph = di_prom_init();
+ if (ph == DI_PROM_HANDLE_NIL) {
+ dprint(("%s: di_prom_init() failed for %s%d\n",
+ fnm, DRVINST(pci_node)));
+ goto OUT;
+ }
+
+ /*
+ * Since incoming nodes from hotplug events are from snapshots that
+ * do NOT contain parent/ancestor data, we must retake our own
+ * snapshot and search for the target node
+ */
+ root_node = pci_cfg_snapshot(pci_node, pci_minor, &node, &minor);
+ if (root_node == DI_NODE_NIL || node == DI_NODE_NIL ||
+ minor == DI_MINOR_NIL) {
+ dprint(("%s: devinfo snapshot or search failed for %s%d\n",
+ fnm, DRVINST(pci_node)));
+ goto OUT;
+ }
+
+ if (pci_cfg_is_ap_path(node, ph)) {
+ rv = pci_cfg_ap_path(minor, node, ph, ap_path, ap_pathsz,
+ &fullpath);
+ if (rv == 0)
+ goto OUT;
+
+ (void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
+ CFG_DIRNAME, ap_path);
+
+ /*
+ * We must remove existing links because we may have invalid
+ * apids that are valid links. Since these are not dangling,
+ * devfsadm will not invoke the remove callback on them.
+ *
+ * What are "invalid apids with valid links"? Consider swapping
+ * an attachment point bus with another while the system is
+ * down, on the same device path bound to the same drivers
+ * but with the new AP bus having different properties
+ * (e.g. serialid#). If the previous apid is not removed,
+ * there will now be two different links pointing to the same
+ * attachment point, but only one reflects the correct
+ * logical apid
+ */
+ devpath = pci_cfg_devpath(node, minor);
+ if (devpath == NULL)
+ goto OUT;
+ pci_cfg_rm_invalid_links(devpath, linkbuf);
+ free(devpath);
+
+ (void) devfsadm_mklink(linkbuf, node, minor, 0);
+
+ /*
+ * we store the full logical path of the attachment point for
+ * cfgadm to display in its info field which is useful when
+ * the full logical path exceeds the size limit for logical
+ * apids (CFGA_LOG_EXT_LEN)
+ *
+ * for the cfgadm pci plugin to do the same would be expensive
+ * (i.e. devinfo snapshot + top down exhaustive minor search +
+ * equivalent of pci_cfg_ap_path() on every invocation)
+ *
+ * note that if we do not create a link (pci_cfg_ap_path() is
+ * not successful), that is what cfgadm will do anyways to
+ * create a purely dynamic apid
+ */
+ pathinfo = pci_cfg_info_data(fullpath);
+ fd = di_dli_openw(linkbuf);
+ if (fd < 0)
+ goto OUT;
+
+ sz = strlen(pathinfo) + 1;
+ rv = write(fd, pathinfo, sz);
+ if (rv < sz) {
+ dprint(("%s: could not write full pathinfo to dli "
+ "file for %s%d\n", fnm, DRVINST(node)));
+ goto OUT;
}
+ di_dli_close(fd);
+ } else {
+ rv = pci_cfg_ap_legacy(minor, node, ph, ap_path,
+ ap_pathsz);
+ if (rv == 0)
+ goto OUT;
+
+ (void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
+ CFG_DIRNAME, ap_path);
+ (void) devfsadm_mklink(linkbuf, node, minor, 0);
}
- di_fini(dev_node);
- (void) di_devfs_path_free(dev_path);
+OUT:
+ if (fullpath != NULL)
+ free(fullpath);
+ if (pathinfo != NULL)
+ free(pathinfo);
+ if (ph != DI_PROM_HANDLE_NIL)
+ di_prom_fini(ph);
+ if (root_node != DI_NODE_NIL)
+ di_fini(root_node);
return (DEVFSADM_CONTINUE);
+
+#undef ap_pathsz
+}
+
+
+static void
+pci_cfg_rm_all(char *file)
+{
+ pci_cfg_rm_link(file);
}
diff --git a/usr/src/cmd/devfsadm/cfg_link.h b/usr/src/cmd/devfsadm/cfg_link.h
new file mode 100644
index 0000000000..1ed0c059c9
--- /dev/null
+++ b/usr/src/cmd/devfsadm/cfg_link.h
@@ -0,0 +1,100 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* private devlink info interfaces */
+
+#ifndef _CFG_LINK_H
+#define _CFG_LINK_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <devfsadm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SCSI_CFG_LINK_RE "^cfg/c[0-9]+$"
+#define SBD_CFG_LINK_RE "^cfg/((((N[0-9]+[.])?(SB|IB))?[0-9]+)|[abcd])$"
+#define USB_CFG_LINK_RE "^cfg/((usb[0-9]+)/([0-9]+)([.]([0-9])+)*)$"
+#define PCI_CFG_LINK_RE "^cfg/[:alnum:]$"
+#define IB_CFG_LINK_RE "^cfg/(hca[0-9A-F]+)$"
+#define SATA_CFG_LINK_RE "^cfg/((sata[0-9]+)/([0-9]+)([.]([0-9])+)*)$"
+#define PCI_CFG_PATH_LINK_RE "^cfg/(.*pcie.*)$"
+
+#define CFG_DIRNAME "cfg"
+
+#define PROP_FIRST_CHAS "first-in-chassis"
+#define PROP_SLOT_NAMES "slot-names"
+#define PROP_PHYS_SLOT "physical-slot#"
+#define PROP_DEV_TYPE "device_type"
+#define PROP_BUS_RANGE "bus-range"
+#define PROP_SERID "serialid#"
+#define PROP_REG "reg"
+#define PROP_AP_NAMES "ap-names"
+#define PROPVAL_PCIEX "pciex"
+#define DEVTYPE_PCIE "pcie"
+#define IOB_PRE "iob"
+#define AP_PATH_SEP ":"
+#define AP_PATH_IOB_SEP "."
+#define VENDID_SUN 0x108e
+#define APNODE_DEFNAME 0x1
+
+/* converts size in bits to a mask covering those bit positions */
+#define SIZE2MASK(s) ((1 << (s)) - 1)
+#define SIZE2MASK64(s) ((1LL << (s)) - 1LL)
+
+/*
+ * macros for the ieee1275 "reg" property
+ * naming format and semantics:
+ *
+ * REG_<cell>_SIZE_<field> = bit size of <field> in <cell>
+ * REG_<cell>_OFF_<field> = starting bit position of <field> in <cell>
+ *
+ * REG_<cell>_<field>(r) = returns the value of <field> in <cell> using:
+ * (((r) >> REG_<cell>_OFF_<field>) & SIZE2MASK(REG_<cell>_SIZE_<field>))
+ */
+#define REG_PHYSHI_SIZE_PCIDEV 5
+#define REG_PHYSHI_OFF_PCIDEV 11
+#define REG_PHYSHI_PCIDEV(r) \
+ (((r) >> REG_PHYSHI_OFF_PCIDEV) & SIZE2MASK(REG_PHYSHI_SIZE_PCIDEV))
+
+/* rp = ptr to 5-tuple int array */
+#define REG_PHYSHI_INDEX 0
+#define REG_PHYSHI(rp) ((rp)[REG_PHYSHI_INDEX])
+
+#define REG_PCIDEV(rp) (REG_PHYSHI_PCIDEV(REG_PHYSHI(rp)))
+
+
+#define DEV "/dev"
+#define DEV_LEN 4
+#define DEVICES "/devices"
+#define DEVICES_LEN 8
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CFG_LINK_H */
diff --git a/usr/src/cmd/devfsadm/devfsadm.c b/usr/src/cmd/devfsadm/devfsadm.c
index 9b719ac04c..67a46fad15 100644
--- a/usr/src/cmd/devfsadm/devfsadm.c
+++ b/usr/src/cmd/devfsadm/devfsadm.c
@@ -6931,6 +6931,60 @@ devfsadm_root_path(void)
}
}
+void
+devfsadm_free_dev_names(char **dev_names, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ free(dev_names[i]);
+ free(dev_names);
+}
+
+/*
+ * Return all devlinks corresponding to phys_path as an array of strings.
+ * The number of entries in the array is returned through lenp.
+ * devfsadm_free_dev_names() is used to free the returned array.
+ * NULL is returned on failure or when there are no matching devlinks.
+ *
+ * re is an extended regular expression in regex(5) format used to further
+ * match devlinks pointing to phys_path; it may be NULL to match all
+ */
+char **
+devfsadm_lookup_dev_names(char *phys_path, char *re, int *lenp)
+{
+ struct devlink_cb_arg cb_arg;
+ char **dev_names = NULL;
+ int i;
+
+ *lenp = 0;
+ cb_arg.count = 0;
+ cb_arg.rv = 0;
+ (void) di_devlink_cache_walk(devlink_cache, re, phys_path,
+ DI_PRIMARY_LINK, &cb_arg, devlink_cb);
+
+ if (cb_arg.rv == -1 || cb_arg.count <= 0)
+ return (NULL);
+
+ dev_names = s_malloc(cb_arg.count * sizeof (char *));
+ if (dev_names == NULL)
+ goto out;
+
+ for (i = 0; i < cb_arg.count; i++) {
+ dev_names[i] = s_strdup(cb_arg.dev_names[i]);
+ if (dev_names[i] == NULL) {
+ devfsadm_free_dev_names(dev_names, i);
+ dev_names = NULL;
+ goto out;
+ }
+ }
+ *lenp = cb_arg.count;
+
+out:
+ free_dev_names(&cb_arg);
+ return (dev_names);
+}
+
/* common exit function which ensures releasing locks */
static void
devfsadm_exit(int status)
diff --git a/usr/src/cmd/devfsadm/devfsadm.h b/usr/src/cmd/devfsadm/devfsadm.h
index ac72a5fca4..43274c9612 100644
--- a/usr/src/cmd/devfsadm/devfsadm.h
+++ b/usr/src/cmd/devfsadm/devfsadm.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -180,6 +179,9 @@ int devfsadm_enumerate_int(char *devfs_path, int index, char **buf,
devfsadm_enumerate_t rules[], int nrules);
int devfsadm_enumerate_char(char *devfs_path, int index, char **buf,
devfsadm_enumerate_t rules[], int nrules);
+char **devfsadm_lookup_dev_names(char *phys_path, char *re, int *lenp);
+void devfsadm_free_dev_names(char **dev_names, int len);
+
/*
* Private enumerate interface for disks and sgen modules
*/
diff --git a/usr/src/cmd/devfsadm/devfsadm_impl.h b/usr/src/cmd/devfsadm/devfsadm_impl.h
index d0f5703cf6..d73da6d5bd 100644
--- a/usr/src/cmd/devfsadm/devfsadm_impl.h
+++ b/usr/src/cmd/devfsadm/devfsadm_impl.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,7 +18,7 @@
*
* CDDL HEADER END
*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -516,6 +515,8 @@ static int notify_rcm(di_node_t node, char *minor_name);
static void dca_impl_init(char *root, char *minor, struct dca_impl *dcip);
static void lock_dev(void);
static void unlock_dev(int flag);
+static int devlink_cb(di_devlink_t dl, void *arg);
+static void free_dev_names(struct devlink_cb_arg *x);
int load_devpolicy(void);
static void load_dev_acl(void);
diff --git a/usr/src/lib/cfgadm_plugins/pci/common/cfga.c b/usr/src/lib/cfgadm_plugins/pci/common/cfga.c
index cd5ea37d56..c675b9ed3c 100644
--- a/usr/src/lib/cfgadm_plugins/pci/common/cfga.c
+++ b/usr/src/lib/cfgadm_plugins/pci/common/cfga.c
@@ -162,6 +162,7 @@ static void build_control_data(struct hpc_control_data *iocdata, uint_t cmd,
void *retdata);
static cfga_err_t check_options(const char *options);
static void cfga_msg(struct cfga_msg *msgp, const char *str);
+static char *findlink(char *ap_phys_id);
static char *
cfga_strs[] = {
@@ -1439,6 +1440,60 @@ fix_ap_name(char *ap_log_id, const char *ap_id, char *slot_name,
return (CFGA_OK);
}
+
+static int
+findlink_cb(di_devlink_t devlink, void *arg)
+{
+ (*(char **)arg) = strdup(di_devlink_path(devlink));
+
+ return (DI_WALK_TERMINATE);
+}
+
+/*
+ * returns an allocated string containing the full path to the devlink for
+ * <ap_phys_id> in the devlink database; we expect only one devlink per
+ * <ap_phys_id> so we return the first encountered
+ */
+static char *
+findlink(char *ap_phys_id)
+{
+ di_devlink_handle_t hdl;
+ char *path = NULL;
+
+ hdl = di_devlink_init(NULL, 0);
+
+ if (strncmp("/devices/", ap_phys_id, 9) == 0)
+ ap_phys_id += 8;
+
+ (void) di_devlink_walk(hdl, "^cfg/.+$", ap_phys_id, DI_PRIMARY_LINK,
+ (void *)&path, findlink_cb);
+
+ (void) di_devlink_fini(&hdl);
+ return (path);
+}
+
+
+/*
+ * returns CFGA_OK if it can succesfully retrieve the devlink info associated
+ * with devlink for <ap_phys_id> which will be returned through <ap_info>
+ */
+cfga_err_t
+get_dli(char *dlpath, char *ap_info, int ap_info_sz)
+{
+ int fd;
+
+ fd = di_dli_openr(dlpath);
+ if (fd < 0)
+ return (CFGA_ERROR);
+
+ (void) read(fd, ap_info, ap_info_sz);
+ ap_info[ap_info_sz - 1] = '\0';
+
+ di_dli_close(fd);
+ return (CFGA_OK);
+}
+
+
/*ARGSUSED*/
cfga_err_t
cfga_list_ext(const char *ap_id, cfga_list_data_t **cs,
@@ -1454,6 +1509,7 @@ cfga_list_ext(const char *ap_id, cfga_list_data_t **cs,
struct searcharg slotname_arg;
int fd;
int rv = CFGA_OK;
+ char *dlpath = NULL;
if ((rv = check_options(options)) != CFGA_OK) {
return (rv);
@@ -1479,6 +1535,7 @@ cfga_list_ext(const char *ap_id, cfga_list_data_t **cs,
rv = CFGA_ERROR;
return (rv);
}
+ (void) memset(*cs, 0, sizeof (cfga_list_data_t));
if ((dcp = devctl_ap_acquire((char *)ap_id, 0)) == NULL) {
cfga_err(errstring, CMD_GETSTAT, 0);
@@ -1618,15 +1675,25 @@ cfga_list_ext(const char *ap_id, cfga_list_data_t **cs,
cont:
(void) strcpy((*cs)->ap_phys_id, ap_id); /* physical path of AP */
+
+ dlpath = findlink((*cs)->ap_phys_id);
+ if (dlpath != NULL) {
+ if (get_dli(dlpath, (*cs)->ap_info,
+ sizeof ((*cs)->ap_info)) != CFGA_OK)
+ (*cs)->ap_info[0] = '\0';
+ free(dlpath);
+ }
+
if ((*cs)->ap_log_id[0] == '\0')
(void) strcpy((*cs)->ap_log_id, slot_info.pci_slot_name);
- /* slot_names of bus node */
- if (find_physical_slot_names(ap_id, &slotname_arg) != -1)
- (void) strcpy((*cs)->ap_info,
- slotname_arg.slotnames[slotname_arg.minor]);
+ if ((*cs)->ap_info[0] == '\0') {
+ /* slot_names of bus node */
+ if (find_physical_slot_names(ap_id, &slotname_arg) != -1)
+ (void) strcpy((*cs)->ap_info,
+ slotname_arg.slotnames[slotname_arg.minor]);
+ }
- (void) memset((*cs)->ap_type, 0, CFGA_TYPE_LEN);
/* class_code/subclass/boardtype */
get_type(boardtype, cardinfo, (*cs)->ap_type);
diff --git a/usr/src/lib/libdevinfo/Makefile.com b/usr/src/lib/libdevinfo/Makefile.com
index d071f6a304..048c28653f 100644
--- a/usr/src/lib/libdevinfo/Makefile.com
+++ b/usr/src/lib/libdevinfo/Makefile.com
@@ -29,13 +29,13 @@ LIBRARY= libdevinfo.a
VERS= .1
OBJECTS= devfsinfo.o devinfo.o devinfo_prop_decode.o devinfo_devlink.o \
- devinfo_devperm.o devfsmap.o
+ devinfo_devperm.o devfsmap.o devinfo_dli.o
include ../../Makefile.lib
include ../../Makefile.rootfs
LIBS = $(DYNLIB) $(LINTLIB)
-LDLIBS += -lnvpair -lsec -lc
+LDLIBS += -lnvpair -lsec -lc -lgen
$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
CFLAGS += $(CCVERBOSE)
diff --git a/usr/src/lib/libdevinfo/devinfo_dli.c b/usr/src/lib/libdevinfo/devinfo_dli.c
new file mode 100644
index 0000000000..a0120027ee
--- /dev/null
+++ b/usr/src/lib/libdevinfo/devinfo_dli.c
@@ -0,0 +1,158 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Interfaces for getting device configuration data from kernel
+ * through the devinfo driver.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <libgen.h>
+#include "libdevinfo.h"
+
+/* Function Prototypes */
+static int di_dli_open(char *, int, short, int);
+
+#define DLI_NAME 0x1
+
+/*
+ * Private hotplug interfaces to be used between cfgadm pci plugin and
+ * devfsadm link generator.
+ */
+
+
+/*
+ * returns a devlink info file name derived from <path>
+ * callers need to free the returned string
+ */
+char *
+di_dli_name(char *path)
+{
+#define dliroot "/etc/devices/dli/info."
+#define dliroot_len (sizeof (dliroot) - 1)
+
+ char *dlipath;
+ int dlipathsz;
+ char *basep;
+
+ basep = basename(path);
+ dlipathsz = strlen(basep) + dliroot_len + 1;
+ dlipath = malloc(sizeof (char) * dlipathsz);
+
+ (void) snprintf(dlipath, dlipathsz, "%s%s", dliroot, basep);
+ dlipath[dlipathsz - 1] = '\0';
+ return (dlipath);
+
+#undef dlipre
+#undef dlipre_len
+#undef dliroot
+#undef dliroot_len
+}
+
+
+static int
+di_dli_open(char *path, int oflag, short l_type, int flags)
+{
+ int fd;
+ char *dlipath, *dlipath_dir, *dlipath_dup;
+ struct stat statbuf;
+ int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ flock_t lock;
+
+ dlipath = (flags & DLI_NAME) ? di_dli_name(path) : (char *)path;
+ dlipath_dup = strdup(dlipath);
+ dlipath_dir = dirname(dlipath_dup);
+
+ if (stat(dlipath_dir, &statbuf) < 0) {
+ if (mkdirp(dlipath_dir,
+ S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
+ fd = -1;
+ goto OUT;
+ }
+ }
+
+ fd = open(dlipath, oflag, mode);
+ if (fd < 0)
+ goto OUT;
+
+ if (fchmod(fd, mode) < 0) {
+ (void) close(fd);
+ fd = -1;
+ goto OUT;
+ }
+
+ bzero(&lock, sizeof (lock));
+ lock.l_type = l_type;
+ if (fcntl(fd, F_SETLKW, &lock) < 0) {
+ (void) close(fd);
+ fd = -1;
+ }
+OUT:
+ free(dlipath_dup);
+ if (flags & DLI_NAME)
+ free(dlipath);
+ return (fd);
+}
+
+
+int
+di_dli_openr(char *path)
+{
+ return (di_dli_open(path, O_RDONLY, F_RDLCK, DLI_NAME));
+}
+
+
+int
+di_dli_openw(char *path)
+{
+ return (di_dli_open(path, O_RDWR | O_SYNC | O_TRUNC | O_CREAT,
+ F_WRLCK, DLI_NAME));
+}
+
+
+void
+di_dli_close(int fd)
+{
+ flock_t lock;
+ if (fd < 0)
+ return;
+
+ bzero(&lock, sizeof (lock));
+ lock.l_type = F_UNLCK;
+ (void) fcntl(fd, F_SETLK, &lock);
+ (void) close(fd);
+}
diff --git a/usr/src/lib/libdevinfo/libdevinfo.h b/usr/src/lib/libdevinfo/libdevinfo.h
index 195eee4e55..62fa27bcb2 100644
--- a/usr/src/lib/libdevinfo/libdevinfo.h
+++ b/usr/src/lib/libdevinfo/libdevinfo.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -385,6 +384,16 @@ extern int di_devperm_logout(const char *);
*/
extern di_node_t di_lookup_node(di_node_t root, char *path);
+
+/*
+ * Private hotplug interfaces to be used between cfgadm pci plugin and
+ * devfsadm link generator.
+ */
+extern char *di_dli_name(char *);
+extern int di_dli_openr(char *);
+extern int di_dli_openw(char *);
+extern void di_dli_close(int);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libdevinfo/mapfile-vers b/usr/src/lib/libdevinfo/mapfile-vers
index 7ddb19dae3..6e84e1bee1 100644
--- a/usr/src/lib/libdevinfo/mapfile-vers
+++ b/usr/src/lib/libdevinfo/mapfile-vers
@@ -180,6 +180,10 @@ SUNWprivate_1.1 {
di_prop_sys_next;
di_vhci_first_node;
di_vhci_next_node;
+ di_dli_name;
+ di_dli_openr;
+ di_dli_openw;
+ di_dli_close;
local:
*;
};
diff --git a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c
index 274be45bb5..1f43392152 100644
--- a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c
+++ b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c
@@ -1933,11 +1933,12 @@ pciehpc_set_slot_name(pciehpc_t *ctrl_p)
(char *)slotname_data + 4);
kmem_free(slotname_data, len);
} else {
- if (invalid_slotnum)
- (void) sprintf(p->slot_info.pci_slot_name, "pcie%ddev0",
- p->slotNum);
+ if (invalid_slotnum) /* use device number ie. 0 */
+ (void) snprintf(p->slot_info.pci_slot_name,
+ sizeof (p->slot_info.pci_slot_name), "pcie0");
else
- (void) sprintf(p->slot_info.pci_slot_name, "pcie%d",
+ (void) snprintf(p->slot_info.pci_slot_name,
+ sizeof (p->slot_info.pci_slot_name), "pcie%d",
p->slotNum);
}
}
diff --git a/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c b/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c
index 99d09f2288..830b0cc59a 100644
--- a/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c
+++ b/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c
@@ -2319,9 +2319,6 @@ pcishpc_set_slot_name(pcishpc_ctrl_t *ctrl_p, int slot)
}
if (!p->phy_slot_num) { /* platform may not have initialized it */
- pcishpc_debug("%s#%d: Invalid slot number! ",
- ddi_driver_name(ctrl_p->shpc_dip),
- ddi_get_instance(ctrl_p->shpc_dip));
p->phy_slot_num = pci_config_get8(ctrl_p->shpc_config_hdl,
PCI_BCNF_SECBUS);
invalid_slotnum = 1;
diff --git a/usr/src/uts/common/io/pci_cap.c b/usr/src/uts/common/io/pci_cap.c
index ee5b2d712a..f9b8f18adf 100644
--- a/usr/src/uts/common/io/pci_cap.c
+++ b/usr/src/uts/common/io/pci_cap.c
@@ -185,7 +185,7 @@ pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
* because no cap list traversal is required.
*/
uint32_t
-pci_cap_get(ddi_acc_handle_t h, pci_config_size_t size,
+pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size,
uint32_t id, uint16_t base, uint16_t offset)
{
uint32_t data;
@@ -226,7 +226,7 @@ pci_cap_get(ddi_acc_handle_t h, pci_config_size_t size,
* because no cap list traversal is required.
*/
int
-pci_cap_put(ddi_acc_handle_t h, pci_config_size_t size,
+pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
uint32_t id, uint16_t base, uint16_t offset,
uint32_t data)
{
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcihp.h b/usr/src/uts/common/sys/hotplug/pci/pcihp.h
index cd8f57a7e7..00ad40be26 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcihp.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pcihp.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -35,6 +34,7 @@ extern "C" {
#include <sys/hotplug/pci/pcihp_impl.h>
+#ifdef _KERNEL
/*
* Interfaces exported by PCI Nexus extension module, kernel/misc/pcihp.
*/
@@ -42,6 +42,7 @@ int pcihp_init(dev_info_t *);
int pcihp_uninit(dev_info_t *);
int pcihp_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
struct cb_ops *pcihp_get_cb_ops(void);
+#endif
/* definitions for minor numbers */
#define PCIHP_AP_MINOR_NUM(x, y) (((uint_t)(x) << 8) | \
diff --git a/usr/src/uts/common/sys/pci_cap.h b/usr/src/uts/common/sys/pci_cap.h
index fafc5f1ab9..43b0974f83 100644
--- a/usr/src/uts/common/sys/pci_cap.h
+++ b/usr/src/uts/common/sys/pci_cap.h
@@ -67,7 +67,7 @@ typedef enum {
PCI_CAP_CFGSZ_8 = 0,
PCI_CAP_CFGSZ_16 = 1,
PCI_CAP_CFGSZ_32 = 2
-} pci_config_size_t;
+} pci_cap_config_size_t;
/* Define Macros */
@@ -107,10 +107,10 @@ typedef enum {
extern int pci_cap_probe(ddi_acc_handle_t h, uint16_t index,
uint32_t *id_p, uint16_t *base_p);
-extern uint32_t pci_cap_get(ddi_acc_handle_t h, pci_config_size_t size,
+extern uint32_t pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size,
uint32_t id, uint16_t base, uint16_t offset);
-extern int pci_cap_put(ddi_acc_handle_t h, pci_config_size_t size,
+extern int pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
uint32_t id, uint16_t base, uint16_t offset, uint32_t data);
extern int pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
diff --git a/usr/src/uts/sun4/io/px/px.c b/usr/src/uts/sun4/io/px/px.c
index f1d7b21da2..cf52dbe2d7 100644
--- a/usr/src/uts/sun4/io/px/px.c
+++ b/usr/src/uts/sun4/io/px/px.c
@@ -301,15 +301,15 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
if ((px_dma_attach(px_p)) != DDI_SUCCESS)
goto err_bad_dma; /* nothing to uninitialize on DMA */
+ if ((px_fm_attach(px_p)) != DDI_SUCCESS)
+ goto err_bad_dma;
+
/*
* All of the error handlers have been registered
* by now so it's time to activate the interrupt.
*/
if ((ret = px_err_add_intr(&px_p->px_fault)) != DDI_SUCCESS)
- goto err_bad_dma;
-
- if ((px_fm_attach(px_p)) != DDI_SUCCESS)
- goto err_bad_fm;
+ goto err_bad_intr;
(void) px_init_hotplug(px_p);
@@ -353,9 +353,9 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
err_bad_pcitool_node:
ddi_remove_minor_node(dip, "devctl");
err_bad_devctl_node:
- px_fm_detach(px_p);
-err_bad_fm:
px_err_rem_intr(&px_p->px_fault);
+err_bad_intr:
+ px_fm_detach(px_p);
err_bad_dma:
px_pec_detach(px_p);
err_bad_pec:
@@ -456,8 +456,8 @@ px_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
pxtool_uninit(dip);
ddi_remove_minor_node(dip, "devctl");
- px_fm_detach(px_p);
px_err_rem_intr(&px_p->px_fault);
+ px_fm_detach(px_p);
px_pec_detach(px_p);
px_pwr_teardown(dip);
pwr_common_teardown(dip);
diff --git a/usr/src/uts/sun4/io/px/px_pci.c b/usr/src/uts/sun4/io/px/px_pci.c
index 481285e0e0..8b0a4a1cf6 100644
--- a/usr/src/uts/sun4/io/px/px_pci.c
+++ b/usr/src/uts/sun4/io/px/px_pci.c
@@ -47,6 +47,7 @@
#include <sys/pcie_impl.h>
#include <sys/ddi.h>
#include <sys/sunndi.h>
+#include <sys/pci_cap.h>
#include <sys/hotplug/pci/pcihp.h>
#include <sys/hotplug/pci/pciehpc.h>
#include <sys/hotplug/pci/pcishpc.h>
@@ -57,6 +58,9 @@
#include "px_pci.h"
#include "px_debug.h"
+/* 32-bit Shifts */
+#define WDSHIFT 32
+
/* Tunables. Beware: Some are for debug purpose only. */
/*
* PXB MSI tunable:
@@ -172,9 +176,6 @@ static int pxb_pwr_setup(dev_info_t *dip);
static int pxb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p);
static void pxb_pwr_teardown(dev_info_t *dip);
-static int pxb_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id);
-static uint16_t pxb_find_ext_cap_reg(ddi_acc_handle_t config_handle,
- uint16_t cap_id);
static int pxb_bad_func(pxb_devstate_t *pxb, int func);
static int pxb_check_bad_devs(pxb_devstate_t *pxb, int vend);
@@ -182,6 +183,7 @@ static int pxb_check_bad_devs(pxb_devstate_t *pxb, int vend);
static int pxb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle);
static int pxb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle);
static void pxb_init_hotplug(pxb_devstate_t *pxb);
+static void pxb_id_props(pxb_devstate_t *pxb);
static struct dev_ops pxb_ops = {
DEVO_REV, /* devo_rev */
@@ -231,7 +233,6 @@ static void pxb_intr_fini(pxb_devstate_t *pxb);
static uint_t pxb_intr(caddr_t arg1, caddr_t arg2);
static int pxb_intr_attach(pxb_devstate_t *pxb);
-static int pxb_get_port_type(dev_info_t *dip, ddi_acc_handle_t config_handle);
static void pxb_removechild(dev_info_t *);
static int pxb_initchild(dev_info_t *child);
static dev_info_t *get_my_childs_dip(dev_info_t *dip, dev_info_t *rdip);
@@ -305,6 +306,7 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
pxb_devstate_t *pxb;
ddi_acc_handle_t config_handle;
char device_type[8];
+ uint16_t cap_ptr;
instance = ddi_get_instance(devi);
@@ -358,6 +360,9 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
pxb->pxb_device_id = pci_config_get16(config_handle, PCI_CONF_DEVID);
pxb->pxb_rev_id = pci_config_get8(config_handle, PCI_CONF_REVID);
+ /* create special properties for device identification */
+ pxb_id_props(pxb);
+
/*
* Power management setup. This also makes sure that switch/bridge
* is at D0 during attach.
@@ -370,7 +375,12 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
goto fail;
}
- pxb->pxb_port_type = pxb_get_port_type(devi, config_handle);
+ if ((PCI_CAP_LOCATE(pxb->pxb_config_handle, PCI_CAP_ID_PCI_E,
+ &cap_ptr)) != DDI_FAILURE)
+ pxb->pxb_port_type = PCI_CAP_GET16(config_handle, NULL, cap_ptr,
+ PX_CAP_REG) & PX_CAP_REG_DEV_TYPE_MASK;
+ else
+ pxb->pxb_port_type = PX_CAP_REG_DEV_TYPE_PCIE_DEV;
if ((pxb->pxb_port_type != PX_CAP_REG_DEV_TYPE_UP) &&
(pxb->pxb_port_type != PX_CAP_REG_DEV_TYPE_DOWN) &&
@@ -1163,45 +1173,6 @@ pxb_intr(caddr_t arg1, caddr_t arg2)
return (rval);
}
-static int
-pxb_get_port_type(dev_info_t *dip, ddi_acc_handle_t config_handle)
-{
- ushort_t caps_ptr, cap;
- int port_type = PX_CAP_REG_DEV_TYPE_PCIE_DEV;
-
- /*
- * Check if capabilities list is supported. If not then it is a PCI
- * device. If so, check to see if it contains PCI Express Capability
- * Register. Eventually move this code to PCI-EX Framework.
- */
- if (pci_config_get16(config_handle, PCI_CONF_STAT)
- & PCI_STAT_CAP)
- caps_ptr = P2ALIGN(pci_config_get8(config_handle,
- PCI_CONF_CAP_PTR), 4);
- else
- caps_ptr = PCI_CAP_NEXT_PTR_NULL;
-
- while (caps_ptr != PCI_CAP_NEXT_PTR_NULL) {
- if (caps_ptr < 0x40) {
- DBG(DBG_ATTACH, dip,
- "Capability pointer 0x%x out of range.\n",
- caps_ptr);
- break;
- }
-
- cap = pci_config_get8(config_handle, caps_ptr);
- if (cap == PCI_CAP_ID_PCI_E) {
- port_type = pci_config_get16(config_handle,
- caps_ptr + PX_CAP_REG) & PX_CAP_REG_DEV_TYPE_MASK;
- break;
- }
- caps_ptr = P2ALIGN(pci_config_get8(config_handle,
- (caps_ptr + PCI_CAP_NEXT_PTR)), 4);
- }
-
- return (port_type);
-}
-
static void
pxb_removechild(dev_info_t *dip)
{
@@ -1513,8 +1484,7 @@ pxb_pwr_setup(dev_info_t *dip)
char *comp_array[5];
int i, instance;
ddi_acc_handle_t conf_hdl;
- uint8_t cap_ptr, cap_id;
- uint16_t pmcap;
+ uint16_t pmcap, cap_ptr;
pcie_pwr_t *pwr_p;
pxb_devstate_t *pxb;
@@ -1550,21 +1520,12 @@ pxb_pwr_setup(dev_info_t *dip)
return (DDI_FAILURE);
}
conf_hdl = pwr_p->pwr_conf_hdl;
- cap_ptr = pci_config_get8(conf_hdl, PCI_BCNF_CAP_PTR);
+
/*
* Walk the capabilities searching for a PM entry.
*/
- while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
- cap_id = pci_config_get8(conf_hdl,
- cap_ptr + PCI_CAP_ID);
- if (cap_id == PCI_CAP_ID_PM) {
- break;
- }
- cap_ptr = pci_config_get8(conf_hdl,
- cap_ptr + PCI_CAP_NEXT_PTR);
- }
-
- if (cap_ptr == 0) {
+ if ((PCI_CAP_LOCATE(conf_hdl, PCI_CAP_ID_PM, &cap_ptr))
+ == DDI_FAILURE) {
DBG(DBG_PWR, dip, "switch/bridge does not support PM. PCI"
" PM data structure not found in config header\n");
pci_config_teardown(&conf_hdl);
@@ -1574,7 +1535,7 @@ pxb_pwr_setup(dev_info_t *dip)
* Save offset to pmcsr for future references.
*/
pwr_p->pwr_pmcsr_offset = cap_ptr + PCI_PMCSR;
- pmcap = pci_config_get16(conf_hdl, cap_ptr + PCI_PMCAP);
+ pmcap = PCI_CAP_GET16(conf_hdl, NULL, cap_ptr, PCI_PMCAP);
if (pmcap & PCI_PMCAP_D1) {
DBG(DBG_PWR, dip, "D1 state supported\n");
pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D1;
@@ -1750,68 +1711,34 @@ pxb_pwr_teardown(dev_info_t *dip)
/*ARGSUSED*/
static int pxb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
{
- uint16_t status;
- uint8_t cap_ptr, cap_id;
-
- status = pci_config_get16(config_handle, PCI_CONF_STAT);
+ uint16_t cap_ptr;
- if (!(status & PCI_STAT_CAP))
- cap_ptr = PCI_CAP_NEXT_PTR_NULL;
- else {
- cap_ptr = pci_config_get8(config_handle, PCI_CONF_CAP_PTR);
- cap_ptr &= 0xFC;
+ if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_E, &cap_ptr))
+ != DDI_FAILURE) {
+ uint16_t slotimpl = PCI_CAP_GET16(config_handle, NULL, cap_ptr,
+ PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL;
+ if (slotimpl)
+ if (PCI_CAP_GET32(config_handle, NULL, cap_ptr,
+ PCIE_SLOTCAP) & PCIE_SLOTCAP_HP_CAPABLE)
+ return (DDI_SUCCESS);
}
- while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
- cap_id = pci_config_get8(config_handle, cap_ptr);
-
- if (cap_id == PCI_CAP_ID_PCI_E) {
- uint16_t slotimpl;
-
- slotimpl = pci_config_get16(config_handle, cap_ptr +
- PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL;
- if (slotimpl)
- if (pci_config_get32(config_handle, cap_ptr +
- PCIE_SLOTCAP) &
- PCIE_SLOTCAP_HP_CAPABLE)
- break;
- }
-
- cap_ptr = pci_config_get8(config_handle, cap_ptr +
- PCI_CAP_NEXT_PTR);
- cap_ptr &= 0xFC;
- }
+ return (DDI_FAILURE);
- return ((cap_ptr != PCI_CAP_NEXT_PTR_NULL) ? DDI_SUCCESS : DDI_FAILURE);
}
/*ARGSUSED*/
static int pxb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
{
- uint16_t status;
- uint8_t cap_ptr, cap_id;
-
- status = pci_config_get16(config_handle, PCI_CONF_STAT);
+ uint16_t cap_ptr;
- if (!(status & PCI_STAT_CAP))
- cap_ptr = PCI_CAP_NEXT_PTR_NULL;
- else {
- cap_ptr = pci_config_get8(config_handle, PCI_CONF_CAP_PTR);
- cap_ptr &= 0xFC;
+ if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_HOTPLUG, &cap_ptr))
+ != DDI_FAILURE) {
+ return (DDI_SUCCESS);
}
- while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
- cap_id = pci_config_get8(config_handle, cap_ptr);
-
- if (cap_id == PCI_CAP_ID_PCI_HOTPLUG)
- break;
-
- cap_ptr = pci_config_get8(config_handle, cap_ptr +
- PCI_CAP_NEXT_PTR);
- cap_ptr &= 0xFC;
- }
+ return (DDI_FAILURE);
- return ((cap_ptr != PCI_CAP_NEXT_PTR_NULL) ? DDI_SUCCESS : DDI_FAILURE);
}
/* check if this device has PCIe link underneath. */
@@ -1879,71 +1806,6 @@ pxb_print_plx_seeprom_crc_data(pxb_devstate_t *pxb_p)
}
#endif
-/*
- * given a cap_id, return its cap_id location in config space
- */
-static int
-pxb_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id)
-{
- uint8_t curcap;
- uint_t cap_id_loc;
- uint16_t status;
- int location = -1;
-
- /*
- * Need to check the Status register for ECP support first.
- * Also please note that for type 1 devices, the
- * offset could change. Should support type 1 next.
- */
- status = pci_config_get16(config_handle, PCI_CONF_STAT);
- if (!(status & PCI_STAT_CAP)) {
- return (-1);
- }
- cap_id_loc = pci_config_get8(config_handle, PCI_CONF_CAP_PTR);
-
- /* Walk the list of capabilities */
- while (cap_id_loc) {
-
- curcap = pci_config_get8(config_handle, cap_id_loc);
-
- if (curcap == cap_id) {
- location = cap_id_loc;
- break;
- }
- cap_id_loc = pci_config_get8(config_handle,
- cap_id_loc + 1);
- }
- return (location);
-}
-
-static uint16_t
-pxb_find_ext_cap_reg(ddi_acc_handle_t config_handle, uint16_t cap_id)
-{
- uint32_t hdr, hdr_next_ptr, hdr_cap_id;
- uint16_t offset = P2ALIGN(PCIE_EXT_CAP, 4);
-
- hdr = pci_config_get32(config_handle, offset);
- hdr_next_ptr = (hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) &
- PCIE_EXT_CAP_NEXT_PTR_MASK;
- hdr_cap_id = (hdr >> PCIE_EXT_CAP_ID_SHIFT) &
- PCIE_EXT_CAP_ID_MASK;
-
- while ((hdr_next_ptr != PCIE_EXT_CAP_NEXT_PTR_NULL) &&
- (hdr_cap_id != cap_id)) {
- offset = P2ALIGN(hdr_next_ptr, 4);
- hdr = pci_config_get32(config_handle, offset);
- hdr_next_ptr = (hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) &
- PCIE_EXT_CAP_NEXT_PTR_MASK;
- hdr_cap_id = (hdr >> PCIE_EXT_CAP_ID_SHIFT) &
- PCIE_EXT_CAP_ID_MASK;
- }
-
- if (hdr_cap_id == cap_id)
- return (P2ALIGN(offset, 4));
-
- return (PCIE_EXT_CAP_NEXT_PTR_NULL);
-}
-
/* check for known bad functionality in devices */
static int
pxb_bad_func(pxb_devstate_t *pxb, int func)
@@ -2021,6 +1883,46 @@ pxb_check_bad_devs(pxb_devstate_t *pxb, int vend)
return (ret);
}
+static void
+pxb_id_props(pxb_devstate_t *pxb)
+{
+ uint64_t serialid = 0; /* 40b field of EUI-64 serial no. register */
+ uint16_t cap_ptr;
+ uint8_t fic = 0; /* 1 = first in chassis device */
+
+ if ((pxb->pxb_vendor_id == PXB_VENDOR_SUN) &&
+ ((pxb->pxb_device_id == PXB_DEVICE_PLX_PCIX) ||
+ (pxb->pxb_device_id == PXB_DEVICE_PLX_PCIE)))
+ fic = 1;
+ else {
+ /* look for Expansion Slot Reg and first-in-chassis bit */
+ if ((PCI_CAP_LOCATE(pxb->pxb_config_handle, PCI_CAP_ID_SLOT_ID,
+ &cap_ptr)) != DDI_FAILURE) {
+ uint8_t esr = PCI_CAP_GET8(pxb->pxb_config_handle, NULL,
+ cap_ptr, PCI_CAP_ID_REGS_OFF);
+ if (PCI_CAPSLOT_FIC(esr))
+ fic = 1;
+ }
+ }
+
+ if ((PCI_CAP_LOCATE(pxb->pxb_config_handle, PCIE_EXT_CAP_ID_SER,
+ &cap_ptr)) != DDI_FAILURE) {
+ /* serialid can be 0 thru a full 40b number */
+ serialid = PCI_XCAP_GET32(pxb->pxb_config_handle, NULL, cap_ptr,
+ 4);
+ serialid <<= WDSHIFT;
+ serialid |= PCI_XCAP_GET32(pxb->pxb_config_handle, NULL,
+ cap_ptr, 8);
+ }
+
+ if (fic)
+ (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pxb->pxb_dip,
+ "first-in-chassis");
+ if (serialid)
+ (void) ddi_prop_update_int64(DDI_DEV_T_NONE, pxb->pxb_dip,
+ "serialid#", serialid);
+}
+
#ifdef BCM_SW_WORKAROUNDS
/*
diff --git a/usr/src/uts/sun4u/io/px/px_hlib.c b/usr/src/uts/sun4u/io/px/px_hlib.c
index 6ee60b34aa..cec5a814d6 100644
--- a/usr/src/uts/sun4u/io/px/px_hlib.c
+++ b/usr/src/uts/sun4u/io/px/px_hlib.c
@@ -39,6 +39,7 @@
#include "oberon_regs.h"
#include "px_csr.h"
#include "px_lib4u.h"
+#include "px_err.h"
/*
* Registers that need to be saved and restored during suspend/resume.
@@ -163,6 +164,11 @@ static uint64_t msiq_config_other_regs[] = {
#define MSIQ_STATE_SIZE (EVENT_QUEUE_STATE_ENTRIES * sizeof (uint64_t))
#define MSIQ_MAPPING_SIZE (MSI_MAPPING_ENTRIES * sizeof (uint64_t))
+/* OPL tuning variables for link unstable issue */
+int wait_perst = 500000; /* step 9, default: 500ms */
+int wait_enable_port = 45000; /* step 11, default: 45ms */
+int link_retry_count = 2; /* step 11, default: 2 */
+
static uint64_t msiq_suspend(devhandle_t dev_hdl, pxu_t *pxu_p);
static void msiq_resume(devhandle_t dev_hdl, pxu_t *pxu_p);
static void jbc_init(caddr_t xbc_csr_base, pxu_t *pxu_p);
@@ -2990,8 +2996,8 @@ static uint_t
oberon_hp_pwron(caddr_t csr_base)
{
volatile uint64_t reg;
- boolean_t link_retrain, link_up;
- int i;
+ boolean_t link_retry, link_up;
+ int loop, i;
DBG(DBG_HP, NULL, "oberon_hp_pwron the slot\n");
@@ -3039,51 +3045,53 @@ oberon_hp_pwron(caddr_t csr_base)
/* Turn on slot clock */
CSR_BS(csr_base, HOTPLUG_CONTROL, CLKEN);
- /* Release PCI-E Reset */
- delay(drv_usectohz(100000));
- CSR_BS(csr_base, HOTPLUG_CONTROL, N_PERST);
+ link_up = B_FALSE;
+ link_retry = B_FALSE;
+
+ for (loop = 0; (loop < link_retry_count) && (link_up == B_FALSE);
+ loop++) {
+ if (link_retry == B_TRUE) {
+ DBG(DBG_HP, NULL, "oberon_hp_pwron : retry link loop "
+ "%d\n", loop);
+ CSR_BS(csr_base, TLU_CONTROL, DRN_TR_DIS);
+ CSR_XS(csr_base, FLP_PORT_CONTROL, 0x1);
+ delay(drv_usectohz(10000));
+ CSR_BC(csr_base, TLU_CONTROL, DRN_TR_DIS);
+ CSR_BS(csr_base, TLU_DIAGNOSTIC, IFC_DIS);
+ CSR_BC(csr_base, HOTPLUG_CONTROL, N_PERST);
+ delay(drv_usectohz(50000));
+ }
- /*
- * Open events' mask
- * This should be done from pciehpc already
- */
+ /* Release PCI-E Reset */
+ delay(drv_usectohz(wait_perst));
+ CSR_BS(csr_base, HOTPLUG_CONTROL, N_PERST);
- /*
- * Initialize Leaf
- * SPLS = 00b, SPLV = 11001b, i.e. 25W
- */
- reg = CSR_XR(csr_base, TLU_SLOT_CAPABILITIES);
- reg &= ~(TLU_SLOT_CAPABILITIES_SPLS_MASK <<
- TLU_SLOT_CAPABILITIES_SPLS);
- reg &= ~(TLU_SLOT_CAPABILITIES_SPLV_MASK <<
- TLU_SLOT_CAPABILITIES_SPLS);
- reg |= (0x19 << TLU_SLOT_CAPABILITIES_SPLS);
- CSR_XS(csr_base, TLU_SLOT_CAPABILITIES, reg);
+ /*
+ * Open events' mask
+ * This should be done from pciehpc already
+ */
- /* Enable PCIE port */
- CSR_BS(csr_base, TLU_CONTROL, DRN_TR_DIS);
- CSR_BC(csr_base, FLP_PORT_CONTROL, PORT_DIS);
+ /* Enable PCIE port */
+ delay(drv_usectohz(wait_enable_port));
+ CSR_BS(csr_base, TLU_CONTROL, DRN_TR_DIS);
+ CSR_XS(csr_base, FLP_PORT_CONTROL, 0x20);
- /* wait for the link up */
- link_up = B_FALSE;
- link_retrain = B_TRUE;
- for (i = 0; (i < 2) && (link_up == B_FALSE); i++) {
- delay(drv_usectohz(100000));
- reg = CSR_XR(csr_base, DLU_LINK_LAYER_STATUS);
+ /* wait for the link up */
+ for (i = 0; (i < 2) && (link_up == B_FALSE); i++) {
+ delay(drv_usectohz(100000));
+ reg = CSR_XR(csr_base, DLU_LINK_LAYER_STATUS);
- if ((((reg >> DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS) &
+ if ((((reg >> DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS) &
DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS_MASK) ==
DLU_LINK_LAYER_STATUS_INIT_FC_SM_STS_FC_INIT_DONE) &&
- (reg & (1ull << DLU_LINK_LAYER_STATUS_DLUP_STS)) &&
- ((reg & DLU_LINK_LAYER_STATUS_LNK_STATE_MACH_STS_MASK) ==
+ (reg & (1ull << DLU_LINK_LAYER_STATUS_DLUP_STS)) &&
+ ((reg & DLU_LINK_LAYER_STATUS_LNK_STATE_MACH_STS_MASK)
+ ==
DLU_LINK_LAYER_STATUS_LNK_STATE_MACH_STS_DL_ACTIVE)) {
DBG(DBG_HP, NULL, "oberon_hp_pwron : link is up\n");
- link_up = B_TRUE;
- } else if (link_retrain == B_TRUE) {
- DBG(DBG_HP, NULL, "oberon_hp_pwron: retrain link\n");
- /* retrain the link */
- CSR_BS(csr_base, FLP_PORT_LINK_CONTROL, RETRAIN);
- link_retrain = B_FALSE;
+ link_up = B_TRUE;
+ } else
+ link_retry = B_TRUE;
}
}
@@ -3094,11 +3102,36 @@ oberon_hp_pwron(caddr_t csr_base)
}
/* link is up */
+ CSR_BC(csr_base, TLU_DIAGNOSTIC, IFC_DIS);
CSR_BS(csr_base, FLP_PORT_ACTIVE_STATUS, TRAIN_ERROR);
CSR_BS(csr_base, TLU_UNCORRECTABLE_ERROR_STATUS_CLEAR, TE_P);
CSR_BS(csr_base, TLU_UNCORRECTABLE_ERROR_STATUS_CLEAR, TE_S);
CSR_BC(csr_base, TLU_CONTROL, DRN_TR_DIS);
+ /* Restore LUP/LDN */
+ reg = CSR_XR(csr_base, TLU_OTHER_EVENT_LOG_ENABLE);
+ if (px_tlu_oe_log_mask & (1ull << TLU_OTHER_EVENT_STATUS_SET_LUP_P))
+ reg |= 1ull << TLU_OTHER_EVENT_STATUS_SET_LUP_P;
+ if (px_tlu_oe_log_mask & (1ull << TLU_OTHER_EVENT_STATUS_SET_LDN_P))
+ reg |= 1ull << TLU_OTHER_EVENT_STATUS_SET_LDN_P;
+ if (px_tlu_oe_log_mask & (1ull << TLU_OTHER_EVENT_STATUS_SET_LUP_S))
+ reg |= 1ull << TLU_OTHER_EVENT_STATUS_SET_LUP_S;
+ if (px_tlu_oe_log_mask & (1ull << TLU_OTHER_EVENT_STATUS_SET_LDN_S))
+ reg |= 1ull << TLU_OTHER_EVENT_STATUS_SET_LDN_S;
+ CSR_XS(csr_base, TLU_OTHER_EVENT_LOG_ENABLE, reg);
+
+ /*
+ * Initialize Leaf
+ * SPLS = 00b, SPLV = 11001b, i.e. 25W
+ */
+ reg = CSR_XR(csr_base, TLU_SLOT_CAPABILITIES);
+ reg &= ~(TLU_SLOT_CAPABILITIES_SPLS_MASK <<
+ TLU_SLOT_CAPABILITIES_SPLS);
+ reg &= ~(TLU_SLOT_CAPABILITIES_SPLV_MASK <<
+ TLU_SLOT_CAPABILITIES_SPLS);
+ reg |= (0x19 << TLU_SLOT_CAPABILITIES_SPLS);
+ CSR_XS(csr_base, TLU_SLOT_CAPABILITIES, reg);
+
/* Turn on Power LED */
reg = CSR_XR(csr_base, TLU_SLOT_CONTROL);
reg &= ~PCIE_SLOTCTL_PWR_INDICATOR_MASK;
@@ -3158,6 +3191,14 @@ oberon_hp_pwroff(caddr_t csr_base)
CSR_BS(csr_base, TLU_CONTROL, DRN_TR_DIS);
delay(drv_usectohz(10000));
+ /* Disable LUP/LDN */
+ reg = CSR_XR(csr_base, TLU_OTHER_EVENT_LOG_ENABLE);
+ reg &= ~((1ull << TLU_OTHER_EVENT_STATUS_SET_LDN_P) |
+ (1ull << TLU_OTHER_EVENT_STATUS_SET_LUP_P) |
+ (1ull << TLU_OTHER_EVENT_STATUS_SET_LDN_S) |
+ (1ull << TLU_OTHER_EVENT_STATUS_SET_LUP_S));
+ CSR_XS(csr_base, TLU_OTHER_EVENT_LOG_ENABLE, reg);
+
/* Save the TLU registers */
reg_tluue = CSR_XR(csr_base, TLU_UNCORRECTABLE_ERROR_LOG_ENABLE);
reg_tluce = CSR_XR(csr_base, TLU_CORRECTABLE_ERROR_LOG_ENABLE);
@@ -3310,6 +3351,7 @@ hvio_hotplug_init(dev_info_t *dip, void *arg)
pciehpc_regops_t *regops = (pciehpc_regops_t *)arg;
px_t *px_p = DIP_TO_STATE(dip);
pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
+ volatile uint64_t reg;
if (PX_CHIP_TYPE(pxu_p) == PX_CHIP_OBERON) {
if (!CSR_BR((caddr_t)pxu_p->px_address[PX_REG_CSR],
@@ -3319,6 +3361,22 @@ hvio_hotplug_init(dev_info_t *dip, void *arg)
return (DDI_FAILURE);
}
+ /* For empty or disconnected slot, disable LUP/LDN */
+ if (!CSR_BR((caddr_t)pxu_p->px_address[PX_REG_CSR],
+ TLU_SLOT_STATUS, PSD) ||
+ !CSR_BR((caddr_t)pxu_p->px_address[PX_REG_CSR],
+ HOTPLUG_CONTROL, PWREN)) {
+
+ reg = CSR_XR((caddr_t)pxu_p->px_address[PX_REG_CSR],
+ TLU_OTHER_EVENT_LOG_ENABLE);
+ reg &= ~((1ull << TLU_OTHER_EVENT_STATUS_SET_LDN_P) |
+ (1ull << TLU_OTHER_EVENT_STATUS_SET_LUP_P) |
+ (1ull << TLU_OTHER_EVENT_STATUS_SET_LDN_S) |
+ (1ull << TLU_OTHER_EVENT_STATUS_SET_LUP_S));
+ CSR_XS((caddr_t)pxu_p->px_address[PX_REG_CSR],
+ TLU_OTHER_EVENT_LOG_ENABLE, reg);
+ }
+
regops->get = oberon_hpreg_get;
regops->put = oberon_hpreg_put;