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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/esunddi.h>
29 #include <sys/promif_impl.h>
30 
31 #ifdef _KMDB
32 static pnode_t chosennode;
33 static pnode_t optionsnode;
34 #else
35 static char *gettoken(char *tp, char *token);
36 static pnode_t finddevice(char *path);
37 #endif
38 
39 /*
40  * Routines for walking the PROMs devinfo tree
41  */
42 
43 #ifdef _KMDB
44 
45 void
promif_set_nodes(pnode_t chosen,pnode_t options)46 promif_set_nodes(pnode_t chosen, pnode_t options)
47 {
48 	chosennode = chosen;
49 	optionsnode = options;
50 }
51 
52 int
promif_finddevice(void * p)53 promif_finddevice(void *p)
54 {
55 	cell_t	*ci = (cell_t *)p;
56 	char *path;
57 
58 	ASSERT(ci[1] == 1);
59 
60 	path = p1275_cell2ptr(ci[3]);
61 
62 	if (strcmp("/chosen", path) == 0) {
63 		ci[4] = p1275_dnode2cell(chosennode);
64 	} else if (strcmp("/options", path) == 0) {
65 		ci[4] = p1275_dnode2cell(optionsnode);
66 	} else {
67 		/* only supports known nodes */
68 		ASSERT(0);
69 	}
70 
71 	return (0);
72 }
73 
74 #else
75 
76 int
promif_finddevice(void * p)77 promif_finddevice(void *p)
78 {
79 	cell_t	*ci = (cell_t *)p;
80 	pnode_t	node;
81 
82 	ASSERT(ci[1] == 1);
83 
84 	/*
85 	 * We are passing the cpu pointer (CPU->cpu_id) explicitly to
86 	 * thread_affinity_set() so that we don't attempt to grab the
87 	 * cpu_lock internally in thread_affinity_set() and may sleep
88 	 * as a result.
89 	 * It is safe to pass CPU->cpu_id and it will always be valid.
90 	 */
91 	thread_affinity_set(curthread, CPU->cpu_id);
92 	node = finddevice(p1275_cell2ptr(ci[3]));
93 
94 	ci[4] = p1275_dnode2cell(node);
95 	thread_affinity_clear(curthread);
96 
97 	return (0);
98 }
99 
100 #endif
101 
102 int
promif_nextnode(void * p)103 promif_nextnode(void *p)
104 {
105 	cell_t	*ci = (cell_t *)p;
106 	pnode_t	next;
107 
108 	ASSERT(ci[1] == 1);
109 
110 	next = promif_stree_nextnode(p1275_cell2dnode(ci[3]));
111 
112 	ci[4] = p1275_dnode2cell(next);
113 
114 	return (0);
115 }
116 
117 int
promif_childnode(void * p)118 promif_childnode(void *p)
119 {
120 	cell_t	*ci = (cell_t *)p;
121 	pnode_t	child;
122 
123 	ASSERT(ci[1] == 1);
124 
125 	child = promif_stree_childnode(p1275_cell2dnode(ci[3]));
126 
127 	ci[4] = p1275_dnode2cell(child);
128 
129 	return (0);
130 }
131 
132 int
promif_parentnode(void * p)133 promif_parentnode(void *p)
134 {
135 	cell_t	*ci = (cell_t *)p;
136 	pnode_t	parent;
137 
138 	ASSERT(ci[1] == 1);
139 
140 	parent = promif_stree_parentnode(p1275_cell2dnode(ci[3]));
141 
142 	ci[4] = p1275_dnode2cell(parent);
143 
144 	return (0);
145 }
146 
147 #ifndef _KMDB
148 
149 /*
150  * Get a token from a prom pathname, collecting everything
151  * until a non-comma, non-colon separator is found. Any
152  * options, including the ':' option separator, on the end
153  * of the token are removed.
154  */
155 static char *
gettoken(char * tp,char * token)156 gettoken(char *tp, char *token)
157 {
158 	char *result = token;
159 
160 	for (;;) {
161 		tp = prom_path_gettoken(tp, token);
162 		token += prom_strlen(token);
163 		if ((*tp == ',') || (*tp == ':')) {
164 			*token++ = *tp++;
165 			*token = '\0';
166 			continue;
167 		}
168 		break;
169 	}
170 
171 	/* strip off any options from the token */
172 	prom_strip_options(result, result);
173 
174 	return (tp);
175 }
176 
177 /*
178  * Retrieve the unit address for a node by looking it up
179  * in the corresponding dip. -1 is returned if no unit
180  * address can be determined.
181  */
182 static int
get_unit_addr(pnode_t np,char * paddr)183 get_unit_addr(pnode_t np, char *paddr)
184 {
185 	dev_info_t	*dip;
186 	char		*addr;
187 
188 	if ((dip = e_ddi_nodeid_to_dip(np)) == NULL) {
189 		return (-1);
190 	}
191 
192 	if ((addr = ddi_get_name_addr(dip)) == NULL) {
193 		ddi_release_devi(dip);
194 		return (-1);
195 	}
196 
197 	(void) prom_strcpy(paddr, addr);
198 
199 	ddi_release_devi(dip);
200 
201 	return (0);
202 }
203 
204 /*
205  * Get node id of node in prom tree that path identifies
206  */
207 static pnode_t
finddevice(char * path)208 finddevice(char *path)
209 {
210 	char	name[OBP_MAXPROPNAME];
211 	char	addr[OBP_MAXPROPNAME];
212 	char	pname[OBP_MAXPROPNAME];
213 	char	paddr[OBP_MAXPROPNAME];
214 	char	*tp;
215 	pnode_t	np;
216 	pnode_t	device;
217 
218 	CIF_DBG_NODE("finddevice: %s\n", path);
219 
220 	tp = path;
221 	np = prom_rootnode();
222 	device = OBP_BADNODE;
223 
224 	/* must be a fully specified path */
225 	if (*tp++ != '/')
226 		goto done;
227 
228 	for (;;) {
229 		/* get the name from the path */
230 		tp = gettoken(tp, name);
231 		if (*name == '\0')
232 			break;
233 
234 		/* get the address from the path */
235 		if (*tp == '@') {
236 			tp++;
237 			tp = gettoken(tp, addr);
238 		} else {
239 			addr[0] = '\0';
240 		}
241 
242 		CIF_DBG_NODE("looking for: %s%s%s\n", name,
243 		    (*addr != '\0') ? "@" : "", addr);
244 
245 		if ((np = prom_childnode(np)) == OBP_NONODE)
246 			break;
247 
248 		while (np != OBP_NONODE) {
249 
250 			/* get the name from the current node */
251 			if (prom_getprop(np, OBP_NAME, pname) < 0)
252 				goto done;
253 
254 			/* get the address from the current node */
255 			if (get_unit_addr(np, paddr) < 0)
256 				paddr[0] = '\0';
257 
258 			/* compare the names and addresses */
259 			if ((prom_strcmp(name, pname) == 0) &&
260 			    (prom_strcmp(addr, paddr) == 0)) {
261 				CIF_DBG_NODE("found dev: %s%s%s (0x%x)\n",
262 				    pname, (*paddr != '\0') ? "@" : "",
263 				    paddr, np);
264 				break;
265 			} else {
266 				CIF_DBG_NODE("  no match: %s%s%s vs %s%s%s\n",
267 				    name, (*addr != '\0') ? "@" : "", addr,
268 				    pname, (*paddr != '\0') ? "@" : "", paddr);
269 			}
270 			np = prom_nextnode(np);
271 		}
272 
273 		/* path does not map to a node */
274 		if (np == OBP_NONODE)
275 			break;
276 
277 		if (*tp == '\0') {
278 			/* found a matching node */
279 			device = np;
280 			break;
281 		}
282 
283 		/*
284 		 * Continue the loop with the
285 		 * next component of the path.
286 		 */
287 		tp++;
288 	}
289 done:
290 
291 	if (device == OBP_BADNODE) {
292 		CIF_DBG_NODE("device not found\n\n");
293 	} else {
294 		CIF_DBG_NODE("returning 0x%x\n\n", device);
295 	}
296 
297 	return (device);
298 }
299 
300 #endif
301