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 * Copyright 2016 Nexenta Systems, Inc. All rights reserved. 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Copyright 2026 Oxide Computer Company 27 */ 28 29 #include <devfsadm.h> 30 #include <stdio.h> 31 #include <strings.h> 32 #include <stdlib.h> 33 #include <limits.h> 34 #include <ctype.h> 35 #include <sys/int_fmtio.h> 36 #include <sys/scsi/scsi_address.h> 37 #include <sys/libdevid.h> 38 39 #define SGEN_LINK_RE "^scsi/.+/c[0-9]+t[0-9A-F]+d[0-9]+$" 40 #define SGEN_DIR "scsi" 41 #define SGEN_CLASS "generic-scsi" 42 43 static int sgen_callback(di_minor_t minor, di_node_t node); 44 static char *find_ctrlr(di_node_t node, di_minor_t minor); 45 46 47 static devfsadm_create_t sgen_create_cbt[] = { 48 { SGEN_CLASS, "ddi_generic:scsi", NULL, 49 TYPE_EXACT | CREATE_DEFER, ILEVEL_0, sgen_callback 50 } 51 }; 52 53 DEVFSADM_CREATE_INIT_V0(sgen_create_cbt); 54 55 /* 56 * HOT auto cleanup of sgen links not desired. 57 */ 58 static devfsadm_remove_t sgen_remove_cbt[] = { 59 { SGEN_CLASS, SGEN_LINK_RE, RM_POST, 60 ILEVEL_0, devfsadm_rm_all 61 } 62 }; 63 64 DEVFSADM_REMOVE_INIT_V0(sgen_remove_cbt); 65 66 static int 67 sgen_callback(di_minor_t minor, di_node_t node) 68 { 69 char *baddr, *cnum, *tstr; 70 char lpath[PATH_MAX], buf[PATH_MAX]; 71 uchar_t *wwnstr; 72 char *tgt_port; 73 74 75 if ((cnum = find_ctrlr(node, minor)) == NULL) 76 goto done; 77 78 /* 79 * SCSAv3 attached devices. 80 */ 81 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 82 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) > 0) { 83 uint64_t wwn; 84 scsi_lun64_t sl; 85 scsi_lun_t lun; 86 int64_t lun64; 87 int64_t *lun64p = NULL; 88 int *intp; 89 uchar_t addr_method; 90 91 /* Get lun property */ 92 if ((di_prop_lookup_int64(DDI_DEV_T_ANY, node, 93 SCSI_ADDR_PROP_LUN64, &lun64p) > 0) && 94 (*lun64p != SCSI_LUN64_ILLEGAL)) { 95 lun64 = *lun64p; 96 } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 97 SCSI_ADDR_PROP_LUN, &intp) > 0) { 98 lun64 = (uint64_t)*intp; 99 } else { 100 goto done; 101 } 102 103 lun = scsi_lun64_to_lun(lun64); 104 105 addr_method = (lun.sl_lun1_msb & SCSI_LUN_AM_MASK); 106 107 (void) scsi_wwnstr_to_wwn(tgt_port, &wwn); 108 if ((addr_method == SCSI_LUN_AM_PDEV) && 109 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 110 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 111 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 112 (void) snprintf(lpath, PATH_MAX, 113 "%s/%s/c%st%"PRIX64"d%"PRId64, SGEN_DIR, 114 di_minor_name(minor), cnum, wwn, lun64); 115 } else if ((addr_method == SCSI_LUN_AM_FLAT) && 116 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 117 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 118 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 119 sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb; 120 (void) snprintf(lpath, PATH_MAX, 121 "%s/%s/c%st%"PRIX64"d%"PRIX16, SGEN_DIR, 122 di_minor_name(minor), cnum, wwn, sl); 123 } else { 124 (void) snprintf(lpath, PATH_MAX, 125 "%s/%s/c%st%"PRIX64"d%"PRIX64, SGEN_DIR, 126 di_minor_name(minor), cnum, wwn, lun64); 127 } 128 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 129 "client-guid", (char **)&wwnstr) > 0) { 130 /* 131 * MPXIO-enabled devices; lun is always 0. 132 */ 133 if (strlcpy((char *)buf, (char *)wwnstr, sizeof (buf)) >= 134 sizeof (buf)) 135 goto done; 136 137 for (tstr = buf; *tstr != '\0'; tstr++) { 138 *tstr = toupper(*tstr); 139 } 140 if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%sd0", SGEN_DIR, 141 di_minor_name(minor), cnum, buf) >= sizeof (lpath)) 142 goto done; 143 144 } else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, 145 "port-wwn", &wwnstr) == 8) { 146 /* 147 * "normal" fibre channel devices 148 */ 149 int lun, *lunp, count; 150 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun", &lunp) > 0) 151 lun = *lunp; 152 else 153 lun = 0; 154 155 for (count = 0, tstr = buf; count < 8; count++, tstr += 2) 156 (void) sprintf(tstr, "%02X", wwnstr[count]); 157 158 *tstr = '\0'; 159 if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%sd%d", SGEN_DIR, 160 di_minor_name(minor), cnum, buf, lun) >= sizeof (lpath)) 161 goto done; 162 } else { 163 /* 164 * Parallel SCSI devices 165 */ 166 uint_t targ, lun; 167 168 if ((baddr = di_bus_addr(node)) == NULL) 169 goto done; 170 171 if (sscanf(baddr, "%X,%X", &targ, &lun) != 2) 172 goto done; 173 174 if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%dd%d", SGEN_DIR, 175 di_minor_name(minor), cnum, targ, lun) >= sizeof (lpath)) 176 goto done; 177 } 178 179 (void) devfsadm_mklink(lpath, node, minor, 0); 180 done: 181 free(cnum); 182 return (DEVFSADM_CONTINUE); 183 } 184 185 /* index of enumeration rule applicable to this module */ 186 #define RULE_INDEX 2 187 188 static char * 189 find_ctrlr(di_node_t node, di_minor_t minor) 190 { 191 char path[PATH_MAX + 1]; 192 char *devfspath; 193 char *buf, *mn; 194 boolean_t is_vhci; 195 196 devfsadm_enumerate_t rules[3] = { 197 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT}, 198 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR}, 199 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT} 200 }; 201 202 mn = di_minor_name(minor); 203 204 if ((devfspath = di_devfs_path(node)) == NULL) { 205 return (NULL); 206 } 207 (void) strcpy(path, devfspath); 208 (void) strcat(path, ":"); 209 (void) strcat(path, mn); 210 di_devfs_path_free(devfspath); 211 212 /* 213 * Use controller (parent) component of device path 214 */ 215 is_vhci = (strncmp(path, "/scsi_vhci/", 11) == 0); 216 217 if (ctrl_enumerate_int(path, RULE_INDEX, &buf, rules, 3, 1, is_vhci) == 218 DEVFSADM_MULTIPLE) { 219 /* 220 * We failed because there are multiple logical controller 221 * numbers for a single physical controller. If we use node 222 * name also for DEVICE paths in the match it should fix this 223 * and only find one logical controller. (See 4045879). 224 * NOTE: Rules for controllers are not changed, as there is 225 * no unique controller number for them in this case. 226 * 227 * MATCH_UNCACHED flag is private to the "disks" and "sgen" 228 * modules. NOT to be used by other modules. 229 */ 230 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */ 231 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */ 232 if (ctrl_enumerate_int(path, RULE_INDEX, &buf, rules, 3, 0, 233 is_vhci)) { 234 return (NULL); 235 } 236 } 237 238 return (buf); 239 } 240