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 2005 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/promif.h> 30 #include <sys/promimpl.h> 31 32 /* 33 * Routines for walking the PROMs devinfo tree 34 */ 35 pnode_t 36 prom_nextnode(pnode_t nodeid) 37 { 38 cell_t ci[5]; 39 40 ci[0] = p1275_ptr2cell("peer"); /* Service name */ 41 ci[1] = (cell_t)1; /* #argument cells */ 42 ci[2] = (cell_t)1; /* #result cells */ 43 ci[3] = p1275_dnode2cell(nodeid); /* Arg1: input phandle */ 44 ci[4] = p1275_dnode2cell(OBP_NONODE); /* Res1: Prime result */ 45 46 promif_preprom(); 47 (void) p1275_cif_handler(&ci); 48 promif_postprom(); 49 50 return (p1275_cell2dnode(ci[4])); /* Res1: peer phandle */ 51 } 52 53 pnode_t 54 prom_childnode(pnode_t nodeid) 55 { 56 cell_t ci[5]; 57 58 ci[0] = p1275_ptr2cell("child"); /* Service name */ 59 ci[1] = (cell_t)1; /* #argument cells */ 60 ci[2] = (cell_t)1; /* #result cells */ 61 ci[3] = p1275_dnode2cell(nodeid); /* Arg1: input phandle */ 62 ci[4] = p1275_dnode2cell(OBP_NONODE); /* Res1: Prime result */ 63 64 promif_preprom(); 65 (void) p1275_cif_handler(&ci); 66 promif_postprom(); 67 68 return (p1275_cell2dnode(ci[4])); /* Res1: child phandle */ 69 } 70 71 /* 72 * prom_walk_devs() implements a generic walker for the OBP tree; this 73 * implementation uses an explicitly managed stack in order to save the 74 * overhead of a recursive implementation. 75 */ 76 void 77 prom_walk_devs(pnode_t node, int (*cb)(pnode_t, void *, void *), void *arg, 78 void *result) 79 { 80 pnode_t stack[OBP_STACKDEPTH]; 81 int stackidx = 0; 82 83 if (node == OBP_NONODE || node == OBP_BADNODE) { 84 prom_panic("Invalid node specified as root of prom tree walk"); 85 } 86 87 stack[0] = node; 88 89 for (;;) { 90 pnode_t curnode = stack[stackidx]; 91 pnode_t child; 92 93 /* 94 * We're out of stuff to do at this level, bump back up a level 95 * in the tree, and move to the next node; if the new level 96 * will be level -1, we're done. 97 */ 98 if (curnode == OBP_NONODE || curnode == OBP_BADNODE) { 99 stackidx--; 100 101 if (stackidx < 0) 102 return; 103 104 stack[stackidx] = prom_nextnode(stack[stackidx]); 105 continue; 106 } 107 108 switch ((*cb)(curnode, arg, result)) { 109 110 case PROM_WALK_TERMINATE: 111 return; 112 113 case PROM_WALK_CONTINUE: 114 /* 115 * If curnode has a child, traverse to it, 116 * otherwise move to curnode's sibling. 117 */ 118 child = prom_childnode(curnode); 119 if (child != OBP_NONODE && child != OBP_BADNODE) { 120 stackidx++; 121 stack[stackidx] = child; 122 } else { 123 stack[stackidx] = 124 prom_nextnode(stack[stackidx]); 125 } 126 break; 127 128 default: 129 prom_panic("unrecognized walk directive"); 130 } 131 } 132 } 133 134 /* 135 * prom_findnode_bydevtype() searches the prom device subtree rooted at 'node' 136 * and returns the first node whose device type property matches the type 137 * supplied in 'devtype'. 138 */ 139 static int 140 bytype_cb(pnode_t node, void *arg, void *result) 141 { 142 if (prom_devicetype(node, (char *)arg)) { 143 *((pnode_t *)result) = node; 144 return (PROM_WALK_TERMINATE); 145 } 146 return (PROM_WALK_CONTINUE); 147 } 148 149 pnode_t 150 prom_findnode_bydevtype(pnode_t node, char *devtype) 151 { 152 pnode_t result = OBP_NONODE; 153 prom_walk_devs(node, bytype_cb, devtype, &result); 154 return (result); 155 } 156 157 158 /* 159 * prom_findnode_byname() searches the prom device subtree rooted at 'node' and 160 * returns the first node whose name matches the name supplied in 'name'. 161 */ 162 static int 163 byname_cb(pnode_t node, void *arg, void *result) 164 { 165 if (prom_getnode_byname(node, (char *)arg)) { 166 *((pnode_t *)result) = node; 167 return (PROM_WALK_TERMINATE); 168 } 169 return (PROM_WALK_CONTINUE); 170 } 171 172 pnode_t 173 prom_findnode_byname(pnode_t node, char *name) 174 { 175 pnode_t result = OBP_NONODE; 176 prom_walk_devs(node, byname_cb, name, &result); 177 return (result); 178 } 179 180 /* 181 * Return the root nodeid. 182 * Calling prom_nextnode(0) returns the root nodeid. 183 */ 184 pnode_t 185 prom_rootnode(void) 186 { 187 static pnode_t rootnode; 188 189 return (rootnode ? rootnode : (rootnode = prom_nextnode(OBP_NONODE))); 190 } 191 192 pnode_t 193 prom_parentnode(pnode_t nodeid) 194 { 195 cell_t ci[5]; 196 197 ci[0] = p1275_ptr2cell("parent"); /* Service name */ 198 ci[1] = (cell_t)1; /* #argument cells */ 199 ci[2] = (cell_t)1; /* #result cells */ 200 ci[3] = p1275_dnode2cell(nodeid); /* Arg1: input phandle */ 201 ci[4] = p1275_dnode2cell(OBP_NONODE); /* Res1: Prime result */ 202 203 promif_preprom(); 204 (void) p1275_cif_handler(&ci); 205 promif_postprom(); 206 207 return (p1275_cell2dnode(ci[4])); /* Res1: parent phandle */ 208 } 209 210 pnode_t 211 prom_finddevice(char *path) 212 { 213 cell_t ci[5]; 214 #ifdef PROM_32BIT_ADDRS 215 char *opath = NULL; 216 size_t len; 217 218 if ((uintptr_t)path > (uint32_t)-1) { 219 opath = path; 220 len = prom_strlen(opath) + 1; /* include terminating NUL */ 221 path = promplat_alloc(len); 222 if (path == NULL) { 223 return (OBP_BADNODE); 224 } 225 (void) prom_strcpy(path, opath); 226 } 227 #endif 228 229 promif_preprom(); 230 231 ci[0] = p1275_ptr2cell("finddevice"); /* Service name */ 232 ci[1] = (cell_t)1; /* #argument cells */ 233 ci[2] = (cell_t)1; /* #result cells */ 234 ci[3] = p1275_ptr2cell(path); /* Arg1: pathname */ 235 ci[4] = p1275_dnode2cell(OBP_BADNODE); /* Res1: Prime result */ 236 237 (void) p1275_cif_handler(&ci); 238 239 promif_postprom(); 240 241 #ifdef PROM_32BIT_ADDRS 242 if (opath != NULL) 243 promplat_free(path, len); 244 #endif 245 246 return ((pnode_t)p1275_cell2dnode(ci[4])); /* Res1: phandle */ 247 } 248 249 pnode_t 250 prom_chosennode(void) 251 { 252 static pnode_t chosen; 253 pnode_t node; 254 255 if (chosen) 256 return (chosen); 257 258 node = prom_finddevice("/chosen"); 259 260 if (node != OBP_BADNODE) 261 return (chosen = node); 262 263 prom_fatal_error("prom_chosennode: Can't find </chosen>\n"); 264 /*NOTREACHED*/ 265 266 /* 267 * gcc doesn't recognize "NOTREACHED" and puts the warning. 268 * To surpress it, returning an integer value is required. 269 */ 270 return ((pnode_t)0); 271 } 272 273 /* 274 * Returns the nodeid of /aliases. 275 * /aliases exists in OBP >= 2.4 and in Open Firmware. 276 * Returns OBP_BADNODE if it doesn't exist. 277 */ 278 pnode_t 279 prom_alias_node(void) 280 { 281 static pnode_t node; 282 283 if (node == 0) 284 node = prom_finddevice("/aliases"); 285 return (node); 286 } 287 288 /* 289 * Returns the nodeid of /options. 290 * Returns OBP_BADNODE if it doesn't exist. 291 */ 292 pnode_t 293 prom_optionsnode(void) 294 { 295 static pnode_t node; 296 297 if (node == 0) 298 node = prom_finddevice("/options"); 299 return (node); 300 } 301