xref: /illumos-gate/usr/src/cmd/devfsadm/cfg_link.c (revision b6b3bf89)
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 2007 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: IEEE company 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 	uint64_t serid;
557 
558 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, PROP_SERID,
559 	    &seridp) < 1) {
560 		(void) strlcpy(buf, IOB_PRE, bufsz);
561 		return (1);
562 	}
563 	serid = (uint64_t)*seridp;
564 
565 	if ((serid >> 40) != (uint64_t)IEEE_SUN_ID) {
566 		(void) snprintf(buf, bufsz, "%s%llx", IOB_PRE, serid);
567 		return (1);
568 	}
569 
570 	/*
571 	 * skip 32 bits because the first 3 bytes are the company id and the
572 	 * next byte is the PCIe or PCI-X indicator. The last 4 bytes
573 	 * is being treated as raw unsigned integer instead of a string
574 	 * because some of the bytes are 0x0 (NULL).
575 	 */
576 
577 	(void) snprintf(buf, bufsz, "%s%08x", IOB_PRE,
578 	    (uint32_t)(SIZE2MASK64(32) & serid));
579 
580 	return (1);
581 }
582 
583 
584 /*
585  * returns the pci device number for <node> if found, else returns PCIDEV_NIL
586  */
587 static minor_t
588 pci_cfg_pcidev(di_node_t node, di_prom_handle_t ph)
589 {
590 	int rv;
591 	int *regp;
592 
593 	rv = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_REG,
594 	    &regp);
595 
596 	if (rv < 1) {
597 		dprint(("pci_cfg_pcidev: property %s not found "
598 		    "for %s%d\n", PROP_REG, DRVINST(node)));
599 		return (PCIDEV_NIL);
600 	}
601 
602 	return (REG_PCIDEV(regp));
603 }
604 
605 
606 /*
607  * returns non-zero when it can successfully return an attachment point
608  * through <ap_path> whose length is less than <ap_pathsz>; returns the full
609  * path of the AP through <pathret> which may be larger than <ap_pathsz>.
610  * Callers need to free <pathret>.  If it cannot return the full path through
611  * <pathret> it will be set to NULL
612  *
613  * The ap path reflects a subset of the device path from an onboard host slot
614  * up to <node>.  We traverse up the device tree starting from <node>, naming
615  * each component using pci_cfg_ap_node().  If we detect that a certain
616  * segment is contained within an expansion chassis, then we skip any bus
617  * nodes in between our current node and the topmost node of the chassis,
618  * which is identified by the PROP_FIRST_CHAS property, and prepend the name
619  * of the expansion chassis as given by pci_cfg_iob_name()
620  *
621  * This scheme is always used for <pathret>.  If however, the size of
622  * <pathret> is greater than <ap_pathsz> then only the default name as given
623  * by pci_cfg_ap_node() for <node> will be used
624  */
625 static int
626 pci_cfg_ap_path(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
627     char *ap_path, int ap_pathsz, char **pathret)
628 {
629 #ifdef	DEBUG
630 	char *fnm = "pci_cfg_ap_path";
631 #endif
632 #define	seplen		(sizeof (AP_PATH_SEP) - 1)
633 #define	iob_pre_len	(sizeof (IOB_PRE) - 1)
634 #define	ap_path_iob_sep_len	(sizeof (AP_PATH_IOB_SEP) - 1)
635 
636 	char *bufptr;
637 	char buf[MAXPATHLEN];
638 	char pathbuf[MAXPATHLEN];
639 	int bufsz;
640 	char *pathptr;
641 	char *pathend = NULL;
642 	int len;
643 	int rv = 0;
644 	int chasflag = 0;
645 	di_node_t curnode = node;
646 	di_node_t chasnode = DI_NODE_NIL;
647 	minor_t pci_dev;
648 
649 	buf[0] = '\0';
650 	pathbuf[0] = '\0';
651 	pathptr = &pathbuf[sizeof (pathbuf) - 1];
652 	*pathptr = '\0';
653 
654 	/*
655 	 * as we traverse up the device tree, we prepend components of our
656 	 * path inside pathbuf, using pathptr and decrementing
657 	 */
658 	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
659 	do {
660 		bufptr = buf;
661 		bufsz = sizeof (buf);
662 
663 		chasnode = pci_cfg_chassis_node(curnode, ph);
664 		if (chasnode != DI_NODE_NIL) {
665 			rv = pci_cfg_iob_name(minor, chasnode, ph,
666 			    bufptr, bufsz);
667 			if (rv == 0) {
668 				dprint(("%s: cannot create iob name "
669 				    "for %s%d\n", fnm, DRVINST(node)));
670 				*pathptr = '\0';
671 				goto OUT;
672 			}
673 
674 			(void) strncat(bufptr, AP_PATH_IOB_SEP, bufsz);
675 			len = strlen(bufptr);
676 			bufptr += len;
677 			bufsz -= len - 1;
678 
679 			/* set chasflag when the leaf node is within an iob */
680 			if ((curnode == node) != NULL)
681 				chasflag = 1;
682 		}
683 		rv = pci_cfg_ap_node(pci_dev, curnode, ph, bufptr, bufsz, 0);
684 		if (rv == 0) {
685 			dprint(("%s: cannot create ap node name "
686 			    "for %s%d\n", fnm, DRVINST(node)));
687 			*pathptr = '\0';
688 			goto OUT;
689 		}
690 
691 		/*
692 		 * if we can't fit the entire path in our pathbuf, then use
693 		 * the default short name and nullify pathptr; also, since
694 		 * we prepend in the buffer, we must avoid adding a null char
695 		 */
696 		if (curnode != node) {
697 			pathptr -= seplen;
698 			if (pathptr < pathbuf) {
699 				pathptr = pathbuf;
700 				*pathptr = '\0';
701 				goto DEF;
702 			}
703 			(void) memcpy(pathptr, AP_PATH_SEP, seplen);
704 		}
705 		len = strlen(buf);
706 		pathptr -= len;
707 		if (pathptr < pathbuf) {
708 			pathptr = pathbuf;
709 			*pathptr = '\0';
710 			goto DEF;
711 		}
712 		(void) memcpy(pathptr, buf, len);
713 
714 		/* remember the leaf component */
715 		if (curnode == node)
716 			pathend = pathptr;
717 
718 		/*
719 		 * go no further than the hosts' onboard slots
720 		 */
721 		if (chasnode == DI_NODE_NIL)
722 			break;
723 		curnode = chasnode;
724 
725 		/*
726 		 * the pci device number of the current node is used to
727 		 * identify which slot of the parent's bus (next iteration)
728 		 * the current node is on
729 		 */
730 		pci_dev = pci_cfg_pcidev(curnode, ph);
731 		if (pci_dev == PCIDEV_NIL) {
732 			dprint(("%s: cannot obtain pci device number "
733 			    "for %s%d\n", fnm, DRVINST(node)));
734 			*pathptr = '\0';
735 			goto OUT;
736 		}
737 	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
738 
739 	pathbuf[sizeof (pathbuf) - 1] = '\0';
740 	if (strlen(pathptr) < ap_pathsz) {
741 		(void) strlcpy(ap_path, pathptr, ap_pathsz);
742 		rv = 1;
743 		goto OUT;
744 	}
745 
746 DEF:
747 	/*
748 	 * when our name won't fit <ap_pathsz> we use the endpoint/leaf
749 	 * <node>'s name ONLY IF it has a serialid# which will make the apid
750 	 * globally unique
751 	 */
752 	if (chasflag && pathend != NULL) {
753 		if ((strncmp(pathend + iob_pre_len, AP_PATH_IOB_SEP,
754 		    ap_path_iob_sep_len) != 0) &&
755 		    (strlen(pathend) < ap_pathsz)) {
756 			(void) strlcpy(ap_path, pathend, ap_pathsz);
757 			rv = 1;
758 			goto OUT;
759 		}
760 	}
761 
762 	/*
763 	 * if our name still won't fit <ap_pathsz>, then use the leaf <node>'s
764 	 * default name
765 	 */
766 	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
767 	rv = pci_cfg_ap_node(pci_dev, node, ph, buf, bufsz, APNODE_DEFNAME);
768 	if (rv == 0) {
769 		dprint(("%s: cannot create default ap node name for %s%d\n",
770 		    fnm, DRVINST(node)));
771 		*pathptr = '\0';
772 		goto OUT;
773 	}
774 	if (strlen(buf) < ap_pathsz) {
775 		(void) strlcpy(ap_path, buf, ap_pathsz);
776 		rv = 1;
777 		goto OUT;
778 	}
779 
780 	/*
781 	 * in this case, cfgadm goes through an expensive process to generate
782 	 * a purely dynamic logical apid: the framework will look through
783 	 * the device tree for attachment point minor nodes and will invoke
784 	 * each plugin responsible for that attachment point class, and if
785 	 * the plugin returns a logical apid that matches the queried apid
786 	 * or matches the default apid generated by the cfgadm framework for
787 	 * that driver/class (occurs when plugin returns an empty logical apid)
788 	 * then that is what it will use
789 	 *
790 	 * it is doubly expensive because the cfgadm pci plugin itself will
791 	 * also search the entire device tree in the absence of a link
792 	 */
793 	rv = 0;
794 	dprint(("%s: cannot create apid for %s%d within length of %d\n",
795 	    fnm, DRVINST(node), ap_pathsz));
796 
797 OUT:
798 	ap_path[ap_pathsz - 1] = '\0';
799 	*pathret = (*pathptr == '\0') ? NULL : strdup(pathptr);
800 	return (rv);
801 
802 #undef	seplen
803 #undef	iob_pre_len
804 #undef	ap_path_iob_sep_len
805 }
806 
807 
808 /*
809  * the PROP_AP_NAMES property contains the first integer section of the
810  * ieee1275 "slot-names" property and functions as a bitmask; see comment for
811  * pci_cfg_slotname()
812  *
813  * we use the name of the attachment point minor node if its pci device
814  * number (encoded in the minor number) is allowed by PROP_AP_NAMES
815  *
816  * returns non-zero if we return a valid attachment point through <path>
817  */
818 static int
819 pci_cfg_ap_legacy(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
820     char *ap_path, int ap_pathsz)
821 {
822 	minor_t pci_dev;
823 	int *anp;
824 
825 	if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, PROP_AP_NAMES,
826 	    &anp) < 1)
827 		return (0);
828 
829 	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
830 	if ((*anp & (1 << pci_dev)) == 0)
831 		return (0);
832 
833 	(void) strlcpy(ap_path, di_minor_name(minor), ap_pathsz);
834 	return (1);
835 }
836 
837 
838 /*
839  * determine if <node> qualifies for a path style apid
840  */
841 static int
842 pci_cfg_is_ap_path(di_node_t node, di_prom_handle_t ph)
843 {
844 	char *devtype;
845 	di_node_t curnode = node;
846 
847 	do {
848 		if (di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, curnode,
849 		    PROP_DEV_TYPE, &devtype) > 0)
850 			if (strcmp(devtype, PROPVAL_PCIEX) == 0)
851 				return (1);
852 	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
853 
854 	return (0);
855 }
856 
857 
858 /*
859  * takes a full path as returned by <pathret> from pci_cfg_ap_path() and
860  * returns an allocated string intendend to be stored in a devlink info (dli)
861  * file
862  *
863  * data format: "Location: <transformed path>"
864  * where <transformed path> is <path> with occurrances of AP_PATH_SEP
865  * replaced by "/"
866  */
867 static char *
868 pci_cfg_info_data(char *path)
869 {
870 #define	head	"Location: "
871 #define	headlen	(sizeof (head) - 1)
872 #define	seplen	(sizeof (AP_PATH_SEP) - 1)
873 
874 	char *sep, *prev, *np;
875 	char *newpath;
876 	int pathlen = strlen(path);
877 	int len;
878 
879 	newpath = malloc(sizeof (char) * (headlen + pathlen + 1));
880 	np = newpath;
881 	(void) strcpy(np, head);
882 	np += headlen;
883 
884 	prev = path;
885 	while ((sep = strstr(prev, AP_PATH_SEP)) != NULL) {
886 		len = sep - prev;
887 		(void) memcpy(np, prev, len);
888 		np += len;
889 		*np++ = '/';
890 		prev = sep + seplen;
891 	}
892 	(void) strcpy(np, prev);
893 	return (newpath);
894 
895 #undef	head
896 #undef	headlen
897 #undef	seplen
898 }
899 
900 
901 static void
902 pci_cfg_rm_link(char *file)
903 {
904 	char *dlipath;
905 
906 	dlipath = di_dli_name(file);
907 	(void) unlink(dlipath);
908 
909 	devfsadm_rm_all(file);
910 	free(dlipath);
911 }
912 
913 /*
914  * removes all registered devlinks to physical path <physpath> except for
915  * the devlink <valid> if not NULL;
916  * <physpath> must include the minor node
917  */
918 static void
919 pci_cfg_rm_invalid_links(char *physpath, char *valid)
920 {
921 	char **dnp;
922 	char *cp, *vcp;
923 	int i, dnlen;
924 
925 	dnp = devfsadm_lookup_dev_names(physpath, NULL, &dnlen);
926 	if (dnp == NULL)
927 		return;
928 
929 	if (valid != NULL) {
930 		if (strncmp(valid, DEV "/", DEV_LEN + 1) == 0)
931 			vcp = valid + DEV_LEN + 1;
932 		else
933 			vcp = valid;
934 	}
935 
936 	for (i = 0; i < dnlen; i++) {
937 		if (strncmp(dnp[i], DEV "/", DEV_LEN + 1) == 0)
938 			cp = dnp[i] + DEV_LEN + 1;
939 		else
940 			cp = dnp[i];
941 
942 		if (valid != NULL) {
943 			if (strcmp(vcp, cp) == 0)
944 				continue;
945 		}
946 		pci_cfg_rm_link(cp);
947 	}
948 	devfsadm_free_dev_names(dnp, dnlen);
949 }
950 
951 
952 /*
953  * takes a complete devinfo snapshot and returns the root node;
954  * callers must do a di_fini() on the returned node;
955  * if the snapshot failed, DI_NODE_NIL is returned instead
956  *
957  * if <pci_node> is not DI_NODE_NIL, it will search for the same devinfo node
958  * in the new snapshot and return it through <ret_node> if it is found,
959  * else DI_NODE_NIL is returned instead
960  *
961  * in addition, if <pci_minor> is not DI_MINOR_NIL, it will also return
962  * the matching minor in the new snapshot through <ret_minor> if it is found,
963  * else DI_MINOR_NIL is returned instead
964  */
965 static di_node_t
966 pci_cfg_snapshot(di_node_t pci_node, di_minor_t pci_minor,
967     di_node_t *ret_node, di_minor_t *ret_minor)
968 {
969 	di_node_t root_node;
970 	di_node_t node;
971 	di_minor_t minor;
972 	int pci_inst;
973 	dev_t pci_devt;
974 
975 	*ret_node = DI_NODE_NIL;
976 	*ret_minor = DI_MINOR_NIL;
977 
978 	root_node = di_init("/", DINFOCPYALL);
979 	if (root_node == DI_NODE_NIL)
980 		return (DI_NODE_NIL);
981 
982 	/*
983 	 * narrow down search by driver, then instance, then minor
984 	 */
985 	if (pci_node == DI_NODE_NIL)
986 		return (root_node);
987 
988 	pci_inst = di_instance(pci_node);
989 	node = di_drv_first_node(di_driver_name(pci_node), root_node);
990 	do {
991 		if (pci_inst == di_instance(node)) {
992 			*ret_node = node;
993 			break;
994 		}
995 	} while ((node = di_drv_next_node(node)) != DI_NODE_NIL);
996 
997 	if (node == DI_NODE_NIL)
998 		return (root_node);
999 
1000 	/*
1001 	 * found node, now search minors
1002 	 */
1003 	if (pci_minor == DI_MINOR_NIL)
1004 		return (root_node);
1005 
1006 	pci_devt = di_minor_devt(pci_minor);
1007 	minor = DI_MINOR_NIL;
1008 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1009 		if (pci_devt == di_minor_devt(minor)) {
1010 			*ret_minor = minor;
1011 			break;
1012 		}
1013 	}
1014 	return (root_node);
1015 }
1016 
1017 
1018 static int
1019 pci_cfg_creat_cb(di_minor_t pci_minor, di_node_t pci_node)
1020 {
1021 #ifdef	DEBUG
1022 	char *fnm = "pci_cfg_creat_cb";
1023 #endif
1024 #define	ap_pathsz	(sizeof (ap_path))
1025 
1026 	char ap_path[CFGA_LOG_EXT_LEN];
1027 	char linkbuf[MAXPATHLEN];
1028 	char *fullpath = NULL;
1029 	char *pathinfo = NULL;
1030 	char *devpath = NULL;
1031 	int rv, fd = -1;
1032 	size_t sz;
1033 	di_prom_handle_t ph;
1034 	di_node_t node;
1035 	di_node_t root_node = DI_NODE_NIL;
1036 	di_minor_t minor;
1037 
1038 	ph = di_prom_init();
1039 	if (ph == DI_PROM_HANDLE_NIL) {
1040 		dprint(("%s: di_prom_init() failed for %s%d\n",
1041 		    fnm, DRVINST(pci_node)));
1042 		goto OUT;
1043 	}
1044 
1045 	/*
1046 	 * Since incoming nodes from hotplug events are from snapshots that
1047 	 * do NOT contain parent/ancestor data, we must retake our own
1048 	 * snapshot and search for the target node
1049 	 */
1050 	root_node = pci_cfg_snapshot(pci_node, pci_minor, &node, &minor);
1051 	if (root_node == DI_NODE_NIL || node == DI_NODE_NIL ||
1052 	    minor == DI_MINOR_NIL) {
1053 		dprint(("%s: devinfo snapshot or search failed for %s%d\n",
1054 		    fnm, DRVINST(pci_node)));
1055 		goto OUT;
1056 	}
1057 
1058 	if (pci_cfg_is_ap_path(node, ph)) {
1059 		rv = pci_cfg_ap_path(minor, node, ph, ap_path, ap_pathsz,
1060 		    &fullpath);
1061 		if (rv == 0)
1062 			goto OUT;
1063 
1064 		(void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
1065 		    CFG_DIRNAME, ap_path);
1066 
1067 		/*
1068 		 * We must remove existing links because we may have invalid
1069 		 * apids that are valid links.  Since these are not dangling,
1070 		 * devfsadm will not invoke the remove callback on them.
1071 		 *
1072 		 * What are "invalid apids with valid links"?  Consider swapping
1073 		 * an attachment point bus with another while the system is
1074 		 * down, on the same device path bound to the same drivers
1075 		 * but with the new AP bus having different properties
1076 		 * (e.g. serialid#).  If the previous apid is not removed,
1077 		 * there will now be two different links pointing to the same
1078 		 * attachment point, but only one reflects the correct
1079 		 * logical apid
1080 		 */
1081 		devpath = pci_cfg_devpath(node, minor);
1082 		if (devpath == NULL)
1083 			goto OUT;
1084 		pci_cfg_rm_invalid_links(devpath, linkbuf);
1085 		free(devpath);
1086 
1087 		(void) devfsadm_mklink(linkbuf, node, minor, 0);
1088 
1089 		/*
1090 		 * we store the full logical path of the attachment point for
1091 		 * cfgadm to display in its info field which is useful when
1092 		 * the full logical path exceeds the size limit for logical
1093 		 * apids (CFGA_LOG_EXT_LEN)
1094 		 *
1095 		 * for the cfgadm pci plugin to do the same would be expensive
1096 		 * (i.e. devinfo snapshot + top down exhaustive minor search +
1097 		 * equivalent of pci_cfg_ap_path() on every invocation)
1098 		 *
1099 		 * note that if we do not create a link (pci_cfg_ap_path() is
1100 		 * not successful), that is what cfgadm will do anyways to
1101 		 * create a purely dynamic apid
1102 		 */
1103 		pathinfo = pci_cfg_info_data(fullpath);
1104 		fd = di_dli_openw(linkbuf);
1105 		if (fd < 0)
1106 			goto OUT;
1107 
1108 		sz = strlen(pathinfo) + 1;
1109 		rv = write(fd, pathinfo, sz);
1110 		if (rv < sz) {
1111 			dprint(("%s: could not write full pathinfo to dli "
1112 			    "file for %s%d\n", fnm, DRVINST(node)));
1113 			goto OUT;
1114 		}
1115 		di_dli_close(fd);
1116 	} else {
1117 		rv = pci_cfg_ap_legacy(minor, node, ph, ap_path,
1118 		    ap_pathsz);
1119 		if (rv == 0)
1120 			goto OUT;
1121 
1122 		(void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
1123 		    CFG_DIRNAME, ap_path);
1124 		(void) devfsadm_mklink(linkbuf, node, minor, 0);
1125 	}
1126 
1127 OUT:
1128 	if (fd >= 0)
1129 		di_dli_close(fd);
1130 	if (fullpath != NULL)
1131 		free(fullpath);
1132 	if (pathinfo != NULL)
1133 		free(pathinfo);
1134 	if (ph != DI_PROM_HANDLE_NIL)
1135 		di_prom_fini(ph);
1136 	if (root_node != DI_NODE_NIL)
1137 		di_fini(root_node);
1138 	return (DEVFSADM_CONTINUE);
1139 
1140 #undef	ap_pathsz
1141 }
1142 
1143 
1144 static void
1145 pci_cfg_rm_all(char *file)
1146 {
1147 	pci_cfg_rm_link(file);
1148 }
1149 
1150 
1151 /*
1152  * ib_cfg_creat_cb() creates two types of links
1153  * One for the fabric as /dev/cfg/ib
1154  * Another for each HCA seen in the fabric as /dev/cfg/hca:<HCA-GUID>
1155  */
1156 static int
1157 ib_cfg_creat_cb(di_minor_t minor, di_node_t node)
1158 {
1159 	char	*cp;
1160 	char	path[PATH_MAX + 1];
1161 
1162 	if ((cp = di_devfs_path(node)) == NULL) {
1163 		return (DEVFSADM_CONTINUE);
1164 	}
1165 
1166 	(void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
1167 	di_devfs_path_free(cp);
1168 
1169 	/* create fabric or hca:GUID and the symlink */
1170 	if (strstr(path, "ib:fabric") != NULL) {
1171 		(void) snprintf(path, sizeof (path), "%s/ib", CFG_DIRNAME);
1172 	} else {
1173 		(void) snprintf(path, sizeof (path), "%s/hca:%s", CFG_DIRNAME,
1174 		    di_minor_name(minor));
1175 	}
1176 
1177 	(void) devfsadm_mklink(path, node, minor, 0);
1178 	return (DEVFSADM_CONTINUE);
1179 }
1180