10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*7413SJaven.Wu@Sun.COM * Common Development and Distribution License (the "License").
6*7413SJaven.Wu@Sun.COM * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*7413SJaven.Wu@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #include <sys/systm.h>
270Sstevel@tonic-gate #include <sys/pathname.h>
280Sstevel@tonic-gate #include <sys/modctl.h>
290Sstevel@tonic-gate #include <sys/sunndi.h>
300Sstevel@tonic-gate #include <sys/sunmdi.h>
310Sstevel@tonic-gate #include <sys/mdi_impldefs.h>
320Sstevel@tonic-gate #include <sys/promif.h>
330Sstevel@tonic-gate
340Sstevel@tonic-gate struct parinfo {
350Sstevel@tonic-gate dev_info_t *dip;
360Sstevel@tonic-gate dev_info_t *pdip;
370Sstevel@tonic-gate };
380Sstevel@tonic-gate
390Sstevel@tonic-gate /*
400Sstevel@tonic-gate * internal functions
410Sstevel@tonic-gate */
420Sstevel@tonic-gate static int resolve_devfs_name(char *, char *);
430Sstevel@tonic-gate static dev_info_t *find_alternate_node(dev_info_t *, major_t);
44*7413SJaven.Wu@Sun.COM static dev_info_t *get_parent(dev_info_t *, struct parinfo *);
45*7413SJaven.Wu@Sun.COM static int i_devi_to_promname(dev_info_t *, char *, dev_info_t **alt_dipp);
460Sstevel@tonic-gate
470Sstevel@tonic-gate /* internal global data */
480Sstevel@tonic-gate static struct modlmisc modlmisc = {
49*7413SJaven.Wu@Sun.COM &mod_miscops, "bootdev misc module 1.22"
500Sstevel@tonic-gate };
510Sstevel@tonic-gate
520Sstevel@tonic-gate static struct modlinkage modlinkage = {
530Sstevel@tonic-gate MODREV_1, (void *)&modlmisc, NULL
540Sstevel@tonic-gate };
550Sstevel@tonic-gate
560Sstevel@tonic-gate int
_init()570Sstevel@tonic-gate _init()
580Sstevel@tonic-gate {
590Sstevel@tonic-gate return (mod_install(&modlinkage));
600Sstevel@tonic-gate }
610Sstevel@tonic-gate
620Sstevel@tonic-gate int
_fini()630Sstevel@tonic-gate _fini()
640Sstevel@tonic-gate {
650Sstevel@tonic-gate return (mod_remove(&modlinkage));
660Sstevel@tonic-gate }
670Sstevel@tonic-gate
680Sstevel@tonic-gate int
_info(struct modinfo * modinfop)690Sstevel@tonic-gate _info(struct modinfo *modinfop)
700Sstevel@tonic-gate {
710Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
720Sstevel@tonic-gate }
730Sstevel@tonic-gate
740Sstevel@tonic-gate /*
750Sstevel@tonic-gate * convert a prom device path to an equivalent path in /devices
760Sstevel@tonic-gate * Does not deal with aliases. Does deal with pathnames which
770Sstevel@tonic-gate * are not fully qualified. This routine is generalized
780Sstevel@tonic-gate * to work across several flavors of OBP
790Sstevel@tonic-gate */
800Sstevel@tonic-gate int
i_promname_to_devname(char * prom_name,char * ret_buf)810Sstevel@tonic-gate i_promname_to_devname(char *prom_name, char *ret_buf)
820Sstevel@tonic-gate {
830Sstevel@tonic-gate if (prom_name == NULL || ret_buf == NULL ||
840Sstevel@tonic-gate (strlen(prom_name) >= MAXPATHLEN)) {
850Sstevel@tonic-gate return (EINVAL);
860Sstevel@tonic-gate }
870Sstevel@tonic-gate if (i_ddi_prompath_to_devfspath(prom_name, ret_buf) != DDI_SUCCESS)
880Sstevel@tonic-gate return (EINVAL);
890Sstevel@tonic-gate
900Sstevel@tonic-gate return (0);
910Sstevel@tonic-gate }
920Sstevel@tonic-gate
930Sstevel@tonic-gate /*
94*7413SJaven.Wu@Sun.COM * The function is to get prom name according non-client dip node.
95*7413SJaven.Wu@Sun.COM * And the function will set the alternate node of dip to alt_dip
96*7413SJaven.Wu@Sun.COM * if it is exist which must be PROM node.
97*7413SJaven.Wu@Sun.COM */
98*7413SJaven.Wu@Sun.COM static int
i_devi_to_promname(dev_info_t * dip,char * prom_path,dev_info_t ** alt_dipp)99*7413SJaven.Wu@Sun.COM i_devi_to_promname(dev_info_t *dip, char *prom_path, dev_info_t **alt_dipp)
100*7413SJaven.Wu@Sun.COM {
101*7413SJaven.Wu@Sun.COM dev_info_t *pdip, *cdip, *idip;
102*7413SJaven.Wu@Sun.COM char *unit_address, *nodename;
103*7413SJaven.Wu@Sun.COM major_t major;
104*7413SJaven.Wu@Sun.COM int depth, old_depth = 0;
105*7413SJaven.Wu@Sun.COM struct parinfo *parinfo = NULL;
106*7413SJaven.Wu@Sun.COM struct parinfo *info;
107*7413SJaven.Wu@Sun.COM int ret = 0;
108*7413SJaven.Wu@Sun.COM
109*7413SJaven.Wu@Sun.COM if (MDI_CLIENT(dip))
110*7413SJaven.Wu@Sun.COM return (EINVAL);
111*7413SJaven.Wu@Sun.COM
112*7413SJaven.Wu@Sun.COM if (ddi_pathname_obp(dip, prom_path) != NULL) {
113*7413SJaven.Wu@Sun.COM return (0);
114*7413SJaven.Wu@Sun.COM }
115*7413SJaven.Wu@Sun.COM /*
116*7413SJaven.Wu@Sun.COM * ddi_pathname_obp return NULL, but the obp path still could
117*7413SJaven.Wu@Sun.COM * be different with the devfs path name, so need use a parents
118*7413SJaven.Wu@Sun.COM * stack to compose the path name string layer by layer.
119*7413SJaven.Wu@Sun.COM */
120*7413SJaven.Wu@Sun.COM
121*7413SJaven.Wu@Sun.COM /* find the closest ancestor which is a prom node */
122*7413SJaven.Wu@Sun.COM pdip = dip;
123*7413SJaven.Wu@Sun.COM parinfo = kmem_alloc(OBP_STACKDEPTH * sizeof (*parinfo),
124*7413SJaven.Wu@Sun.COM KM_SLEEP);
125*7413SJaven.Wu@Sun.COM for (depth = 0; ndi_dev_is_prom_node(pdip) == 0; depth++) {
126*7413SJaven.Wu@Sun.COM if (depth == OBP_STACKDEPTH) {
127*7413SJaven.Wu@Sun.COM ret = EINVAL;
128*7413SJaven.Wu@Sun.COM /* must not have been an obp node */
129*7413SJaven.Wu@Sun.COM goto out;
130*7413SJaven.Wu@Sun.COM }
131*7413SJaven.Wu@Sun.COM pdip = get_parent(pdip, &parinfo[depth]);
132*7413SJaven.Wu@Sun.COM }
133*7413SJaven.Wu@Sun.COM old_depth = depth;
134*7413SJaven.Wu@Sun.COM ASSERT(pdip); /* at least root is prom node */
135*7413SJaven.Wu@Sun.COM if (pdip)
136*7413SJaven.Wu@Sun.COM (void) ddi_pathname(pdip, prom_path);
137*7413SJaven.Wu@Sun.COM
138*7413SJaven.Wu@Sun.COM ndi_hold_devi(pdip);
139*7413SJaven.Wu@Sun.COM
140*7413SJaven.Wu@Sun.COM for (depth = old_depth; depth > 0; depth--) {
141*7413SJaven.Wu@Sun.COM info = &parinfo[depth - 1];
142*7413SJaven.Wu@Sun.COM idip = info->dip;
143*7413SJaven.Wu@Sun.COM nodename = ddi_node_name(idip);
144*7413SJaven.Wu@Sun.COM unit_address = ddi_get_name_addr(idip);
145*7413SJaven.Wu@Sun.COM
146*7413SJaven.Wu@Sun.COM if (pdip) {
147*7413SJaven.Wu@Sun.COM major = ddi_driver_major(idip);
148*7413SJaven.Wu@Sun.COM cdip = find_alternate_node(pdip, major);
149*7413SJaven.Wu@Sun.COM ndi_rele_devi(pdip);
150*7413SJaven.Wu@Sun.COM if (cdip) {
151*7413SJaven.Wu@Sun.COM nodename = ddi_node_name(cdip);
152*7413SJaven.Wu@Sun.COM }
153*7413SJaven.Wu@Sun.COM }
154*7413SJaven.Wu@Sun.COM
155*7413SJaven.Wu@Sun.COM /*
156*7413SJaven.Wu@Sun.COM * node name + unitaddr to the prom_path
157*7413SJaven.Wu@Sun.COM */
158*7413SJaven.Wu@Sun.COM (void) strcat(prom_path, "/");
159*7413SJaven.Wu@Sun.COM (void) strcat(prom_path, nodename);
160*7413SJaven.Wu@Sun.COM if (unit_address && (*unit_address)) {
161*7413SJaven.Wu@Sun.COM (void) strcat(prom_path, "@");
162*7413SJaven.Wu@Sun.COM (void) strcat(prom_path, unit_address);
163*7413SJaven.Wu@Sun.COM }
164*7413SJaven.Wu@Sun.COM pdip = cdip;
165*7413SJaven.Wu@Sun.COM }
166*7413SJaven.Wu@Sun.COM
167*7413SJaven.Wu@Sun.COM if (pdip) {
168*7413SJaven.Wu@Sun.COM ndi_rele_devi(pdip); /* hold from find_alternate_node */
169*7413SJaven.Wu@Sun.COM }
170*7413SJaven.Wu@Sun.COM /*
171*7413SJaven.Wu@Sun.COM * Now pdip is the alternate node which is same hierarchy as dip
172*7413SJaven.Wu@Sun.COM * if it exists.
173*7413SJaven.Wu@Sun.COM */
174*7413SJaven.Wu@Sun.COM *alt_dipp = pdip;
175*7413SJaven.Wu@Sun.COM out:
176*7413SJaven.Wu@Sun.COM if (parinfo) {
177*7413SJaven.Wu@Sun.COM /* release holds from get_parent() */
178*7413SJaven.Wu@Sun.COM for (depth = old_depth; depth > 0; depth--) {
179*7413SJaven.Wu@Sun.COM info = &parinfo[depth - 1];
180*7413SJaven.Wu@Sun.COM if (info && info->pdip)
181*7413SJaven.Wu@Sun.COM ndi_rele_devi(info->pdip);
182*7413SJaven.Wu@Sun.COM }
183*7413SJaven.Wu@Sun.COM kmem_free(parinfo, OBP_STACKDEPTH * sizeof (*parinfo));
184*7413SJaven.Wu@Sun.COM }
185*7413SJaven.Wu@Sun.COM return (ret);
186*7413SJaven.Wu@Sun.COM }
187*7413SJaven.Wu@Sun.COM
188*7413SJaven.Wu@Sun.COM /*
1890Sstevel@tonic-gate * translate a devfs pathname to one that will be acceptable
1900Sstevel@tonic-gate * by the prom. In most cases, there is no translation needed.
1910Sstevel@tonic-gate * For systems supporting generically named devices, the prom
1920Sstevel@tonic-gate * may support nodes such as 'disk' that do not have any unit
1930Sstevel@tonic-gate * address information (i.e. target,lun info). If this is the
1940Sstevel@tonic-gate * case, the ddi framework will reject the node as invalid and
1950Sstevel@tonic-gate * populate the devinfo tree with nodes froms the .conf file
1960Sstevel@tonic-gate * (e.g. sd). In this case, the names that show up in /devices
1970Sstevel@tonic-gate * are sd - since the prom only knows about 'disk' nodes, this
1980Sstevel@tonic-gate * routine detects this situation and does the conversion
1990Sstevel@tonic-gate * There are also cases such as pluto where the disk node in the
2000Sstevel@tonic-gate * prom is named "SUNW,ssd" but in /devices the name is "ssd".
2010Sstevel@tonic-gate *
2020Sstevel@tonic-gate * If MPxIO is enabled, the translation involves following
203*7413SJaven.Wu@Sun.COM * pathinfo nodes to the "best" parent.
2040Sstevel@tonic-gate *
2050Sstevel@tonic-gate * return a 0 on success with the new device string in ret_buf.
2060Sstevel@tonic-gate * Otherwise return the appropriate error code as we may be called
2070Sstevel@tonic-gate * from the openprom driver.
2080Sstevel@tonic-gate */
2090Sstevel@tonic-gate int
i_devname_to_promname(char * dev_name,char * ret_buf,size_t len)2100Sstevel@tonic-gate i_devname_to_promname(char *dev_name, char *ret_buf, size_t len)
2110Sstevel@tonic-gate {
212*7413SJaven.Wu@Sun.COM dev_info_t *dip, *pdip, *cdip, *alt_dip = NULL;
213*7413SJaven.Wu@Sun.COM mdi_pathinfo_t *pip = NULL;
2140Sstevel@tonic-gate char *dev_path, *prom_path;
2150Sstevel@tonic-gate char *unit_address, *minorname, *nodename;
2160Sstevel@tonic-gate major_t major;
2170Sstevel@tonic-gate char *rptr, *optr, *offline;
2180Sstevel@tonic-gate size_t olen, rlen;
219*7413SJaven.Wu@Sun.COM int circ;
220*7413SJaven.Wu@Sun.COM int ret = 0;
2210Sstevel@tonic-gate
2220Sstevel@tonic-gate /* do some sanity checks */
2230Sstevel@tonic-gate if ((dev_name == NULL) || (ret_buf == NULL) ||
2240Sstevel@tonic-gate (strlen(dev_name) > MAXPATHLEN)) {
2250Sstevel@tonic-gate return (EINVAL);
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate /*
2290Sstevel@tonic-gate * Convert to a /devices name. Fail the translation if
2300Sstevel@tonic-gate * the name doesn't exist.
2310Sstevel@tonic-gate */
2320Sstevel@tonic-gate dev_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
2330Sstevel@tonic-gate if (resolve_devfs_name(dev_name, dev_path) != 0 ||
2340Sstevel@tonic-gate strncmp(dev_path, "/devices/", 9) != 0) {
2350Sstevel@tonic-gate kmem_free(dev_path, MAXPATHLEN);
2360Sstevel@tonic-gate return (EINVAL);
2370Sstevel@tonic-gate }
2380Sstevel@tonic-gate dev_name = dev_path + sizeof ("/devices") - 1;
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate bzero(ret_buf, len);
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate if (prom_finddevice(dev_name) != OBP_BADNODE) {
2430Sstevel@tonic-gate /* we are done */
2440Sstevel@tonic-gate (void) snprintf(ret_buf, len, "%s", dev_name);
2450Sstevel@tonic-gate kmem_free(dev_path, MAXPATHLEN);
2460Sstevel@tonic-gate return (0);
2470Sstevel@tonic-gate }
2480Sstevel@tonic-gate
2490Sstevel@tonic-gate /*
2500Sstevel@tonic-gate * if we get here, then some portion of the device path is
2510Sstevel@tonic-gate * not understood by the prom. We need to look for alternate
2520Sstevel@tonic-gate * names (e.g. replace ssd by disk) and mpxio enabled devices.
2530Sstevel@tonic-gate */
2540Sstevel@tonic-gate dip = e_ddi_hold_devi_by_path(dev_name, 0);
2550Sstevel@tonic-gate if (dip == NULL) {
2560Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot find dip for %s", dev_name);
2570Sstevel@tonic-gate kmem_free(dev_path, MAXPATHLEN);
2580Sstevel@tonic-gate return (EINVAL);
2590Sstevel@tonic-gate }
2600Sstevel@tonic-gate
261*7413SJaven.Wu@Sun.COM prom_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
2620Sstevel@tonic-gate rlen = len;
2630Sstevel@tonic-gate rptr = ret_buf;
2640Sstevel@tonic-gate
265*7413SJaven.Wu@Sun.COM if (!MDI_CLIENT(dip)) {
266*7413SJaven.Wu@Sun.COM ret = i_devi_to_promname(dip, prom_path, &alt_dip);
267*7413SJaven.Wu@Sun.COM if (ret == 0) {
268*7413SJaven.Wu@Sun.COM minorname = strrchr(dev_name, ':');
269*7413SJaven.Wu@Sun.COM if (minorname && (minorname[1] != '\0')) {
270*7413SJaven.Wu@Sun.COM (void) strcat(prom_path, minorname);
271*7413SJaven.Wu@Sun.COM }
272*7413SJaven.Wu@Sun.COM (void) snprintf(rptr, rlen, "%s", prom_path);
273*7413SJaven.Wu@Sun.COM }
274*7413SJaven.Wu@Sun.COM } else {
275*7413SJaven.Wu@Sun.COM /*
276*7413SJaven.Wu@Sun.COM * if get to here, means dip is a vhci client
277*7413SJaven.Wu@Sun.COM */
278*7413SJaven.Wu@Sun.COM offline = kmem_zalloc(len, KM_SLEEP); /* offline paths */
279*7413SJaven.Wu@Sun.COM olen = len;
280*7413SJaven.Wu@Sun.COM optr = offline;
281*7413SJaven.Wu@Sun.COM /*
282*7413SJaven.Wu@Sun.COM * The following code assumes that the phci client is at leaf
283*7413SJaven.Wu@Sun.COM * level.
284*7413SJaven.Wu@Sun.COM */
285*7413SJaven.Wu@Sun.COM ndi_devi_enter(dip, &circ);
286*7413SJaven.Wu@Sun.COM while ((pip = mdi_get_next_phci_path(dip, pip)) != NULL) {
287*7413SJaven.Wu@Sun.COM /*
288*7413SJaven.Wu@Sun.COM * walk all paths associated to the client node
289*7413SJaven.Wu@Sun.COM */
290*7413SJaven.Wu@Sun.COM bzero(prom_path, MAXPATHLEN);
2910Sstevel@tonic-gate
292*7413SJaven.Wu@Sun.COM /*
293*7413SJaven.Wu@Sun.COM * replace with mdi_hold_path() when mpxio goes into
294*7413SJaven.Wu@Sun.COM * genunix
295*7413SJaven.Wu@Sun.COM */
296*7413SJaven.Wu@Sun.COM MDI_PI_LOCK(pip);
297*7413SJaven.Wu@Sun.COM MDI_PI_HOLD(pip);
298*7413SJaven.Wu@Sun.COM MDI_PI_UNLOCK(pip);
299*7413SJaven.Wu@Sun.COM
300*7413SJaven.Wu@Sun.COM if (mdi_pi_pathname_obp(pip, prom_path) != NULL) {
301*7413SJaven.Wu@Sun.COM /*
302*7413SJaven.Wu@Sun.COM * The path has different obp path
303*7413SJaven.Wu@Sun.COM */
304*7413SJaven.Wu@Sun.COM goto minor_pathinfo;
3050Sstevel@tonic-gate }
3060Sstevel@tonic-gate
307*7413SJaven.Wu@Sun.COM pdip = mdi_pi_get_phci(pip);
308*7413SJaven.Wu@Sun.COM ndi_hold_devi(pdip);
309*7413SJaven.Wu@Sun.COM
310*7413SJaven.Wu@Sun.COM /*
311*7413SJaven.Wu@Sun.COM * Get obp path name of the phci node firstly.
312*7413SJaven.Wu@Sun.COM * NOTE: if the alternate node of pdip exists,
313*7413SJaven.Wu@Sun.COM * the third argument of the i_devi_to_promname()
314*7413SJaven.Wu@Sun.COM * would be set to the alternate node.
315*7413SJaven.Wu@Sun.COM */
316*7413SJaven.Wu@Sun.COM (void) i_devi_to_promname(pdip, prom_path, &alt_dip);
317*7413SJaven.Wu@Sun.COM if (alt_dip != NULL) {
3180Sstevel@tonic-gate ndi_rele_devi(pdip);
319*7413SJaven.Wu@Sun.COM pdip = alt_dip;
320*7413SJaven.Wu@Sun.COM ndi_hold_devi(pdip);
3210Sstevel@tonic-gate }
3220Sstevel@tonic-gate
323*7413SJaven.Wu@Sun.COM nodename = ddi_node_name(dip);
324*7413SJaven.Wu@Sun.COM unit_address = MDI_PI(pip)->pi_addr;
325*7413SJaven.Wu@Sun.COM
326*7413SJaven.Wu@Sun.COM major = ddi_driver_major(dip);
327*7413SJaven.Wu@Sun.COM cdip = find_alternate_node(pdip, major);
328*7413SJaven.Wu@Sun.COM
329*7413SJaven.Wu@Sun.COM if (cdip) {
330*7413SJaven.Wu@Sun.COM nodename = ddi_node_name(cdip);
331*7413SJaven.Wu@Sun.COM }
3320Sstevel@tonic-gate /*
3330Sstevel@tonic-gate * node name + unitaddr to the prom_path
3340Sstevel@tonic-gate */
3350Sstevel@tonic-gate (void) strcat(prom_path, "/");
3360Sstevel@tonic-gate (void) strcat(prom_path, nodename);
3370Sstevel@tonic-gate if (unit_address && (*unit_address)) {
3380Sstevel@tonic-gate (void) strcat(prom_path, "@");
3390Sstevel@tonic-gate (void) strcat(prom_path, unit_address);
3400Sstevel@tonic-gate }
341*7413SJaven.Wu@Sun.COM if (cdip) {
342*7413SJaven.Wu@Sun.COM /* hold from find_alternate_node */
343*7413SJaven.Wu@Sun.COM ndi_rele_devi(cdip);
344*7413SJaven.Wu@Sun.COM }
345*7413SJaven.Wu@Sun.COM ndi_rele_devi(pdip);
346*7413SJaven.Wu@Sun.COM minor_pathinfo:
347*7413SJaven.Wu@Sun.COM minorname = strrchr(dev_name, ':');
348*7413SJaven.Wu@Sun.COM if (minorname && (minorname[1] != '\0')) {
349*7413SJaven.Wu@Sun.COM (void) strcat(prom_path, minorname);
350*7413SJaven.Wu@Sun.COM }
3510Sstevel@tonic-gate
352*7413SJaven.Wu@Sun.COM if (MDI_PI_IS_ONLINE(pip)) {
353*7413SJaven.Wu@Sun.COM (void) snprintf(rptr, rlen, "%s", prom_path);
354*7413SJaven.Wu@Sun.COM rlen -= strlen(rptr) + 1;
355*7413SJaven.Wu@Sun.COM rptr += strlen(rptr) + 1;
356*7413SJaven.Wu@Sun.COM if (rlen <= 0) /* drop paths we can't store */
357*7413SJaven.Wu@Sun.COM break;
358*7413SJaven.Wu@Sun.COM } else { /* path is offline */
359*7413SJaven.Wu@Sun.COM (void) snprintf(optr, olen, "%s", prom_path);
360*7413SJaven.Wu@Sun.COM olen -= strlen(optr) + 1;
361*7413SJaven.Wu@Sun.COM if (olen > 0) /* drop paths we can't store */
362*7413SJaven.Wu@Sun.COM optr += strlen(optr) + 1;
3630Sstevel@tonic-gate }
364*7413SJaven.Wu@Sun.COM MDI_PI_LOCK(pip);
365*7413SJaven.Wu@Sun.COM MDI_PI_RELE(pip);
366*7413SJaven.Wu@Sun.COM if (MDI_PI(pip)->pi_ref_cnt == 0)
367*7413SJaven.Wu@Sun.COM cv_broadcast(&MDI_PI(pip)->pi_ref_cv);
368*7413SJaven.Wu@Sun.COM MDI_PI_UNLOCK(pip);
3690Sstevel@tonic-gate }
370*7413SJaven.Wu@Sun.COM ndi_devi_exit(dip, circ);
371*7413SJaven.Wu@Sun.COM ret = 0;
372*7413SJaven.Wu@Sun.COM if (rlen > 0) {
373*7413SJaven.Wu@Sun.COM /* now add as much of offline to ret_buf as possible */
374*7413SJaven.Wu@Sun.COM bcopy(offline, rptr, rlen);
3750Sstevel@tonic-gate }
376*7413SJaven.Wu@Sun.COM kmem_free(offline, len);
3770Sstevel@tonic-gate }
378*7413SJaven.Wu@Sun.COM /* release hold from e_ddi_hold_devi_by_path() */
379*7413SJaven.Wu@Sun.COM ndi_rele_devi(dip);
3800Sstevel@tonic-gate ret_buf[len - 1] = '\0';
3810Sstevel@tonic-gate ret_buf[len - 2] = '\0';
3820Sstevel@tonic-gate kmem_free(dev_path, MAXPATHLEN);
3830Sstevel@tonic-gate kmem_free(prom_path, MAXPATHLEN);
384*7413SJaven.Wu@Sun.COM
385*7413SJaven.Wu@Sun.COM return (ret);
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate
3880Sstevel@tonic-gate /*
3890Sstevel@tonic-gate * check for a possible substitute node. This routine searches the
3900Sstevel@tonic-gate * children of parent_dip, looking for a node that:
3910Sstevel@tonic-gate * 1. is a prom node
3920Sstevel@tonic-gate * 2. binds to the same major number
3930Sstevel@tonic-gate * 3. there is no need to verify that the unit-address information
3940Sstevel@tonic-gate * match since it is likely that the substitute node
3950Sstevel@tonic-gate * will have none (e.g. disk) - this would be the reason the
3960Sstevel@tonic-gate * framework rejected it in the first place.
3970Sstevel@tonic-gate *
3980Sstevel@tonic-gate * assumes parent_dip is held
3990Sstevel@tonic-gate */
4000Sstevel@tonic-gate static dev_info_t *
find_alternate_node(dev_info_t * parent_dip,major_t major)4010Sstevel@tonic-gate find_alternate_node(dev_info_t *parent_dip, major_t major)
4020Sstevel@tonic-gate {
4030Sstevel@tonic-gate int circ;
4040Sstevel@tonic-gate dev_info_t *child_dip;
4050Sstevel@tonic-gate
4060Sstevel@tonic-gate /* lock down parent to keep children from being removed */
4070Sstevel@tonic-gate ndi_devi_enter(parent_dip, &circ);
4080Sstevel@tonic-gate for (child_dip = ddi_get_child(parent_dip); child_dip != NULL;
4090Sstevel@tonic-gate child_dip = ddi_get_next_sibling(child_dip)) {
4100Sstevel@tonic-gate
4110Sstevel@tonic-gate /* look for obp node with matching major */
4120Sstevel@tonic-gate if ((ndi_dev_is_prom_node(child_dip) != 0) &&
4130Sstevel@tonic-gate (ddi_driver_major(child_dip) == major)) {
4140Sstevel@tonic-gate ndi_hold_devi(child_dip);
4150Sstevel@tonic-gate break;
4160Sstevel@tonic-gate }
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate ndi_devi_exit(parent_dip, circ);
4190Sstevel@tonic-gate return (child_dip);
4200Sstevel@tonic-gate }
4210Sstevel@tonic-gate
4220Sstevel@tonic-gate /*
4230Sstevel@tonic-gate * given an absolute pathname, convert it, if possible, to a devfs
4240Sstevel@tonic-gate * name. Examples:
4250Sstevel@tonic-gate * /dev/rsd3a to /pci@1f,4000/glm@3/sd@0,0:a
4260Sstevel@tonic-gate * /dev/dsk/c0t0d0s0 to /pci@1f,4000/glm@3/sd@0,0:a
4270Sstevel@tonic-gate * /devices/pci@1f,4000/glm@3/sd@0,0:a to /pci@1f,4000/glm@3/sd@0,0:a
4280Sstevel@tonic-gate * /pci@1f,4000/glm@3/sd@0,0:a unchanged
4290Sstevel@tonic-gate *
4300Sstevel@tonic-gate * This routine deals with symbolic links, physical pathname with and
4310Sstevel@tonic-gate * without /devices stripped. Returns 0 on success or -1 on failure.
4320Sstevel@tonic-gate */
4330Sstevel@tonic-gate static int
resolve_devfs_name(char * name,char * buffer)4340Sstevel@tonic-gate resolve_devfs_name(char *name, char *buffer)
4350Sstevel@tonic-gate {
4360Sstevel@tonic-gate int error;
4370Sstevel@tonic-gate char *fullname = NULL;
4380Sstevel@tonic-gate struct pathname pn, rpn;
4390Sstevel@tonic-gate
4400Sstevel@tonic-gate /* if not a /dev or /device name, prepend /devices */
4410Sstevel@tonic-gate if (strncmp(name, "/dev/", 5) != 0 &&
4420Sstevel@tonic-gate strncmp(name, "/devices/", 9) != 0) {
4430Sstevel@tonic-gate fullname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
4440Sstevel@tonic-gate (void) snprintf(fullname, MAXPATHLEN, "/devices%s", name);
4450Sstevel@tonic-gate name = fullname;
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate
4480Sstevel@tonic-gate if (pn_get(name, UIO_SYSSPACE, &pn) != 0) {
4490Sstevel@tonic-gate if (fullname)
4500Sstevel@tonic-gate kmem_free(fullname, MAXPATHLEN);
4510Sstevel@tonic-gate return (-1);
4520Sstevel@tonic-gate }
4530Sstevel@tonic-gate
4540Sstevel@tonic-gate pn_alloc(&rpn);
4550Sstevel@tonic-gate error = lookuppn(&pn, &rpn, FOLLOW, NULL, NULL);
4560Sstevel@tonic-gate if (error == 0)
4570Sstevel@tonic-gate bcopy(rpn.pn_path, buffer, rpn.pn_pathlen);
4580Sstevel@tonic-gate
4590Sstevel@tonic-gate pn_free(&pn);
4600Sstevel@tonic-gate pn_free(&rpn);
4610Sstevel@tonic-gate if (fullname)
4620Sstevel@tonic-gate kmem_free(fullname, MAXPATHLEN);
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate return (error);
4650Sstevel@tonic-gate }
4660Sstevel@tonic-gate
4670Sstevel@tonic-gate /*
4680Sstevel@tonic-gate * If bootstring contains a device path, we need to convert to a format
4690Sstevel@tonic-gate * the prom will understand. To do so, we convert the existing path to
4700Sstevel@tonic-gate * a prom-compatible path and return the value of new_path. If the
4710Sstevel@tonic-gate * caller specifies new_path as NULL, we allocate an appropriately
4720Sstevel@tonic-gate * sized new_path on behalf of the caller. If the caller invokes this
4730Sstevel@tonic-gate * function with new_path = NULL, they must do so from a context in
4740Sstevel@tonic-gate * which it is safe to perform a sleeping memory allocation.
4750Sstevel@tonic-gate */
4760Sstevel@tonic-gate char *
i_convert_boot_device_name(char * cur_path,char * new_path,size_t * len)4770Sstevel@tonic-gate i_convert_boot_device_name(char *cur_path, char *new_path, size_t *len)
4780Sstevel@tonic-gate {
4790Sstevel@tonic-gate char *ptr;
4800Sstevel@tonic-gate int rval;
4810Sstevel@tonic-gate
4820Sstevel@tonic-gate ASSERT(cur_path != NULL && len != NULL);
4830Sstevel@tonic-gate ASSERT(new_path == NULL || *len >= MAXPATHLEN);
4840Sstevel@tonic-gate
4850Sstevel@tonic-gate if (new_path == NULL) {
4860Sstevel@tonic-gate *len = MAXPATHLEN + MAXNAMELEN;
4870Sstevel@tonic-gate new_path = kmem_alloc(*len, KM_SLEEP);
4880Sstevel@tonic-gate }
4890Sstevel@tonic-gate
4900Sstevel@tonic-gate if ((ptr = strchr(cur_path, ' ')) != NULL)
4910Sstevel@tonic-gate *ptr = '\0';
4920Sstevel@tonic-gate
4930Sstevel@tonic-gate rval = i_devname_to_promname(cur_path, new_path, *len);
4940Sstevel@tonic-gate
4950Sstevel@tonic-gate if (ptr != NULL)
4960Sstevel@tonic-gate *ptr = ' ';
4970Sstevel@tonic-gate
4980Sstevel@tonic-gate if (rval == 0) {
4990Sstevel@tonic-gate if (ptr != NULL) {
5000Sstevel@tonic-gate (void) snprintf(new_path + strlen(new_path),
5010Sstevel@tonic-gate *len - strlen(new_path), "%s", ptr);
5020Sstevel@tonic-gate }
5030Sstevel@tonic-gate } else { /* the conversion failed */
5040Sstevel@tonic-gate (void) snprintf(new_path, *len, "%s", cur_path);
5050Sstevel@tonic-gate }
5060Sstevel@tonic-gate
5070Sstevel@tonic-gate return (new_path);
5080Sstevel@tonic-gate }
5090Sstevel@tonic-gate
5100Sstevel@tonic-gate /*
511*7413SJaven.Wu@Sun.COM * Get the parent dip.
5120Sstevel@tonic-gate */
5130Sstevel@tonic-gate static dev_info_t *
get_parent(dev_info_t * dip,struct parinfo * info)514*7413SJaven.Wu@Sun.COM get_parent(dev_info_t *dip, struct parinfo *info)
5150Sstevel@tonic-gate {
5160Sstevel@tonic-gate dev_info_t *pdip;
5170Sstevel@tonic-gate
518*7413SJaven.Wu@Sun.COM pdip = ddi_get_parent(dip);
5190Sstevel@tonic-gate ndi_hold_devi(pdip);
5200Sstevel@tonic-gate info->dip = dip;
5210Sstevel@tonic-gate info->pdip = pdip;
5220Sstevel@tonic-gate return (pdip);
5230Sstevel@tonic-gate }
524