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