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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/systm.h> 30 #include <sys/pathname.h> 31 #include <sys/modctl.h> 32 #include <sys/sunndi.h> 33 #include <sys/sunmdi.h> 34 #include <sys/mdi_impldefs.h> 35 #include <sys/promif.h> 36 37 struct parinfo { 38 dev_info_t *dip; 39 mdi_pathinfo_t *pip; 40 dev_info_t *pdip; 41 }; 42 43 /* 44 * internal functions 45 */ 46 static int resolve_devfs_name(char *, char *); 47 static dev_info_t *find_alternate_node(dev_info_t *, major_t); 48 static dev_info_t *get_path_parent(dev_info_t *, struct parinfo *); 49 50 /* internal global data */ 51 static struct modlmisc modlmisc = { 52 &mod_miscops, "bootdev misc module 1.21" 53 }; 54 55 static struct modlinkage modlinkage = { 56 MODREV_1, (void *)&modlmisc, NULL 57 }; 58 59 int 60 _init() 61 { 62 return (mod_install(&modlinkage)); 63 } 64 65 int 66 _fini() 67 { 68 return (mod_remove(&modlinkage)); 69 } 70 71 int 72 _info(struct modinfo *modinfop) 73 { 74 return (mod_info(&modlinkage, modinfop)); 75 } 76 77 /* 78 * convert a prom device path to an equivalent path in /devices 79 * Does not deal with aliases. Does deal with pathnames which 80 * are not fully qualified. This routine is generalized 81 * to work across several flavors of OBP 82 */ 83 int 84 i_promname_to_devname(char *prom_name, char *ret_buf) 85 { 86 if (prom_name == NULL || ret_buf == NULL || 87 (strlen(prom_name) >= MAXPATHLEN)) { 88 return (EINVAL); 89 } 90 if (i_ddi_prompath_to_devfspath(prom_name, ret_buf) != DDI_SUCCESS) 91 return (EINVAL); 92 93 return (0); 94 } 95 96 /* 97 * translate a devfs pathname to one that will be acceptable 98 * by the prom. In most cases, there is no translation needed. 99 * For systems supporting generically named devices, the prom 100 * may support nodes such as 'disk' that do not have any unit 101 * address information (i.e. target,lun info). If this is the 102 * case, the ddi framework will reject the node as invalid and 103 * populate the devinfo tree with nodes froms the .conf file 104 * (e.g. sd). In this case, the names that show up in /devices 105 * are sd - since the prom only knows about 'disk' nodes, this 106 * routine detects this situation and does the conversion 107 * There are also cases such as pluto where the disk node in the 108 * prom is named "SUNW,ssd" but in /devices the name is "ssd". 109 * 110 * If MPxIO is enabled, the translation involves following 111 * pathinfo nodes to the "best" parent. See get_obp_parent(). 112 * 113 * return a 0 on success with the new device string in ret_buf. 114 * Otherwise return the appropriate error code as we may be called 115 * from the openprom driver. 116 */ 117 int 118 i_devname_to_promname(char *dev_name, char *ret_buf, size_t len) 119 { 120 dev_info_t *dip, *pdip, *cdip, *idip; 121 char *dev_path, *prom_path; 122 char *unit_address, *minorname, *nodename; 123 major_t major; 124 int depth, old_depth; 125 struct parinfo *parinfo; 126 struct parinfo *info; 127 char *rptr, *optr, *offline; 128 size_t olen, rlen; 129 130 /* do some sanity checks */ 131 if ((dev_name == NULL) || (ret_buf == NULL) || 132 (strlen(dev_name) > MAXPATHLEN)) { 133 return (EINVAL); 134 } 135 136 /* 137 * Convert to a /devices name. Fail the translation if 138 * the name doesn't exist. 139 */ 140 dev_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 141 if (resolve_devfs_name(dev_name, dev_path) != 0 || 142 strncmp(dev_path, "/devices/", 9) != 0) { 143 kmem_free(dev_path, MAXPATHLEN); 144 return (EINVAL); 145 } 146 dev_name = dev_path + sizeof ("/devices") - 1; 147 148 bzero(ret_buf, len); 149 150 if (prom_finddevice(dev_name) != OBP_BADNODE) { 151 /* we are done */ 152 (void) snprintf(ret_buf, len, "%s", dev_name); 153 kmem_free(dev_path, MAXPATHLEN); 154 return (0); 155 } 156 157 /* 158 * if we get here, then some portion of the device path is 159 * not understood by the prom. We need to look for alternate 160 * names (e.g. replace ssd by disk) and mpxio enabled devices. 161 */ 162 dip = e_ddi_hold_devi_by_path(dev_name, 0); 163 if (dip == NULL) { 164 cmn_err(CE_NOTE, "cannot find dip for %s", dev_name); 165 kmem_free(dev_path, MAXPATHLEN); 166 return (EINVAL); 167 } 168 169 /* find the closest ancestor which is a prom node */ 170 pdip = dip; 171 parinfo = kmem_alloc(OBP_STACKDEPTH * sizeof (*parinfo), KM_SLEEP); 172 for (depth = 0; ndi_dev_is_prom_node(pdip) == 0; depth++) { 173 if (depth == OBP_STACKDEPTH) { 174 kmem_free(dev_path, MAXPATHLEN); 175 kmem_free(parinfo, OBP_STACKDEPTH * sizeof (*parinfo)); 176 return (EINVAL); /* must not have been an obp node */ 177 } 178 179 pdip = get_path_parent(pdip, &parinfo[depth]); 180 } 181 ASSERT(pdip); /* at least root is prom node */ 182 ASSERT(depth > 0); 183 184 prom_path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 185 186 offline = kmem_zalloc(len, KM_SLEEP); /* offline paths */ 187 olen = len; 188 rlen = len; 189 190 rptr = ret_buf; 191 optr = offline; 192 193 old_depth = depth; 194 do { 195 bzero(prom_path, MAXPATHLEN); 196 if (pdip) 197 (void) ddi_pathname(pdip, prom_path); 198 199 ndi_hold_devi(pdip); 200 for (depth = old_depth; depth > 0; depth--) { 201 info = &parinfo[depth - 1]; 202 idip = info->dip; 203 204 nodename = ddi_node_name(idip); 205 if (info->pip) { 206 unit_address = MDI_PI(info->pip)->pi_addr; 207 } else { 208 unit_address = ddi_get_name_addr(idip); 209 } 210 211 if (pdip) { 212 major = ddi_driver_major(idip); 213 cdip = find_alternate_node(pdip, major); 214 ndi_rele_devi(pdip); 215 if (cdip) { 216 nodename = ddi_node_name(cdip); 217 } 218 } 219 220 /* 221 * node name + unitaddr to the prom_path 222 */ 223 (void) strcat(prom_path, "/"); 224 (void) strcat(prom_path, nodename); 225 if (unit_address && (*unit_address)) { 226 (void) strcat(prom_path, "@"); 227 (void) strcat(prom_path, unit_address); 228 } 229 pdip = cdip; 230 } 231 232 if (pdip) { 233 ndi_rele_devi(pdip); /* hold from find_alternate_node */ 234 } 235 236 minorname = strrchr(dev_name, ':'); 237 if (minorname && (minorname[1] != '\0')) { 238 (void) strcat(prom_path, minorname); 239 } 240 241 if (!info || !info->pip || MDI_PI_IS_ONLINE(info->pip)) { 242 (void) snprintf(rptr, rlen, "%s", prom_path); 243 rlen -= strlen(rptr) + 1; 244 rptr += strlen(rptr) + 1; 245 if (rlen <= 0) /* drop paths we can't store */ 246 break; 247 } else { /* path is offline */ 248 (void) snprintf(optr, olen, "%s", prom_path); 249 olen -= strlen(optr) + 1; 250 if (olen > 0) /* drop paths we can't store */ 251 optr += strlen(optr) + 1; 252 253 } 254 255 /* 256 * The following code assumes that the phci client is at leaf 257 * level and that all phci nodes are prom nodes. 258 */ 259 info = &parinfo[0]; 260 if (info && info->dip && info->pip) { 261 info->pip = 262 (mdi_pathinfo_t *)MDI_PI(info->pip)->pi_client_link; 263 if (info->pip) { 264 pdip = mdi_pi_get_phci(info->pip); 265 pdip = ddi_get_parent(pdip); 266 } else { 267 break; 268 } 269 } else { 270 break; 271 } 272 273 } while (info && info->pip && pdip); 274 275 ndi_rele_devi(dip); /* release hold from e_ddi_hold_devi_by_path() */ 276 277 /* release holds from get_path_parent() */ 278 for (depth = old_depth; depth > 0; depth--) { 279 info = &parinfo[depth - 1]; 280 281 /* replace with mdi_rele_path() when mpxio goes into genunix */ 282 if (info && info->pip) { 283 MDI_PI_LOCK(info->pip); 284 MDI_PI_RELE(info->pip); 285 if (MDI_PI(info->pip)->pi_ref_cnt == 0) 286 cv_broadcast(&MDI_PI(info->pip)->pi_ref_cv); 287 MDI_PI_UNLOCK(info->pip); 288 } 289 if (info && info->pdip) 290 ndi_rele_devi(info->pdip); 291 } 292 293 /* now add as much of offline to ret_buf as possible */ 294 bcopy(offline, rptr, rlen); 295 296 ret_buf[len - 1] = '\0'; 297 ret_buf[len - 2] = '\0'; 298 299 kmem_free(offline, len); 300 kmem_free(dev_path, MAXPATHLEN); 301 kmem_free(prom_path, MAXPATHLEN); 302 kmem_free(parinfo, OBP_STACKDEPTH * sizeof (*parinfo)); 303 return (0); 304 } 305 306 /* 307 * check for a possible substitute node. This routine searches the 308 * children of parent_dip, looking for a node that: 309 * 1. is a prom node 310 * 2. binds to the same major number 311 * 3. there is no need to verify that the unit-address information 312 * match since it is likely that the substitute node 313 * will have none (e.g. disk) - this would be the reason the 314 * framework rejected it in the first place. 315 * 316 * assumes parent_dip is held 317 */ 318 static dev_info_t * 319 find_alternate_node(dev_info_t *parent_dip, major_t major) 320 { 321 int circ; 322 dev_info_t *child_dip; 323 324 /* lock down parent to keep children from being removed */ 325 ndi_devi_enter(parent_dip, &circ); 326 for (child_dip = ddi_get_child(parent_dip); child_dip != NULL; 327 child_dip = ddi_get_next_sibling(child_dip)) { 328 329 /* look for obp node with matching major */ 330 if ((ndi_dev_is_prom_node(child_dip) != 0) && 331 (ddi_driver_major(child_dip) == major)) { 332 ndi_hold_devi(child_dip); 333 break; 334 } 335 } 336 ndi_devi_exit(parent_dip, circ); 337 return (child_dip); 338 } 339 340 /* 341 * given an absolute pathname, convert it, if possible, to a devfs 342 * name. Examples: 343 * /dev/rsd3a to /pci@1f,4000/glm@3/sd@0,0:a 344 * /dev/dsk/c0t0d0s0 to /pci@1f,4000/glm@3/sd@0,0:a 345 * /devices/pci@1f,4000/glm@3/sd@0,0:a to /pci@1f,4000/glm@3/sd@0,0:a 346 * /pci@1f,4000/glm@3/sd@0,0:a unchanged 347 * 348 * This routine deals with symbolic links, physical pathname with and 349 * without /devices stripped. Returns 0 on success or -1 on failure. 350 */ 351 static int 352 resolve_devfs_name(char *name, char *buffer) 353 { 354 int error; 355 char *fullname = NULL; 356 struct pathname pn, rpn; 357 358 /* if not a /dev or /device name, prepend /devices */ 359 if (strncmp(name, "/dev/", 5) != 0 && 360 strncmp(name, "/devices/", 9) != 0) { 361 fullname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 362 (void) snprintf(fullname, MAXPATHLEN, "/devices%s", name); 363 name = fullname; 364 } 365 366 if (pn_get(name, UIO_SYSSPACE, &pn) != 0) { 367 if (fullname) 368 kmem_free(fullname, MAXPATHLEN); 369 return (-1); 370 } 371 372 pn_alloc(&rpn); 373 error = lookuppn(&pn, &rpn, FOLLOW, NULL, NULL); 374 if (error == 0) 375 bcopy(rpn.pn_path, buffer, rpn.pn_pathlen); 376 377 pn_free(&pn); 378 pn_free(&rpn); 379 if (fullname) 380 kmem_free(fullname, MAXPATHLEN); 381 382 return (error); 383 } 384 385 /* 386 * If bootstring contains a device path, we need to convert to a format 387 * the prom will understand. To do so, we convert the existing path to 388 * a prom-compatible path and return the value of new_path. If the 389 * caller specifies new_path as NULL, we allocate an appropriately 390 * sized new_path on behalf of the caller. If the caller invokes this 391 * function with new_path = NULL, they must do so from a context in 392 * which it is safe to perform a sleeping memory allocation. 393 */ 394 char * 395 i_convert_boot_device_name(char *cur_path, char *new_path, size_t *len) 396 { 397 char *ptr; 398 int rval; 399 400 ASSERT(cur_path != NULL && len != NULL); 401 ASSERT(new_path == NULL || *len >= MAXPATHLEN); 402 403 if (new_path == NULL) { 404 *len = MAXPATHLEN + MAXNAMELEN; 405 new_path = kmem_alloc(*len, KM_SLEEP); 406 } 407 408 if ((ptr = strchr(cur_path, ' ')) != NULL) 409 *ptr = '\0'; 410 411 rval = i_devname_to_promname(cur_path, new_path, *len); 412 413 if (ptr != NULL) 414 *ptr = ' '; 415 416 if (rval == 0) { 417 if (ptr != NULL) { 418 (void) snprintf(new_path + strlen(new_path), 419 *len - strlen(new_path), "%s", ptr); 420 } 421 } else { /* the conversion failed */ 422 (void) snprintf(new_path, *len, "%s", cur_path); 423 } 424 425 return (new_path); 426 } 427 428 /* 429 * Get the parent dip. If dip is mpxio client, get the first online 430 * path and return the phci dip with both pathinfo and dip held 431 * otherwise just return with the dip held. 432 */ 433 static dev_info_t * 434 get_path_parent(dev_info_t *dip, struct parinfo *info) 435 { 436 dev_info_t *pdip; 437 mdi_pathinfo_t *pip; 438 int circ; 439 440 if (!MDI_CLIENT(dip)) { 441 pdip = ddi_get_parent(dip); 442 pip = NULL; 443 goto finish; 444 } 445 446 /* find and hold the pathinfo */ 447 448 ndi_devi_enter(dip, &circ); 449 pip = mdi_get_next_phci_path(dip, NULL); 450 451 if (pip == NULL) { 452 ndi_devi_exit(dip, circ); 453 return (NULL); 454 } 455 456 /* replace with mdi_hold_path() when mpxio goes into genunix */ 457 MDI_PI_LOCK(pip); 458 MDI_PI_HOLD(pip); 459 MDI_PI_UNLOCK(pip); 460 461 ndi_devi_exit(dip, circ); 462 pdip = mdi_pi_get_phci(pip); 463 464 finish: 465 ndi_hold_devi(pdip); 466 467 info->dip = dip; 468 info->pip = pip; 469 info->pdip = pdip; 470 return (pdip); 471 } 472