xref: /illumos-gate/usr/src/cmd/devfsadm/cfg_link.c (revision 3ebafc43)
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 static int 	serid_printable(uint64_t *seridp);
91 static int	di_propall_lookup_slot_names(di_prom_handle_t, int,
92 		    dev_t, di_node_t, di_slot_name_t **);
93 
94 
95 /*
96  * NOTE: The CREATE_DEFER flag is private to this module.
97  *	 NOT to be used by other modules
98  */
99 static devfsadm_create_t cfg_create_cbt[] = {
100 	{ "attachment-point", DDI_NT_SCSI_ATTACHMENT_POINT, NULL,
101 	    TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
102 	},
103 	{ "attachment-point", DDI_NT_SBD_ATTACHMENT_POINT, NULL,
104 	    TYPE_EXACT, ILEVEL_0, sbd_cfg_creat_cb
105 	},
106 	{ "fc-attachment-point", DDI_NT_FC_ATTACHMENT_POINT, NULL,
107 	    TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
108 	},
109 	{ "attachment-point", DDI_NT_USB_ATTACHMENT_POINT, NULL,
110 	    TYPE_EXACT, ILEVEL_0, usb_cfg_creat_cb
111 	},
112 	{ "attachment-point", DDI_NT_PCI_ATTACHMENT_POINT, NULL,
113 	    TYPE_EXACT, ILEVEL_0, pci_cfg_creat_cb
114 	},
115 	{ "attachment-point", DDI_NT_IB_ATTACHMENT_POINT, NULL,
116 	    TYPE_EXACT, ILEVEL_0, ib_cfg_creat_cb
117 	},
118 	{ "attachment-point", DDI_NT_SATA_ATTACHMENT_POINT, NULL,
119 	    TYPE_EXACT, ILEVEL_0, sata_cfg_creat_cb
120 	}
121 };
122 
123 DEVFSADM_CREATE_INIT_V0(cfg_create_cbt);
124 
125 static devfsadm_remove_t cfg_remove_cbt[] = {
126 	{ "attachment-point", SCSI_CFG_LINK_RE, RM_POST,
127 	    ILEVEL_0, devfsadm_rm_all
128 	},
129 	{ "attachment-point", SBD_CFG_LINK_RE, RM_POST,
130 	    ILEVEL_0, devfsadm_rm_all
131 	},
132 	{ "fc-attachment-point", SCSI_CFG_LINK_RE, RM_POST,
133 	    ILEVEL_0, devfsadm_rm_all
134 	},
135 	{ "attachment-point", USB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
136 	    ILEVEL_0, devfsadm_rm_all
137 	},
138 	{ "attachment-point", PCI_CFG_LINK_RE, RM_POST,
139 	    ILEVEL_0, devfsadm_rm_all
140 	},
141 	{ "attachment-point", PCI_CFG_PATH_LINK_RE, RM_POST|RM_HOT,
142 	    ILEVEL_0, pci_cfg_rm_all
143 	},
144 	{ "attachment-point", IB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
145 	    ILEVEL_0, devfsadm_rm_all
146 	},
147 	{ "attachment-point", SATA_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
148 	    ILEVEL_0, devfsadm_rm_all
149 	}
150 };
151 
152 DEVFSADM_REMOVE_INIT_V0(cfg_remove_cbt);
153 
154 static int
155 scsi_cfg_creat_cb(di_minor_t minor, di_node_t node)
156 {
157 	char path[PATH_MAX + 1];
158 	char *c_num = NULL, *devfs_path, *mn;
159 	devfsadm_enumerate_t rules[3] = {
160 	    {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT},
161 	    {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR},
162 	    {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT}
163 	};
164 
165 	mn = di_minor_name(minor);
166 
167 	if ((devfs_path = di_devfs_path(node)) == NULL) {
168 		return (DEVFSADM_CONTINUE);
169 	}
170 	(void) strcpy(path, devfs_path);
171 	(void) strcat(path, ":");
172 	(void) strcat(path, mn);
173 	di_devfs_path_free(devfs_path);
174 
175 	if (devfsadm_enumerate_int(path, 1, &c_num, rules, 3)
176 	    == DEVFSADM_FAILURE) {
177 		/*
178 		 * Unlike the disks module we don't retry on failure.
179 		 * If we have multiple "c" numbers for a single physical
180 		 * controller due to bug 4045879, we will not assign a
181 		 * c-number/symlink for the controller.
182 		 */
183 		return (DEVFSADM_CONTINUE);
184 	}
185 
186 	(void) strcpy(path, CFG_DIRNAME);
187 	(void) strcat(path, "/c");
188 	(void) strcat(path, c_num);
189 
190 	free(c_num);
191 
192 	(void) devfsadm_mklink(path, node, minor, 0);
193 
194 	return (DEVFSADM_CONTINUE);
195 }
196 
197 static int
198 sbd_cfg_creat_cb(di_minor_t minor, di_node_t node)
199 {
200 	char path[PATH_MAX + 1];
201 
202 	(void) strcpy(path, CFG_DIRNAME);
203 	(void) strcat(path, "/");
204 	(void) strcat(path, di_minor_name(minor));
205 	(void) devfsadm_mklink(path, node, minor, 0);
206 	return (DEVFSADM_CONTINUE);
207 }
208 
209 
210 static int
211 usb_cfg_creat_cb(di_minor_t minor, di_node_t node)
212 {
213 	char *cp, path[PATH_MAX + 1];
214 	devfsadm_enumerate_t rules[1] =
215 		{"^cfg$/^usb([0-9]+)$", 1, MATCH_CALLBACK, NULL, get_roothub};
216 
217 	if ((cp = di_devfs_path(node)) == NULL) {
218 		return (DEVFSADM_CONTINUE);
219 	}
220 
221 	(void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
222 	di_devfs_path_free(cp);
223 
224 	if (devfsadm_enumerate_int(path, 0, &cp, rules, 1)) {
225 		return (DEVFSADM_CONTINUE);
226 	}
227 
228 	/* create usbN and the symlink */
229 	(void) snprintf(path, sizeof (path), "%s/usb%s/%s", CFG_DIRNAME, cp,
230 	    di_minor_name(minor));
231 	free(cp);
232 
233 	(void) devfsadm_mklink(path, node, minor, 0);
234 
235 	return (DEVFSADM_CONTINUE);
236 }
237 
238 
239 static int
240 sata_cfg_creat_cb(di_minor_t minor, di_node_t node)
241 {
242 	char path[PATH_MAX + 1], l_path[PATH_MAX], *buf, *devfspath;
243 	char *minor_nm;
244 	devfsadm_enumerate_t rules[1] =
245 		{"^cfg$/^sata([0-9]+)$", 1, MATCH_ADDR};
246 
247 	minor_nm = di_minor_name(minor);
248 	if (minor_nm == NULL)
249 		return (DEVFSADM_CONTINUE);
250 
251 	devfspath = di_devfs_path(node);
252 	if (devfspath == NULL)
253 		return (DEVFSADM_CONTINUE);
254 
255 	(void) strlcpy(path, devfspath, sizeof (path));
256 	(void) strlcat(path, ":", sizeof (path));
257 	(void) strlcat(path, minor_nm, sizeof (path));
258 	di_devfs_path_free(devfspath);
259 
260 	/* build the physical path from the components */
261 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1) ==
262 	    DEVFSADM_FAILURE) {
263 		return (DEVFSADM_CONTINUE);
264 	}
265 
266 	(void) snprintf(l_path, sizeof (l_path), "%s/sata%s/%s", CFG_DIRNAME,
267 			buf, minor_nm);
268 	free(buf);
269 
270 	(void) devfsadm_mklink(l_path, node, minor, 0);
271 
272 	return (DEVFSADM_CONTINUE);
273 }
274 
275 
276 /*
277  * get_roothub:
278  *	figure out the root hub path to calculate /dev/cfg/usbN
279  */
280 /* ARGSUSED */
281 static char *
282 get_roothub(const char *path, void *cb_arg)
283 {
284 	int  i, count = 0;
285 	char *physpath, *cp;
286 
287 	/* make a copy */
288 	if ((physpath = strdup(path)) == NULL) {
289 		return (NULL);
290 	}
291 
292 	/*
293 	 * physpath must always have a minor name component
294 	 */
295 	if ((cp = strrchr(physpath, ':')) == NULL) {
296 		free(physpath);
297 		return (NULL);
298 	}
299 	*cp++ = '\0';
300 
301 	/*
302 	 * No '.' in the minor name indicates a roothub port.
303 	 */
304 	if (strchr(cp, '.') == NULL) {
305 		/* roothub device */
306 		return (physpath);
307 	}
308 
309 	while (*cp) {
310 		if (*cp == '.')
311 			count++;
312 		cp++;
313 	}
314 
315 	/* Remove as many trailing path components as there are '.'s */
316 	for (i = 0; i < count; i++) {
317 		if ((cp = strrchr(physpath, '/')) == NULL || (cp == physpath)) {
318 			free(physpath);
319 			return (NULL);
320 		}
321 		*cp = '\0';
322 	}
323 
324 	return (physpath);
325 }
326 
327 
328 /*
329  * returns an allocted string containing the device path for <node> and
330  * <minor>
331  */
332 static char *
333 pci_cfg_devpath(di_node_t node, di_minor_t minor)
334 {
335 	char *path;
336 	char *bufp;
337 	char *minor_nm;
338 	int buflen;
339 
340 	path = di_devfs_path(node);
341 	minor_nm = di_minor_name(minor);
342 	buflen = snprintf(NULL, 0, "%s:%s", path, minor_nm) + 1;
343 
344 	bufp = malloc(sizeof (char) * buflen);
345 	if (bufp != NULL)
346 		(void) snprintf(bufp, buflen, "%s:%s", path, minor_nm);
347 
348 	di_devfs_path_free(path);
349 	return (bufp);
350 }
351 
352 
353 static int
354 di_propall_lookup_ints(di_prom_handle_t ph, int flags,
355     dev_t dev, di_node_t node, const char *prop_name, int **prop_data)
356 {
357 	int rv;
358 
359 	if (flags & DIPROP_PRI_PROM) {
360 		rv = di_prom_prop_lookup_ints(ph, node, prop_name, prop_data);
361 		if (rv < 0)
362 			rv = di_prop_lookup_ints(dev, node, prop_name,
363 			    prop_data);
364 	} else {
365 		rv = di_prop_lookup_ints(dev, node, prop_name, prop_data);
366 		if (rv < 0)
367 			rv = di_prom_prop_lookup_ints(ph, node, prop_name,
368 			    prop_data);
369 	}
370 	return (rv);
371 }
372 
373 
374 static int
375 di_propall_lookup_strings(di_prom_handle_t ph, int flags,
376     dev_t dev, di_node_t node, const char *prop_name, char **prop_data)
377 {
378 	int rv;
379 
380 	if (flags & DIPROP_PRI_PROM) {
381 		rv = di_prom_prop_lookup_strings(ph, node, prop_name,
382 		    prop_data);
383 		if (rv < 0)
384 			rv = di_prop_lookup_strings(dev, node, prop_name,
385 			    prop_data);
386 	} else {
387 		rv = di_prop_lookup_strings(dev, node, prop_name, prop_data);
388 		if (rv < 0)
389 			rv = di_prom_prop_lookup_strings(ph, node, prop_name,
390 			    prop_data);
391 	}
392 	return (rv);
393 }
394 
395 
396 static di_node_t
397 pci_cfg_chassis_node(di_node_t node, di_prom_handle_t ph)
398 {
399 	di_node_t curnode = node;
400 	int *firstchas;
401 
402 	do {
403 		if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, curnode,
404 		    DI_PROP_FIRST_CHAS, &firstchas) >= 0)
405 			return (curnode);
406 	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
407 
408 	return (DI_NODE_NIL);
409 }
410 
411 
412 static int
413 di_propall_lookup_slot_names(di_prom_handle_t ph, int flags,
414     dev_t dev, di_node_t node, di_slot_name_t **prop_data)
415 {
416 	int rv;
417 
418 	if (flags & DIPROP_PRI_PROM) {
419 		rv = di_prom_prop_lookup_slot_names(ph, node, prop_data);
420 		if (rv < 0)
421 			rv = di_prop_lookup_slot_names(dev, node, prop_data);
422 	} else {
423 		rv = di_prop_lookup_slot_names(dev, node, prop_data);
424 		if (rv < 0)
425 			rv = di_prom_prop_lookup_slot_names(ph, node,
426 			    prop_data);
427 	}
428 	return (rv);
429 }
430 
431 /*
432  * returns an allocated string containing the slot name for the slot with
433  * device number <pci_dev> on bus <node>
434  */
435 static char *
436 pci_cfg_slotname(di_node_t node, di_prom_handle_t ph, minor_t pci_dev)
437 {
438 #ifdef	DEBUG
439 	char *fnm = "pci_cfg_slotname";
440 #endif
441 	int i, count;
442 	char *name = NULL;
443 	di_slot_name_t *slot_names = NULL;
444 
445 	count = di_propall_lookup_slot_names(ph, 0, DDI_DEV_T_ANY, node,
446 	    &slot_names);
447 	if (count < 0)
448 		return (NULL);
449 
450 	for (i = 0; i < count; i++) {
451 		if (slot_names[i].num == (int)pci_dev) {
452 			name = strdup(slot_names[i].name);
453 			break;
454 		}
455 	}
456 #ifdef	DEBUG
457 	if (name == NULL)
458 		dprint(("%s: slot w/ pci_dev %d not found in %s for %s%d\n",
459 		    fnm, (int)pci_dev, DI_PROP_SLOT_NAMES, DRVINST(node)));
460 #endif
461 	if (count > 0)
462 		di_slot_names_free(count, slot_names);
463 	return (name);
464 }
465 
466 
467 /*
468  * returns non-zero if we can return a valid attachment point name for <node>,
469  * for its slot identified by child pci device number <pci_dev>, through <buf>
470  *
471  * prioritized naming scheme:
472  *	1) <DI_PROP_SLOT_NAMES property>    (see pci_cfg_slotname())
473  *	2) <device-type><DI_PROP_PHYS_SLOT property>
474  *	3) <drv name><drv inst>.<device-type><pci_dev>
475  *
476  * where <device-type> is derived from the DI_PROP_DEV_TYPE property:
477  *	if its value is "pciex" then <device-type> is "pcie"
478  *	else the raw value is used
479  *
480  * if <flags> contains APNODE_DEFNAME, then scheme (3) is used
481  */
482 static int
483 pci_cfg_ap_node(minor_t pci_dev, di_node_t node, di_prom_handle_t ph,
484     char *buf, int bufsz, int flags)
485 {
486 	int *nump;
487 	int rv;
488 	char *str, *devtype;
489 
490 	rv = di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, node,
491 	    DI_PROP_DEV_TYPE, &devtype);
492 	if (rv < 1)
493 		return (0);
494 
495 	if (strcmp(devtype, PROPVAL_PCIEX) == 0)
496 		devtype = DEVTYPE_PCIE;
497 
498 	if (flags & APNODE_DEFNAME)
499 		goto DEF;
500 
501 	str = pci_cfg_slotname(node, ph, pci_dev);
502 	if (str != NULL) {
503 		(void) strlcpy(buf, str, bufsz);
504 		free(str);
505 		return (1);
506 	}
507 
508 	if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node,
509 	    DI_PROP_PHYS_SLOT, &nump) > 0) {
510 		if (*nump > 0) {
511 			(void) snprintf(buf, bufsz, "%s%d", devtype, *nump);
512 			return (1);
513 		}
514 	}
515 DEF:
516 	(void) snprintf(buf, bufsz, "%s%d.%s%d",
517 	    di_driver_name(node), di_instance(node), devtype, pci_dev);
518 
519 	return (1);
520 }
521 
522 
523 /*
524  * returns non-zero if we can return a valid expansion chassis name for <node>
525  * through <buf>
526  *
527  * prioritized naming scheme:
528  *	1) <IOB_PRE string><DI_PROP_SERID property: sun specific portion>
529  *	2) <IOB_PRE string><full DI_PROP_SERID property in hex>
530  *	3) <IOB_PRE string>
531  *
532  * DI_PROP_SERID encoding <64-bit int: msb ... lsb>:
533  * <24 bits: IEEE company id><40 bits: serial number>
534  *
535  * sun encoding of 40 bit serial number:
536  * first byte = device type indicator
537  * next 4 bytes = 4 ascii characters
538  *
539  * In the unlikely event that serial id contains non-printable characters
540  * the full 64 bit raw hex string will be used for the attachment point.
541  */
542 /*ARGSUSED*/
543 static int
544 pci_cfg_iob_name(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
545     char *buf, int bufsz)
546 {
547 	int64_t *seridp;
548 	uint64_t serid;
549 	char *idstr;
550 
551 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, DI_PROP_SERID,
552 	    &seridp) < 1) {
553 		(void) strlcpy(buf, IOB_PRE, bufsz);
554 		return (1);
555 	}
556 
557 	serid = (uint64_t)*seridp;
558 
559 	if ((serid >> 40) != (uint64_t)IEEE_SUN_ID ||
560 		!serid_printable(&serid)) {
561 		(void) snprintf(buf, bufsz, "%s%llx", IOB_PRE, serid);
562 		return (1);
563 	}
564 
565 	/*
566 	 * the serial id is constructed from lower 40 bits of the serialid
567 	 * property and is represented by 5 ascii characters. The first
568 	 * character indicates if the IO Box is PCIe or PCI-X.
569 	 */
570 
571 	serid <<= 24;
572 	idstr = (char *)&serid;
573 	idstr[sizeof (serid) -1] = '\0';
574 
575 	(void) snprintf(buf, bufsz, "%s%s", IOB_PRE, idstr);
576 
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, DI_PROP_REG,
591 	    &regp);
592 
593 	if (rv < 1) {
594 		dprint(("pci_cfg_pcidev: property %s not found "
595 		    "for %s%d\n", DI_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 DI_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 DI_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 DI_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, DI_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 		    DI_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 
1178 /*
1179  * This function verifies if the serial id is printable.
1180  */
1181 
1182 static int
1183 serid_printable(uint64_t *seridp)
1184 {
1185 
1186 	char *ptr;
1187 	int i = 0;
1188 
1189 	for (ptr = (char *)seridp+3; i < 5; ptr++, i++)
1190 		if (*ptr < 0x21 || *ptr >= 0x7f)
1191 			return (0);
1192 
1193 	return (1);
1194 
1195 }
1196