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