xref: /titanic_50/usr/src/cmd/devfsadm/cfg_link.c (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
1  /*
2   * CDDL HEADER START
3   *
4   * The contents of this file are subject to the terms of the
5   * Common Development and Distribution License (the "License").
6   * You may not use this file except in compliance with the License.
7   *
8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9   * or http://www.opensolaris.org/os/licensing.
10   * See the License for the specific language governing permissions
11   * and limitations under the License.
12   *
13   * When distributing Covered Code, include this CDDL HEADER in each
14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15   * If applicable, add the following below this CDDL HEADER, with the
16   * fields enclosed by brackets "[]" replaced with your own identifying
17   * information: Portions Copyright [yyyy] [name of copyright owner]
18   *
19   * CDDL HEADER END
20   */
21  
22  /*
23   * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24   * Use is subject to license terms.
25   */
26  
27  #pragma ident	"%Z%%M%	%I%	%E% SMI"
28  
29  #include <devfsadm.h>
30  #include <stdio.h>
31  #include <strings.h>
32  #include <stdlib.h>
33  #include <stdarg.h>
34  #include <limits.h>
35  #include <unistd.h>
36  #include <config_admin.h>
37  #include <cfg_link.h>
38  #include <sys/types.h>
39  #include <sys/mkdev.h>
40  #include <sys/hotplug/pci/pcihp.h>
41  
42  #ifdef	DEBUG
43  #define	dprint(args)	devfsadm_errprint args
44  /*
45   * for use in print routine arg list as a shorthand way to locate node via
46   * "prtconf -D" to avoid messy and cluttered debugging code
47   * don't forget the corresponding "%s%d" format
48   */
49  #define	DRVINST(node)	di_driver_name(node), di_instance(node)
50  #else
51  #define	dprint(args)
52  #endif
53  
54  
55  static int	scsi_cfg_creat_cb(di_minor_t minor, di_node_t node);
56  static int	sbd_cfg_creat_cb(di_minor_t minor, di_node_t node);
57  static int	usb_cfg_creat_cb(di_minor_t minor, di_node_t node);
58  static char	*get_roothub(const char *path, void *cb_arg);
59  static int	pci_cfg_creat_cb(di_minor_t minor, di_node_t node);
60  static int	ib_cfg_creat_cb(di_minor_t minor, di_node_t node);
61  static int	sata_cfg_creat_cb(di_minor_t minor, di_node_t node);
62  
63  static di_node_t	pci_cfg_chassis_node(di_node_t, di_prom_handle_t);
64  static char 	*pci_cfg_slotname(di_node_t, di_prom_handle_t, minor_t);
65  static int	pci_cfg_ap_node(minor_t, di_node_t, di_prom_handle_t,
66  		    char *, int, int);
67  static int	pci_cfg_iob_name(di_minor_t, di_node_t, di_prom_handle_t,
68  		    char *, int);
69  static minor_t	pci_cfg_pcidev(di_node_t, di_prom_handle_t);
70  static int	pci_cfg_ap_path(di_minor_t, di_node_t, di_prom_handle_t,
71  		    char *, int, char **);
72  static char 	*pci_cfg_info_data(char *);
73  static int	pci_cfg_is_ap_path(di_node_t, di_prom_handle_t);
74  static int	pci_cfg_ap_legacy(di_minor_t, di_node_t, di_prom_handle_t,
75  		    char *, int);
76  static void	pci_cfg_rm_invalid_links(char *, char *);
77  static void	pci_cfg_rm_link(char *);
78  static void	pci_cfg_rm_all(char *);
79  static char	*pci_cfg_devpath(di_node_t, di_minor_t);
80  static di_node_t	pci_cfg_snapshot(di_node_t, di_minor_t,
81  			    di_node_t *, di_minor_t *);
82  
83  /* flag definitions for di_propall_*(); value "0" is always the default flag */
84  #define	DIPROP_PRI_NODE		0x0
85  #define	DIPROP_PRI_PROM		0x1
86  static int	di_propall_lookup_ints(di_prom_handle_t, int,
87  		    dev_t, di_node_t, const char *, int **);
88  static int	di_propall_lookup_strings(di_prom_handle_t, int,
89  		    dev_t, di_node_t, const char *, char **);
90  
91  
92  /*
93   * NOTE: The CREATE_DEFER flag is private to this module.
94   *	 NOT to be used by other modules
95   */
96  static devfsadm_create_t cfg_create_cbt[] = {
97  	{ "attachment-point", DDI_NT_SCSI_ATTACHMENT_POINT, NULL,
98  	    TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
99  	},
100  	{ "attachment-point", DDI_NT_SBD_ATTACHMENT_POINT, NULL,
101  	    TYPE_EXACT, ILEVEL_0, sbd_cfg_creat_cb
102  	},
103  	{ "fc-attachment-point", DDI_NT_FC_ATTACHMENT_POINT, NULL,
104  	    TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
105  	},
106  	{ "attachment-point", DDI_NT_USB_ATTACHMENT_POINT, NULL,
107  	    TYPE_EXACT, ILEVEL_0, usb_cfg_creat_cb
108  	},
109  	{ "attachment-point", DDI_NT_PCI_ATTACHMENT_POINT, NULL,
110  	    TYPE_EXACT, ILEVEL_0, pci_cfg_creat_cb
111  	},
112  	{ "attachment-point", DDI_NT_IB_ATTACHMENT_POINT, NULL,
113  	    TYPE_EXACT, ILEVEL_0, ib_cfg_creat_cb
114  	},
115  	{ "attachment-point", DDI_NT_SATA_ATTACHMENT_POINT, NULL,
116  	    TYPE_EXACT, ILEVEL_0, sata_cfg_creat_cb
117  	}
118  };
119  
120  DEVFSADM_CREATE_INIT_V0(cfg_create_cbt);
121  
122  static devfsadm_remove_t cfg_remove_cbt[] = {
123  	{ "attachment-point", SCSI_CFG_LINK_RE, RM_POST,
124  	    ILEVEL_0, devfsadm_rm_all
125  	},
126  	{ "attachment-point", SBD_CFG_LINK_RE, RM_POST,
127  	    ILEVEL_0, devfsadm_rm_all
128  	},
129  	{ "fc-attachment-point", SCSI_CFG_LINK_RE, RM_POST,
130  	    ILEVEL_0, devfsadm_rm_all
131  	},
132  	{ "attachment-point", USB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
133  	    ILEVEL_0, devfsadm_rm_all
134  	},
135  	{ "attachment-point", PCI_CFG_LINK_RE, RM_POST,
136  	    ILEVEL_0, devfsadm_rm_all
137  	},
138  	{ "attachment-point", PCI_CFG_PATH_LINK_RE, RM_POST|RM_HOT,
139  	    ILEVEL_0, pci_cfg_rm_all
140  	},
141  	{ "attachment-point", IB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
142  	    ILEVEL_0, devfsadm_rm_all
143  	},
144  	{ "attachment-point", SATA_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
145  	    ILEVEL_0, devfsadm_rm_all
146  	}
147  };
148  
149  DEVFSADM_REMOVE_INIT_V0(cfg_remove_cbt);
150  
151  static int
152  scsi_cfg_creat_cb(di_minor_t minor, di_node_t node)
153  {
154  	char path[PATH_MAX + 1];
155  	char *c_num = NULL, *devfs_path, *mn;
156  	devfsadm_enumerate_t rules[3] = {
157  	    {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT},
158  	    {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR},
159  	    {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT}
160  	};
161  
162  	mn = di_minor_name(minor);
163  
164  	if ((devfs_path = di_devfs_path(node)) == NULL) {
165  		return (DEVFSADM_CONTINUE);
166  	}
167  	(void) strcpy(path, devfs_path);
168  	(void) strcat(path, ":");
169  	(void) strcat(path, mn);
170  	di_devfs_path_free(devfs_path);
171  
172  	if (devfsadm_enumerate_int(path, 1, &c_num, rules, 3)
173  	    == DEVFSADM_FAILURE) {
174  		/*
175  		 * Unlike the disks module we don't retry on failure.
176  		 * If we have multiple "c" numbers for a single physical
177  		 * controller due to bug 4045879, we will not assign a
178  		 * c-number/symlink for the controller.
179  		 */
180  		return (DEVFSADM_CONTINUE);
181  	}
182  
183  	(void) strcpy(path, CFG_DIRNAME);
184  	(void) strcat(path, "/c");
185  	(void) strcat(path, c_num);
186  
187  	free(c_num);
188  
189  	(void) devfsadm_mklink(path, node, minor, 0);
190  
191  	return (DEVFSADM_CONTINUE);
192  }
193  
194  static int
195  sbd_cfg_creat_cb(di_minor_t minor, di_node_t node)
196  {
197  	char path[PATH_MAX + 1];
198  
199  	(void) strcpy(path, CFG_DIRNAME);
200  	(void) strcat(path, "/");
201  	(void) strcat(path, di_minor_name(minor));
202  	(void) devfsadm_mklink(path, node, minor, 0);
203  	return (DEVFSADM_CONTINUE);
204  }
205  
206  
207  static int
208  usb_cfg_creat_cb(di_minor_t minor, di_node_t node)
209  {
210  	char *cp, path[PATH_MAX + 1];
211  	devfsadm_enumerate_t rules[1] =
212  		{"^cfg$/^usb([0-9]+)$", 1, MATCH_CALLBACK, NULL, get_roothub};
213  
214  	if ((cp = di_devfs_path(node)) == NULL) {
215  		return (DEVFSADM_CONTINUE);
216  	}
217  
218  	(void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
219  	di_devfs_path_free(cp);
220  
221  	if (devfsadm_enumerate_int(path, 0, &cp, rules, 1)) {
222  		return (DEVFSADM_CONTINUE);
223  	}
224  
225  	/* create usbN and the symlink */
226  	(void) snprintf(path, sizeof (path), "%s/usb%s/%s", CFG_DIRNAME, cp,
227  	    di_minor_name(minor));
228  	free(cp);
229  
230  	(void) devfsadm_mklink(path, node, minor, 0);
231  
232  	return (DEVFSADM_CONTINUE);
233  }
234  
235  
236  static int
237  sata_cfg_creat_cb(di_minor_t minor, di_node_t node)
238  {
239  	char path[PATH_MAX + 1], l_path[PATH_MAX], *buf, *devfspath;
240  	char *minor_nm;
241  	devfsadm_enumerate_t rules[1] =
242  		{"^cfg$/^sata([0-9]+)$", 1, MATCH_ADDR};
243  
244  	minor_nm = di_minor_name(minor);
245  	if (minor_nm == NULL)
246  		return (DEVFSADM_CONTINUE);
247  
248  	devfspath = di_devfs_path(node);
249  	if (devfspath == NULL)
250  		return (DEVFSADM_CONTINUE);
251  
252  	(void) strlcpy(path, devfspath, sizeof (path));
253  	(void) strlcat(path, ":", sizeof (path));
254  	(void) strlcat(path, minor_nm, sizeof (path));
255  	di_devfs_path_free(devfspath);
256  
257  	/* build the physical path from the components */
258  	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1) ==
259  	    DEVFSADM_FAILURE) {
260  		return (DEVFSADM_CONTINUE);
261  	}
262  
263  	(void) snprintf(l_path, sizeof (l_path), "%s/sata%s/%s", CFG_DIRNAME,
264  			buf, minor_nm);
265  	free(buf);
266  
267  	(void) devfsadm_mklink(l_path, node, minor, 0);
268  
269  	return (DEVFSADM_CONTINUE);
270  }
271  
272  
273  /*
274   * get_roothub:
275   *	figure out the root hub path to calculate /dev/cfg/usbN
276   */
277  /* ARGSUSED */
278  static char *
279  get_roothub(const char *path, void *cb_arg)
280  {
281  	int  i, count = 0;
282  	char *physpath, *cp;
283  
284  	/* make a copy */
285  	if ((physpath = strdup(path)) == NULL) {
286  		return (NULL);
287  	}
288  
289  	/*
290  	 * physpath must always have a minor name component
291  	 */
292  	if ((cp = strrchr(physpath, ':')) == NULL) {
293  		free(physpath);
294  		return (NULL);
295  	}
296  	*cp++ = '\0';
297  
298  	/*
299  	 * No '.' in the minor name indicates a roothub port.
300  	 */
301  	if (strchr(cp, '.') == NULL) {
302  		/* roothub device */
303  		return (physpath);
304  	}
305  
306  	while (*cp) {
307  		if (*cp == '.')
308  			count++;
309  		cp++;
310  	}
311  
312  	/* Remove as many trailing path components as there are '.'s */
313  	for (i = 0; i < count; i++) {
314  		if ((cp = strrchr(physpath, '/')) == NULL || (cp == physpath)) {
315  			free(physpath);
316  			return (NULL);
317  		}
318  		*cp = '\0';
319  	}
320  
321  	return (physpath);
322  }
323  
324  
325  /*
326   * returns an allocted string containing the device path for <node> and
327   * <minor>
328   */
329  static char *
330  pci_cfg_devpath(di_node_t node, di_minor_t minor)
331  {
332  	char *path;
333  	char *bufp;
334  	char *minor_nm;
335  	int buflen;
336  
337  	path = di_devfs_path(node);
338  	minor_nm = di_minor_name(minor);
339  	buflen = snprintf(NULL, 0, "%s:%s", path, minor_nm) + 1;
340  
341  	bufp = malloc(sizeof (char) * buflen);
342  	if (bufp != NULL)
343  		(void) snprintf(bufp, buflen, "%s:%s", path, minor_nm);
344  
345  	di_devfs_path_free(path);
346  	return (bufp);
347  }
348  
349  
350  static int
351  di_propall_lookup_ints(di_prom_handle_t ph, int flags,
352      dev_t dev, di_node_t node, const char *prop_name, int **prop_data)
353  {
354  	int rv;
355  
356  	if (flags & DIPROP_PRI_PROM) {
357  		rv = di_prom_prop_lookup_ints(ph, node, prop_name, prop_data);
358  		if (rv < 0)
359  			rv = di_prop_lookup_ints(dev, node, prop_name,
360  			    prop_data);
361  	} else {
362  		rv = di_prop_lookup_ints(dev, node, prop_name, prop_data);
363  		if (rv < 0)
364  			rv = di_prom_prop_lookup_ints(ph, node, prop_name,
365  			    prop_data);
366  	}
367  	return (rv);
368  }
369  
370  
371  static int
372  di_propall_lookup_strings(di_prom_handle_t ph, int flags,
373      dev_t dev, di_node_t node, const char *prop_name, char **prop_data)
374  {
375  	int rv;
376  
377  	if (flags & DIPROP_PRI_PROM) {
378  		rv = di_prom_prop_lookup_strings(ph, node, prop_name,
379  		    prop_data);
380  		if (rv < 0)
381  			rv = di_prop_lookup_strings(dev, node, prop_name,
382  			    prop_data);
383  	} else {
384  		rv = di_prop_lookup_strings(dev, node, prop_name, prop_data);
385  		if (rv < 0)
386  			rv = di_prom_prop_lookup_strings(ph, node, prop_name,
387  			    prop_data);
388  	}
389  	return (rv);
390  }
391  
392  
393  static di_node_t
394  pci_cfg_chassis_node(di_node_t node, di_prom_handle_t ph)
395  {
396  	di_node_t curnode = node;
397  	int *firstchas;
398  
399  	do {
400  		if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, curnode,
401  		    PROP_FIRST_CHAS, &firstchas) >= 0)
402  			return (curnode);
403  	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
404  
405  	return (DI_NODE_NIL);
406  }
407  
408  
409  /*
410   * yet another redundant common routine to:
411   * decode the ieee1275 "slot-names" property and returns the string matching
412   * the pci device number <pci_dev>, if any.
413   *
414   * callers must NOT free the returned string
415   *
416   * "slot-names" format: [int][string1][string2]...[stringN]
417   *	- each bit position in [int] represent a pci device number
418   *	- [string1]...[stringN] are concatenated null-terminated strings
419   *	- the number of bits set in [int] == the number of strings that follow
420   *	- each bit that is set corresponds to a string in the following segment
421   */
422  static char *
423  pci_cfg_slotname(di_node_t node, di_prom_handle_t ph, minor_t pci_dev)
424  {
425  #ifdef	DEBUG
426  	char *fnm = "pci_cfg_slotname";
427  #endif
428  	int *snp;
429  	int snlen;
430  	int snentlen = sizeof (int);
431  	int i, max, len, place, curplace;
432  	char *str;
433  
434  	snlen = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node,
435  	    PROP_SLOT_NAMES, &snp);
436  	if (snlen < 1)
437  		return (NULL);
438  	if ((snp[0] & (1 << pci_dev)) == 0)
439  		return (NULL);
440  
441  	/*
442  	 * pci device number must be less than the amount of bits in the first
443  	 * [int] component of slot-names
444  	 */
445  	if (pci_dev >= snentlen * 8) {
446  		dprint(("%s: pci_dev out of range for %s%d\n",
447  		    fnm, DRVINST(node)));
448  		return (NULL);
449  	}
450  
451  	place = 0;
452  	for (i = 0; i < pci_dev; i++) {
453  		if (snp[0] & (1 << i))
454  			place++;
455  	}
456  
457  	max = (snlen * snentlen) - snentlen;
458  	str = (char *)&snp[1];
459  	i = 0;
460  	curplace = 0;
461  	while (i < max && curplace < place) {
462  		len = strlen(str);
463  		if (len <= 0)
464  			break;
465  		str += len + 1;
466  		i += len + 1;
467  		curplace++;
468  	}
469  	/* the following condition indicates a badly formed slot-names */
470  	if (i >= max || *str == '\0') {
471  		dprint(("%s: badly formed slot-names for %s%d\n",
472  		    fnm, DRVINST(node)));
473  		str = NULL;
474  	}
475  	return (str);
476  }
477  
478  
479  /*
480   * returns non-zero if we can return a valid attachment point name for <node>,
481   * for its slot identified by child pci device number <pci_dev>, through <buf>
482   *
483   * prioritized naming scheme:
484   *	1) <PROP_SLOT_NAMES property>    (see pci_cfg_slotname())
485   *	2) <device-type><PROP_PHYS_SLOT property>
486   *	3) <drv name><drv inst>.<device-type><pci_dev>
487   *
488   * where <device-type> is derived from the PROP_DEV_TYPE property:
489   *	if its value is "pciex" then <device-type> is "pcie"
490   *	else the raw value is used
491   *
492   * if <flags> contains APNODE_DEFNAME, then scheme (3) is used
493   */
494  static int
495  pci_cfg_ap_node(minor_t pci_dev, di_node_t node, di_prom_handle_t ph,
496      char *buf, int bufsz, int flags)
497  {
498  	int *nump;
499  	int rv;
500  	char *str, *devtype;
501  
502  	rv = di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, node,
503  	    PROP_DEV_TYPE, &devtype);
504  	if (rv < 1)
505  		return (0);
506  
507  	if (strcmp(devtype, PROPVAL_PCIEX) == 0)
508  		devtype = DEVTYPE_PCIE;
509  
510  	if (flags & APNODE_DEFNAME)
511  		goto DEF;
512  
513  	str = pci_cfg_slotname(node, ph, pci_dev);
514  	if (str != NULL) {
515  		(void) strlcpy(buf, str, bufsz);
516  		return (1);
517  	}
518  
519  	if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_PHYS_SLOT,
520  	    &nump) > 0) {
521  		if (*nump > 0) {
522  			(void) snprintf(buf, bufsz, "%s%d", devtype, *nump);
523  			return (1);
524  		}
525  	}
526  DEF:
527  	(void) snprintf(buf, bufsz, "%s%d.%s%d",
528  	    di_driver_name(node), di_instance(node), devtype, pci_dev);
529  
530  	return (1);
531  }
532  
533  
534  /*
535   * returns non-zero if we can return a valid expansion chassis name for <node>
536   * through <buf>
537   *
538   * prioritized naming scheme:
539   *	1) <IOB_PRE string><PROP_SERID property: sun specific portion>
540   *	2) <IOB_PRE string><full PROP_SERID property in hex>
541   *	3) <IOB_PRE string>
542   *
543   * PROP_SERID encoding <64-bit int: msb ... lsb>:
544   * <24 bits: vendor id><40 bits: serial number>
545   *
546   * sun encoding of 40 bit serial number:
547   * first byte = device type indicator (ignored in naming scheme)
548   * next 4 bytes = 4 ascii characters
549   */
550  /*ARGSUSED*/
551  static int
552  pci_cfg_iob_name(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
553      char *buf, int bufsz)
554  {
555  	int64_t *seridp;
556  	int64_t serid;
557  	char *idstr;
558  
559  	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, PROP_SERID,
560  	    &seridp) < 1) {
561  		(void) strlcpy(buf, IOB_PRE, bufsz);
562  		return (1);
563  	}
564  	serid = *seridp;
565  
566  	if (serid >> 40 != VENDID_SUN) {
567  		(void) snprintf(buf, bufsz, "%s%llx", IOB_PRE, serid);
568  		return (1);
569  	}
570  
571  	serid &= SIZE2MASK64(40);
572  	idstr = (char *)&serid;
573  	idstr[sizeof (serid) - 1] = '\0';
574  	/* skip device type indicator */
575  	idstr++;
576  	(void) snprintf(buf, bufsz, "%s%s", IOB_PRE, idstr);
577  	return (1);
578  }
579  
580  
581  /*
582   * returns the pci device number for <node> if found, else returns PCIDEV_NIL
583   */
584  static minor_t
585  pci_cfg_pcidev(di_node_t node, di_prom_handle_t ph)
586  {
587  	int rv;
588  	int *regp;
589  
590  	rv = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_REG,
591  	    &regp);
592  
593  	if (rv < 1) {
594  		dprint(("pci_cfg_pcidev: property %s not found "
595  		    "for %s%d\n", PROP_REG, DRVINST(node)));
596  		return (PCIDEV_NIL);
597  	}
598  
599  	return (REG_PCIDEV(regp));
600  }
601  
602  
603  /*
604   * returns non-zero when it can successfully return an attachment point
605   * through <ap_path> whose length is less than <ap_pathsz>; returns the full
606   * path of the AP through <pathret> which may be larger than <ap_pathsz>.
607   * Callers need to free <pathret>.  If it cannot return the full path through
608   * <pathret> it will be set to NULL
609   *
610   * The ap path reflects a subset of the device path from an onboard host slot
611   * up to <node>.  We traverse up the device tree starting from <node>, naming
612   * each component using pci_cfg_ap_node().  If we detect that a certain
613   * segment is contained within an expansion chassis, then we skip any bus
614   * nodes in between our current node and the topmost node of the chassis,
615   * which is identified by the PROP_FIRST_CHAS property, and prepend the name
616   * of the expansion chassis as given by pci_cfg_iob_name()
617   *
618   * This scheme is always used for <pathret>.  If however, the size of
619   * <pathret> is greater than <ap_pathsz> then only the default name as given
620   * by pci_cfg_ap_node() for <node> will be used
621   */
622  static int
623  pci_cfg_ap_path(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
624      char *ap_path, int ap_pathsz, char **pathret)
625  {
626  #ifdef	DEBUG
627  	char *fnm = "pci_cfg_ap_path";
628  #endif
629  #define	seplen		(sizeof (AP_PATH_SEP) - 1)
630  #define	iob_pre_len	(sizeof (IOB_PRE) - 1)
631  #define	ap_path_iob_sep_len	(sizeof (AP_PATH_IOB_SEP) - 1)
632  
633  	char *bufptr;
634  	char buf[MAXPATHLEN];
635  	char pathbuf[MAXPATHLEN];
636  	int bufsz;
637  	char *pathptr;
638  	char *pathend = NULL;
639  	int len;
640  	int rv = 0;
641  	int chasflag = 0;
642  	di_node_t curnode = node;
643  	di_node_t chasnode = DI_NODE_NIL;
644  	minor_t pci_dev;
645  
646  	buf[0] = '\0';
647  	pathbuf[0] = '\0';
648  	pathptr = &pathbuf[sizeof (pathbuf) - 1];
649  	*pathptr = '\0';
650  
651  	/*
652  	 * as we traverse up the device tree, we prepend components of our
653  	 * path inside pathbuf, using pathptr and decrementing
654  	 */
655  	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
656  	do {
657  		bufptr = buf;
658  		bufsz = sizeof (buf);
659  
660  		chasnode = pci_cfg_chassis_node(curnode, ph);
661  		if (chasnode != DI_NODE_NIL) {
662  			rv = pci_cfg_iob_name(minor, chasnode, ph,
663  			    bufptr, bufsz);
664  			if (rv == 0) {
665  				dprint(("%s: cannot create iob name "
666  				    "for %s%d\n", fnm, DRVINST(node)));
667  				*pathptr = '\0';
668  				goto OUT;
669  			}
670  
671  			(void) strncat(bufptr, AP_PATH_IOB_SEP, bufsz);
672  			len = strlen(bufptr);
673  			bufptr += len;
674  			bufsz -= len - 1;
675  
676  			/* set chasflag when the leaf node is within an iob */
677  			if ((curnode == node) != NULL)
678  				chasflag = 1;
679  		}
680  		rv = pci_cfg_ap_node(pci_dev, curnode, ph, bufptr, bufsz, 0);
681  		if (rv == 0) {
682  			dprint(("%s: cannot create ap node name "
683  			    "for %s%d\n", fnm, DRVINST(node)));
684  			*pathptr = '\0';
685  			goto OUT;
686  		}
687  
688  		/*
689  		 * if we can't fit the entire path in our pathbuf, then use
690  		 * the default short name and nullify pathptr; also, since
691  		 * we prepend in the buffer, we must avoid adding a null char
692  		 */
693  		if (curnode != node) {
694  			pathptr -= seplen;
695  			if (pathptr < pathbuf) {
696  				pathptr = pathbuf;
697  				*pathptr = '\0';
698  				goto DEF;
699  			}
700  			(void) memcpy(pathptr, AP_PATH_SEP, seplen);
701  		}
702  		len = strlen(buf);
703  		pathptr -= len;
704  		if (pathptr < pathbuf) {
705  			pathptr = pathbuf;
706  			*pathptr = '\0';
707  			goto DEF;
708  		}
709  		(void) memcpy(pathptr, buf, len);
710  
711  		/* remember the leaf component */
712  		if (curnode == node)
713  			pathend = pathptr;
714  
715  		/*
716  		 * go no further than the hosts' onboard slots
717  		 */
718  		if (chasnode == DI_NODE_NIL)
719  			break;
720  		curnode = chasnode;
721  
722  		/*
723  		 * the pci device number of the current node is used to
724  		 * identify which slot of the parent's bus (next iteration)
725  		 * the current node is on
726  		 */
727  		pci_dev = pci_cfg_pcidev(curnode, ph);
728  		if (pci_dev == PCIDEV_NIL) {
729  			dprint(("%s: cannot obtain pci device number "
730  			    "for %s%d\n", fnm, DRVINST(node)));
731  			*pathptr = '\0';
732  			goto OUT;
733  		}
734  	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
735  
736  	pathbuf[sizeof (pathbuf) - 1] = '\0';
737  	if (strlen(pathptr) < ap_pathsz) {
738  		(void) strlcpy(ap_path, pathptr, ap_pathsz);
739  		rv = 1;
740  		goto OUT;
741  	}
742  
743  DEF:
744  	/*
745  	 * when our name won't fit <ap_pathsz> we use the endpoint/leaf
746  	 * <node>'s name ONLY IF it has a serialid# which will make the apid
747  	 * globally unique
748  	 */
749  	if (chasflag && pathend != NULL) {
750  		if ((strncmp(pathend + iob_pre_len, AP_PATH_IOB_SEP,
751  		    ap_path_iob_sep_len) != 0) &&
752  		    (strlen(pathend) < ap_pathsz)) {
753  			(void) strlcpy(ap_path, pathend, ap_pathsz);
754  			rv = 1;
755  			goto OUT;
756  		}
757  	}
758  
759  	/*
760  	 * if our name still won't fit <ap_pathsz>, then use the leaf <node>'s
761  	 * default name
762  	 */
763  	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
764  	rv = pci_cfg_ap_node(pci_dev, node, ph, buf, bufsz, APNODE_DEFNAME);
765  	if (rv == 0) {
766  		dprint(("%s: cannot create default ap node name for %s%d\n",
767  		    fnm, DRVINST(node)));
768  		*pathptr = '\0';
769  		goto OUT;
770  	}
771  	if (strlen(buf) < ap_pathsz) {
772  		(void) strlcpy(ap_path, buf, ap_pathsz);
773  		rv = 1;
774  		goto OUT;
775  	}
776  
777  	/*
778  	 * in this case, cfgadm goes through an expensive process to generate
779  	 * a purely dynamic logical apid: the framework will look through
780  	 * the device tree for attachment point minor nodes and will invoke
781  	 * each plugin responsible for that attachment point class, and if
782  	 * the plugin returns a logical apid that matches the queried apid
783  	 * or matches the default apid generated by the cfgadm framework for
784  	 * that driver/class (occurs when plugin returns an empty logical apid)
785  	 * then that is what it will use
786  	 *
787  	 * it is doubly expensive because the cfgadm pci plugin itself will
788  	 * also search the entire device tree in the absence of a link
789  	 */
790  	rv = 0;
791  	dprint(("%s: cannot create apid for %s%d within length of %d\n",
792  	    fnm, DRVINST(node), ap_pathsz));
793  
794  OUT:
795  	ap_path[ap_pathsz - 1] = '\0';
796  	*pathret = (*pathptr == '\0') ? NULL : strdup(pathptr);
797  	return (rv);
798  
799  #undef	seplen
800  #undef	iob_pre_len
801  #undef	ap_path_iob_sep_len
802  }
803  
804  
805  /*
806   * the PROP_AP_NAMES property contains the first integer section of the
807   * ieee1275 "slot-names" property and functions as a bitmask; see comment for
808   * pci_cfg_slotname()
809   *
810   * we use the name of the attachment point minor node if its pci device
811   * number (encoded in the minor number) is allowed by PROP_AP_NAMES
812   *
813   * returns non-zero if we return a valid attachment point through <path>
814   */
815  static int
816  pci_cfg_ap_legacy(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
817      char *ap_path, int ap_pathsz)
818  {
819  	minor_t pci_dev;
820  	int *anp;
821  
822  	if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_AP_NAMES,
823  	    &anp) < 1)
824  		return (0);
825  
826  	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
827  	if ((*anp & (1 << pci_dev)) == 0)
828  		return (0);
829  
830  	(void) strlcpy(ap_path, di_minor_name(minor), ap_pathsz);
831  	return (1);
832  }
833  
834  
835  /*
836   * determine if <node> qualifies for a path style apid
837   */
838  static int
839  pci_cfg_is_ap_path(di_node_t node, di_prom_handle_t ph)
840  {
841  	char *devtype;
842  	di_node_t curnode = node;
843  
844  	do {
845  		if (di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, curnode,
846  		    PROP_DEV_TYPE, &devtype) > 0)
847  			if (strcmp(devtype, PROPVAL_PCIEX) == 0)
848  				return (1);
849  	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
850  
851  	return (0);
852  }
853  
854  
855  /*
856   * takes a full path as returned by <pathret> from pci_cfg_ap_path() and
857   * returns an allocated string intendend to be stored in a devlink info (dli)
858   * file
859   *
860   * data format: "Location: <transformed path>"
861   * where <transformed path> is <path> with occurrances of AP_PATH_SEP
862   * replaced by "/"
863   */
864  static char *
865  pci_cfg_info_data(char *path)
866  {
867  #define	head	"Location: "
868  #define	headlen	(sizeof (head) - 1)
869  #define	seplen	(sizeof (AP_PATH_SEP) - 1)
870  
871  	char *sep, *prev, *np;
872  	char *newpath;
873  	int pathlen = strlen(path);
874  	int len;
875  
876  	newpath = malloc(sizeof (char) * (headlen + pathlen + 1));
877  	np = newpath;
878  	(void) strcpy(np, head);
879  	np += headlen;
880  
881  	prev = path;
882  	while ((sep = strstr(prev, AP_PATH_SEP)) != NULL) {
883  		len = sep - prev;
884  		(void) memcpy(np, prev, len);
885  		np += len;
886  		*np++ = '/';
887  		prev = sep + seplen;
888  	}
889  	(void) strcpy(np, prev);
890  	return (newpath);
891  
892  #undef	head
893  #undef	headlen
894  #undef	seplen
895  }
896  
897  
898  static void
899  pci_cfg_rm_link(char *file)
900  {
901  	char *dlipath;
902  
903  	dlipath = di_dli_name(file);
904  	(void) unlink(dlipath);
905  
906  	devfsadm_rm_all(file);
907  	free(dlipath);
908  }
909  
910  /*
911   * removes all registered devlinks to physical path <physpath> except for
912   * the devlink <valid> if not NULL;
913   * <physpath> must include the minor node
914   */
915  static void
916  pci_cfg_rm_invalid_links(char *physpath, char *valid)
917  {
918  	char **dnp;
919  	char *cp, *vcp;
920  	int i, dnlen;
921  
922  	dnp = devfsadm_lookup_dev_names(physpath, NULL, &dnlen);
923  	if (dnp == NULL)
924  		return;
925  
926  	if (valid != NULL) {
927  		if (strncmp(valid, DEV "/", DEV_LEN + 1) == 0)
928  			vcp = valid + DEV_LEN + 1;
929  		else
930  			vcp = valid;
931  	}
932  
933  	for (i = 0; i < dnlen; i++) {
934  		if (strncmp(dnp[i], DEV "/", DEV_LEN + 1) == 0)
935  			cp = dnp[i] + DEV_LEN + 1;
936  		else
937  			cp = dnp[i];
938  
939  		if (valid != NULL) {
940  			if (strcmp(vcp, cp) == 0)
941  				continue;
942  		}
943  		pci_cfg_rm_link(cp);
944  	}
945  	devfsadm_free_dev_names(dnp, dnlen);
946  }
947  
948  
949  /*
950   * takes a complete devinfo snapshot and returns the root node;
951   * callers must do a di_fini() on the returned node;
952   * if the snapshot failed, DI_NODE_NIL is returned instead
953   *
954   * if <pci_node> is not DI_NODE_NIL, it will search for the same devinfo node
955   * in the new snapshot and return it through <ret_node> if it is found,
956   * else DI_NODE_NIL is returned instead
957   *
958   * in addition, if <pci_minor> is not DI_MINOR_NIL, it will also return
959   * the matching minor in the new snapshot through <ret_minor> if it is found,
960   * else DI_MINOR_NIL is returned instead
961   */
962  static di_node_t
963  pci_cfg_snapshot(di_node_t pci_node, di_minor_t pci_minor,
964      di_node_t *ret_node, di_minor_t *ret_minor)
965  {
966  	di_node_t root_node;
967  	di_node_t node;
968  	di_minor_t minor;
969  	int pci_inst;
970  	dev_t pci_devt;
971  
972  	*ret_node = DI_NODE_NIL;
973  	*ret_minor = DI_MINOR_NIL;
974  
975  	root_node = di_init("/", DINFOCPYALL);
976  	if (root_node == DI_NODE_NIL)
977  		return (DI_NODE_NIL);
978  
979  	/*
980  	 * narrow down search by driver, then instance, then minor
981  	 */
982  	if (pci_node == DI_NODE_NIL)
983  		return (root_node);
984  
985  	pci_inst = di_instance(pci_node);
986  	node = di_drv_first_node(di_driver_name(pci_node), root_node);
987  	do {
988  		if (pci_inst == di_instance(node)) {
989  			*ret_node = node;
990  			break;
991  		}
992  	} while ((node = di_drv_next_node(node)) != DI_NODE_NIL);
993  
994  	if (node == DI_NODE_NIL)
995  		return (root_node);
996  
997  	/*
998  	 * found node, now search minors
999  	 */
1000  	if (pci_minor == DI_MINOR_NIL)
1001  		return (root_node);
1002  
1003  	pci_devt = di_minor_devt(pci_minor);
1004  	minor = DI_MINOR_NIL;
1005  	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1006  		if (pci_devt == di_minor_devt(minor)) {
1007  			*ret_minor = minor;
1008  			break;
1009  		}
1010  	}
1011  	return (root_node);
1012  }
1013  
1014  
1015  static int
1016  pci_cfg_creat_cb(di_minor_t pci_minor, di_node_t pci_node)
1017  {
1018  #ifdef	DEBUG
1019  	char *fnm = "pci_cfg_creat_cb";
1020  #endif
1021  #define	ap_pathsz	(sizeof (ap_path))
1022  
1023  	char ap_path[CFGA_LOG_EXT_LEN];
1024  	char linkbuf[MAXPATHLEN];
1025  	char *fullpath = NULL;
1026  	char *pathinfo = NULL;
1027  	char *devpath = NULL;
1028  	int rv, fd = -1;
1029  	size_t sz;
1030  	di_prom_handle_t ph;
1031  	di_node_t node;
1032  	di_node_t root_node = DI_NODE_NIL;
1033  	di_minor_t minor;
1034  
1035  	ph = di_prom_init();
1036  	if (ph == DI_PROM_HANDLE_NIL) {
1037  		dprint(("%s: di_prom_init() failed for %s%d\n",
1038  		    fnm, DRVINST(pci_node)));
1039  		goto OUT;
1040  	}
1041  
1042  	/*
1043  	 * Since incoming nodes from hotplug events are from snapshots that
1044  	 * do NOT contain parent/ancestor data, we must retake our own
1045  	 * snapshot and search for the target node
1046  	 */
1047  	root_node = pci_cfg_snapshot(pci_node, pci_minor, &node, &minor);
1048  	if (root_node == DI_NODE_NIL || node == DI_NODE_NIL ||
1049  	    minor == DI_MINOR_NIL) {
1050  		dprint(("%s: devinfo snapshot or search failed for %s%d\n",
1051  		    fnm, DRVINST(pci_node)));
1052  		goto OUT;
1053  	}
1054  
1055  	if (pci_cfg_is_ap_path(node, ph)) {
1056  		rv = pci_cfg_ap_path(minor, node, ph, ap_path, ap_pathsz,
1057  		    &fullpath);
1058  		if (rv == 0)
1059  			goto OUT;
1060  
1061  		(void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
1062  		    CFG_DIRNAME, ap_path);
1063  
1064  		/*
1065  		 * We must remove existing links because we may have invalid
1066  		 * apids that are valid links.  Since these are not dangling,
1067  		 * devfsadm will not invoke the remove callback on them.
1068  		 *
1069  		 * What are "invalid apids with valid links"?  Consider swapping
1070  		 * an attachment point bus with another while the system is
1071  		 * down, on the same device path bound to the same drivers
1072  		 * but with the new AP bus having different properties
1073  		 * (e.g. serialid#).  If the previous apid is not removed,
1074  		 * there will now be two different links pointing to the same
1075  		 * attachment point, but only one reflects the correct
1076  		 * logical apid
1077  		 */
1078  		devpath = pci_cfg_devpath(node, minor);
1079  		if (devpath == NULL)
1080  			goto OUT;
1081  		pci_cfg_rm_invalid_links(devpath, linkbuf);
1082  		free(devpath);
1083  
1084  		(void) devfsadm_mklink(linkbuf, node, minor, 0);
1085  
1086  		/*
1087  		 * we store the full logical path of the attachment point for
1088  		 * cfgadm to display in its info field which is useful when
1089  		 * the full logical path exceeds the size limit for logical
1090  		 * apids (CFGA_LOG_EXT_LEN)
1091  		 *
1092  		 * for the cfgadm pci plugin to do the same would be expensive
1093  		 * (i.e. devinfo snapshot + top down exhaustive minor search +
1094  		 * equivalent of pci_cfg_ap_path() on every invocation)
1095  		 *
1096  		 * note that if we do not create a link (pci_cfg_ap_path() is
1097  		 * not successful), that is what cfgadm will do anyways to
1098  		 * create a purely dynamic apid
1099  		 */
1100  		pathinfo = pci_cfg_info_data(fullpath);
1101  		fd = di_dli_openw(linkbuf);
1102  		if (fd < 0)
1103  			goto OUT;
1104  
1105  		sz = strlen(pathinfo) + 1;
1106  		rv = write(fd, pathinfo, sz);
1107  		if (rv < sz) {
1108  			dprint(("%s: could not write full pathinfo to dli "
1109  			    "file for %s%d\n", fnm, DRVINST(node)));
1110  			goto OUT;
1111  		}
1112  		di_dli_close(fd);
1113  	} else {
1114  		rv = pci_cfg_ap_legacy(minor, node, ph, ap_path,
1115  		    ap_pathsz);
1116  		if (rv == 0)
1117  			goto OUT;
1118  
1119  		(void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
1120  		    CFG_DIRNAME, ap_path);
1121  		(void) devfsadm_mklink(linkbuf, node, minor, 0);
1122  	}
1123  
1124  OUT:
1125  	if (fd >= 0)
1126  		di_dli_close(fd);
1127  	if (fullpath != NULL)
1128  		free(fullpath);
1129  	if (pathinfo != NULL)
1130  		free(pathinfo);
1131  	if (ph != DI_PROM_HANDLE_NIL)
1132  		di_prom_fini(ph);
1133  	if (root_node != DI_NODE_NIL)
1134  		di_fini(root_node);
1135  	return (DEVFSADM_CONTINUE);
1136  
1137  #undef	ap_pathsz
1138  }
1139  
1140  
1141  static void
1142  pci_cfg_rm_all(char *file)
1143  {
1144  	pci_cfg_rm_link(file);
1145  }
1146  
1147  
1148  /*
1149   * ib_cfg_creat_cb() creates two types of links
1150   * One for the fabric as /dev/cfg/ib
1151   * Another for each HCA seen in the fabric as /dev/cfg/hca:<HCA-GUID>
1152   */
1153  static int
1154  ib_cfg_creat_cb(di_minor_t minor, di_node_t node)
1155  {
1156  	char	*cp;
1157  	char	path[PATH_MAX + 1];
1158  
1159  	if ((cp = di_devfs_path(node)) == NULL) {
1160  		return (DEVFSADM_CONTINUE);
1161  	}
1162  
1163  	(void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
1164  	di_devfs_path_free(cp);
1165  
1166  	/* create fabric or hca:GUID and the symlink */
1167  	if (strstr(path, "ib:fabric") != NULL) {
1168  		(void) snprintf(path, sizeof (path), "%s/ib", CFG_DIRNAME);
1169  	} else {
1170  		(void) snprintf(path, sizeof (path), "%s/hca:%s", CFG_DIRNAME,
1171  		    di_minor_name(minor));
1172  	}
1173  
1174  	(void) devfsadm_mklink(path, node, minor, 0);
1175  	return (DEVFSADM_CONTINUE);
1176  }
1177