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