8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / devfsadm / cfg_link.c
blobffdbc6d0af004b6b1e8d696b6d5736dcba45c4f7
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
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>
41 #ifdef DEBUG
42 #define dprint(args) devfsadm_errprint args
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
48 #define DRVINST(node) di_driver_name(node), di_instance(node)
49 #else
50 #define dprint(args)
51 #endif
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);
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 *);
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 **);
96 * NOTE: The CREATE_DEFER flag is private to this module.
97 * NOT to be used by other modules
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
103 { "attachment-point", DDI_NT_SBD_ATTACHMENT_POINT, NULL,
104 TYPE_EXACT, ILEVEL_0, sbd_cfg_creat_cb
106 { "fc-attachment-point", DDI_NT_FC_ATTACHMENT_POINT, NULL,
107 TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
109 { "attachment-point", DDI_NT_USB_ATTACHMENT_POINT, NULL,
110 TYPE_EXACT, ILEVEL_0, usb_cfg_creat_cb
112 { "attachment-point", DDI_NT_PCI_ATTACHMENT_POINT, NULL,
113 TYPE_EXACT, ILEVEL_0, pci_cfg_creat_cb
115 { "attachment-point", DDI_NT_IB_ATTACHMENT_POINT, NULL,
116 TYPE_EXACT, ILEVEL_0, ib_cfg_creat_cb
118 { "attachment-point", DDI_NT_SATA_ATTACHMENT_POINT, NULL,
119 TYPE_EXACT, ILEVEL_0, sata_cfg_creat_cb
121 { "attachment-point", DDI_NT_SDCARD_ATTACHMENT_POINT, NULL,
122 TYPE_EXACT, ILEVEL_0, sdcard_cfg_creat_cb
126 DEVFSADM_CREATE_INIT_V0(cfg_create_cbt);
128 static devfsadm_remove_t cfg_remove_cbt[] = {
129 { "attachment-point", SCSI_CFG_LINK_RE, RM_POST,
130 ILEVEL_0, devfsadm_rm_all
132 { "attachment-point", SBD_CFG_LINK_RE, RM_POST,
133 ILEVEL_0, devfsadm_rm_all
135 { "fc-attachment-point", SCSI_CFG_LINK_RE, RM_POST,
136 ILEVEL_0, devfsadm_rm_all
138 { "attachment-point", USB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
139 ILEVEL_0, devfsadm_rm_all
141 { "attachment-point", PCI_CFG_LINK_RE, RM_POST,
142 ILEVEL_0, devfsadm_rm_all
144 { "attachment-point", PCI_CFG_PATH_LINK_RE, RM_POST|RM_HOT,
145 ILEVEL_0, pci_cfg_rm_all
147 { "attachment-point", IB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
148 ILEVEL_0, devfsadm_rm_all
150 { "attachment-point", SATA_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
151 ILEVEL_0, devfsadm_rm_all
153 { "attachment-point", SDCARD_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
154 ILEVEL_0, devfsadm_rm_all
158 DEVFSADM_REMOVE_INIT_V0(cfg_remove_cbt);
160 static int
161 scsi_cfg_creat_cb(di_minor_t minor, di_node_t node)
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}
171 mn = di_minor_name(minor);
173 if ((devfs_path = di_devfs_path(node)) == NULL) {
174 return (DEVFSADM_CONTINUE);
176 (void) strcpy(path, devfs_path);
177 (void) strcat(path, ":");
178 (void) strcat(path, mn);
179 di_devfs_path_free(devfs_path);
181 if (ctrl_enumerate_int(path, 1, &c_num, rules, 3, 0, B_FALSE)
182 == DEVFSADM_FAILURE) {
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.
189 return (DEVFSADM_CONTINUE);
192 (void) strcpy(path, CFG_DIRNAME);
193 (void) strcat(path, "/c");
194 (void) strcat(path, c_num);
196 free(c_num);
198 (void) devfsadm_mklink(path, node, minor, 0);
200 return (DEVFSADM_CONTINUE);
203 static int
204 sbd_cfg_creat_cb(di_minor_t minor, di_node_t node)
206 char path[PATH_MAX + 1];
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);
216 static int
217 usb_cfg_creat_cb(di_minor_t minor, di_node_t node)
219 char *cp, path[PATH_MAX + 1];
220 devfsadm_enumerate_t rules[1] =
221 {"^cfg$/^usb([0-9]+)$", 1, MATCH_CALLBACK, NULL, get_roothub};
223 if ((cp = di_devfs_path(node)) == NULL) {
224 return (DEVFSADM_CONTINUE);
227 (void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
228 di_devfs_path_free(cp);
230 if (ctrl_enumerate_int(path, 0, &cp, rules, 1, 0, B_FALSE)) {
231 return (DEVFSADM_CONTINUE);
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);
239 (void) devfsadm_mklink(path, node, minor, 0);
241 return (DEVFSADM_CONTINUE);
245 static int
246 sata_cfg_creat_cb(di_minor_t minor, di_node_t node)
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};
253 minor_nm = di_minor_name(minor);
254 if (minor_nm == NULL)
255 return (DEVFSADM_CONTINUE);
257 devfspath = di_devfs_path(node);
258 if (devfspath == NULL)
259 return (DEVFSADM_CONTINUE);
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);
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);
272 (void) snprintf(l_path, sizeof (l_path), "%s/sata%s/%s", CFG_DIRNAME,
273 buf, minor_nm);
274 free(buf);
276 (void) devfsadm_mklink(l_path, node, minor, 0);
278 return (DEVFSADM_CONTINUE);
281 static int
282 sdcard_cfg_creat_cb(di_minor_t minor, di_node_t node)
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};
289 minor_nm = di_minor_name(minor);
290 if (minor_nm == NULL)
291 return (DEVFSADM_CONTINUE);
293 devfspath = di_devfs_path(node);
294 if (devfspath == NULL)
295 return (DEVFSADM_CONTINUE);
297 (void) snprintf(path, sizeof (path), "%s:%s", devfspath, minor_nm);
298 di_devfs_path_free(devfspath);
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);
306 (void) snprintf(l_path, sizeof (l_path), "%s/sdcard%s/%s",
307 CFG_DIRNAME, buf, minor_nm);
308 free(buf);
310 (void) devfsadm_mklink(l_path, node, minor, 0);
312 return (DEVFSADM_CONTINUE);
316 * get_roothub:
317 * figure out the root hub path to calculate /dev/cfg/usbN
319 /* ARGSUSED */
320 static char *
321 get_roothub(const char *path, void *cb_arg)
323 int i, count = 0;
324 char *physpath, *cp;
326 /* make a copy */
327 if ((physpath = strdup(path)) == NULL) {
328 return (NULL);
332 * physpath must always have a minor name component
334 if ((cp = strrchr(physpath, ':')) == NULL) {
335 free(physpath);
336 return (NULL);
338 *cp++ = '\0';
341 * No '.' in the minor name indicates a roothub port.
343 if (strchr(cp, '.') == NULL) {
344 /* roothub device */
345 return (physpath);
348 while (*cp) {
349 if (*cp == '.')
350 count++;
351 cp++;
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);
361 * Check if there is any usb_mid node in the middle
362 * and remove the node as if there is an extra '.'
364 if (strstr(cp, "miscellaneous") != NULL) {
365 count++;
367 *cp = '\0';
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';
377 return (physpath);
382 * returns an allocted string containing the device path for <node> and
383 * <minor>
385 static char *
386 pci_cfg_devpath(di_node_t node, di_minor_t minor)
388 char *path;
389 char *bufp;
390 char *minor_nm;
391 int buflen;
393 path = di_devfs_path(node);
394 minor_nm = di_minor_name(minor);
395 buflen = snprintf(NULL, 0, "%s:%s", path, minor_nm) + 1;
397 bufp = malloc(sizeof (char) * buflen);
398 if (bufp != NULL)
399 (void) snprintf(bufp, buflen, "%s:%s", path, minor_nm);
401 di_devfs_path_free(path);
402 return (bufp);
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)
410 int rv;
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);
423 return (rv);
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)
431 int rv;
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);
445 return (rv);
449 static di_node_t
450 pci_cfg_chassis_node(di_node_t node, di_prom_handle_t ph)
452 di_node_t curnode = node;
453 int *firstchas;
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);
461 return (DI_NODE_NIL);
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)
469 int rv;
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);
481 return (rv);
485 * returns an allocated string containing the slot name for the slot with
486 * device number <pci_dev> on bus <node>
488 static char *
489 pci_cfg_slotname(di_node_t node, di_prom_handle_t ph, minor_t pci_dev)
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;
498 count = di_propall_lookup_slot_names(ph, 0, DDI_DEV_T_ANY, node,
499 &slot_names);
500 if (count < 0)
501 return (NULL);
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;
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);
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>
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>
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
533 * if <flags> contains APNODE_DEFNAME, then scheme (3) is used
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)
539 int *nump;
540 int rv;
541 char *str, *devtype;
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);
548 if (strcmp(devtype, PROPVAL_PCIEX) == 0)
549 devtype = DEVTYPE_PCIE;
551 if (flags & APNODE_DEFNAME)
552 goto DEF;
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);
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);
568 DEF:
569 (void) snprintf(buf, bufsz, "%s%d.%s%d",
570 di_driver_name(node), di_instance(node), devtype, pci_dev);
572 return (1);
577 * returns non-zero if we can return a valid expansion chassis name for <node>
578 * through <buf>
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>
585 * DI_PROP_SERID encoding <64-bit int: msb ... lsb>:
586 * <24 bits: IEEE company id><40 bits: serial number>
588 * sun encoding of 40 bit serial number:
589 * first byte = device type indicator
590 * next 4 bytes = 4 ascii characters
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.
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)
600 int64_t *seridp;
601 uint64_t serid;
602 char *idstr;
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);
610 serid = (uint64_t)*seridp;
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);
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.
624 serid <<= 24;
625 idstr = (char *)&serid;
626 idstr[sizeof (serid) -1] = '\0';
628 (void) snprintf(buf, bufsz, "%s%s", IOB_PRE, idstr);
630 return (1);
635 * returns the pci device number for <node> if found, else returns PCIDEV_NIL
637 static minor_t
638 pci_cfg_pcidev(di_node_t node, di_prom_handle_t ph)
640 int rv;
641 int *regp;
643 rv = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, DI_PROP_REG,
644 &regp);
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);
652 return (REG_PCIDEV(regp));
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
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()
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
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)
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)
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;
699 buf[0] = '\0';
700 pathbuf[0] = '\0';
701 pathptr = &pathbuf[sizeof (pathbuf) - 1];
702 *pathptr = '\0';
705 * as we traverse up the device tree, we prepend components of our
706 * path inside pathbuf, using pathptr and decrementing
708 pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
709 do {
710 bufptr = buf;
711 bufsz = sizeof (buf);
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;
724 (void) strncat(bufptr, AP_PATH_IOB_SEP, bufsz);
725 len = strlen(bufptr);
726 bufptr += len;
727 bufsz -= len - 1;
729 /* set chasflag when the leaf node is within an iob */
730 if ((curnode == node) != NULL)
731 chasflag = 1;
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;
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
746 if (curnode != node) {
747 pathptr -= seplen;
748 if (pathptr < pathbuf) {
749 pathptr = pathbuf;
750 *pathptr = '\0';
751 goto DEF;
753 (void) memcpy(pathptr, AP_PATH_SEP, seplen);
755 len = strlen(buf);
756 pathptr -= len;
757 if (pathptr < pathbuf) {
758 pathptr = pathbuf;
759 *pathptr = '\0';
760 goto DEF;
762 (void) memcpy(pathptr, buf, len);
764 /* remember the leaf component */
765 if (curnode == node)
766 pathend = pathptr;
769 * go no further than the hosts' onboard slots
771 if (chasnode == DI_NODE_NIL)
772 break;
773 curnode = chasnode;
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
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;
787 } while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
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;
796 DEF:
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
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;
813 * if our name still won't fit <ap_pathsz>, then use the leaf <node>'s
814 * default name
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;
824 if (strlen(buf) < ap_pathsz) {
825 (void) strlcpy(ap_path, buf, ap_pathsz);
826 rv = 1;
827 goto OUT;
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
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
843 rv = 0;
844 dprint(("%s: cannot create apid for %s%d within length of %d\n",
845 fnm, DRVINST(node), ap_pathsz));
847 OUT:
848 ap_path[ap_pathsz - 1] = '\0';
849 *pathret = (*pathptr == '\0') ? NULL : strdup(pathptr);
850 return (rv);
852 #undef seplen
853 #undef iob_pre_len
854 #undef ap_path_iob_sep_len
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()
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
866 * returns non-zero if we return a valid attachment point through <path>
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)
872 minor_t pci_dev;
873 int *anp;
875 if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, DI_PROP_AP_NAMES,
876 &anp) < 1)
877 return (0);
879 pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
880 if ((*anp & (1 << pci_dev)) == 0)
881 return (0);
883 (void) strlcpy(ap_path, di_minor_name(minor), ap_pathsz);
884 return (1);
889 * determine if <node> qualifies for a path style apid
891 static int
892 pci_cfg_is_ap_path(di_node_t node, di_prom_handle_t ph)
894 char *devtype;
895 di_node_t curnode = node;
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);
904 return (0);
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
913 * data format: "Location: <transformed path>"
914 * where <transformed path> is <path> with occurrances of AP_PATH_SEP
915 * replaced by "/"
917 static char *
918 pci_cfg_info_data(char *path)
920 #define head "Location: "
921 #define headlen (sizeof (head) - 1)
922 #define seplen (sizeof (AP_PATH_SEP) - 1)
924 char *sep, *prev, *np;
925 char *newpath;
926 int pathlen = strlen(path);
927 int len;
929 newpath = malloc(sizeof (char) * (headlen + pathlen + 1));
930 np = newpath;
931 (void) strcpy(np, head);
932 np += headlen;
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;
942 (void) strcpy(np, prev);
943 return (newpath);
945 #undef head
946 #undef headlen
947 #undef seplen
951 static void
952 pci_cfg_rm_link(char *file)
954 char *dlipath;
956 dlipath = di_dli_name(file);
957 (void) unlink(dlipath);
959 devfsadm_rm_all(file);
960 free(dlipath);
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
968 static void
969 pci_cfg_rm_invalid_links(char *physpath, char *valid)
971 char **dnp;
972 char *cp, *vcp;
973 int i, dnlen;
975 dnp = devfsadm_lookup_dev_names(physpath, NULL, &dnlen);
976 if (dnp == NULL)
977 return;
979 if (valid != NULL) {
980 if (strncmp(valid, DEV "/", DEV_LEN + 1) == 0)
981 vcp = valid + DEV_LEN + 1;
982 else
983 vcp = valid;
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];
992 if (valid != NULL) {
993 if (strcmp(vcp, cp) == 0)
994 continue;
996 pci_cfg_rm_link(cp);
998 devfsadm_free_dev_names(dnp, dnlen);
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
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
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
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)
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;
1025 *ret_node = DI_NODE_NIL;
1026 *ret_minor = DI_MINOR_NIL;
1028 root_node = di_init("/", DINFOCPYALL);
1029 if (root_node == DI_NODE_NIL)
1030 return (DI_NODE_NIL);
1033 * narrow down search by driver, then instance, then minor
1035 if (pci_node == DI_NODE_NIL)
1036 return (root_node);
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;
1045 } while ((node = di_drv_next_node(node)) != DI_NODE_NIL);
1047 if (node == DI_NODE_NIL)
1048 return (root_node);
1051 * found node, now search minors
1053 if (pci_minor == DI_MINOR_NIL)
1054 return (root_node);
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;
1064 return (root_node);
1068 static int
1069 pci_cfg_creat_cb(di_minor_t pci_minor, di_node_t pci_node)
1071 #ifdef DEBUG
1072 char *fnm = "pci_cfg_creat_cb";
1073 #endif
1074 #define ap_pathsz (sizeof (ap_path))
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;
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;
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
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;
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;
1114 (void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
1115 CFG_DIRNAME, ap_path);
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.
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
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);
1137 (void) devfsadm_mklink(linkbuf, node, minor, 0);
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)
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)
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
1153 pathinfo = pci_cfg_info_data(fullpath);
1154 fd = di_dli_openw(linkbuf);
1155 if (fd < 0)
1156 goto OUT;
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;
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;
1172 (void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
1173 CFG_DIRNAME, ap_path);
1174 (void) devfsadm_mklink(linkbuf, node, minor, 0);
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);
1190 #undef ap_pathsz
1194 static void
1195 pci_cfg_rm_all(char *file)
1197 pci_cfg_rm_link(file);
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>
1206 static int
1207 ib_cfg_creat_cb(di_minor_t minor, di_node_t node)
1209 char *cp;
1210 char path[PATH_MAX + 1];
1212 if ((cp = di_devfs_path(node)) == NULL) {
1213 return (DEVFSADM_CONTINUE);
1216 (void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
1217 di_devfs_path_free(cp);
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));
1227 (void) devfsadm_mklink(path, node, minor, 0);
1228 return (DEVFSADM_CONTINUE);
1232 * This function verifies if the serial id is printable.
1235 static int
1236 serid_printable(uint64_t *seridp)
1239 char *ptr;
1240 int i = 0;
1242 for (ptr = (char *)seridp+3; i < 5; ptr++, i++)
1243 if (*ptr < 0x21 || *ptr >= 0x7f)
1244 return (0);
1246 return (1);