diff options
author | pjha <none@none> | 2006-08-21 20:11:18 -0700 |
---|---|---|
committer | pjha <none@none> | 2006-08-21 20:11:18 -0700 |
commit | 3c4226f98775d47a05fa88f9f72479f1a250eaa5 (patch) | |
tree | 2268348f795a468ab405cce9cd8121f280ba5a7b | |
parent | 3247dfed200428da80007948dc664da0d2b81bac (diff) | |
download | illumos-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.c | 898 | ||||
-rw-r--r-- | usr/src/cmd/devfsadm/cfg_link.h | 100 | ||||
-rw-r--r-- | usr/src/cmd/devfsadm/devfsadm.c | 54 | ||||
-rw-r--r-- | usr/src/cmd/devfsadm/devfsadm.h | 10 | ||||
-rw-r--r-- | usr/src/cmd/devfsadm/devfsadm_impl.h | 9 | ||||
-rw-r--r-- | usr/src/lib/cfgadm_plugins/pci/common/cfga.c | 77 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/Makefile.com | 4 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_dli.c | 158 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/libdevinfo.h | 17 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/mapfile-vers | 4 | ||||
-rw-r--r-- | usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c | 9 | ||||
-rw-r--r-- | usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c | 3 | ||||
-rw-r--r-- | usr/src/uts/common/io/pci_cap.c | 4 | ||||
-rw-r--r-- | usr/src/uts/common/sys/hotplug/pci/pcihp.h | 9 | ||||
-rw-r--r-- | usr/src/uts/common/sys/pci_cap.h | 6 | ||||
-rw-r--r-- | usr/src/uts/sun4/io/px/px.c | 14 | ||||
-rw-r--r-- | usr/src/uts/sun4/io/px/px_pci.c | 248 | ||||
-rw-r--r-- | usr/src/uts/sun4u/io/px/px_hlib.c | 134 |
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, + ®p); + + 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; |