/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include /* * Routines for walking the PROMs devinfo tree */ dnode_t prom_nextnode(dnode_t nodeid) { cell_t ci[5]; ci[0] = p1275_ptr2cell("peer"); /* Service name */ ci[1] = (cell_t)1; /* #argument cells */ ci[2] = (cell_t)1; /* #result cells */ ci[3] = p1275_dnode2cell(nodeid); /* Arg1: input phandle */ ci[4] = p1275_dnode2cell(OBP_NONODE); /* Res1: Prime result */ promif_preprom(); (void) p1275_cif_handler(&ci); promif_postprom(); return (p1275_cell2dnode(ci[4])); /* Res1: peer phandle */ } dnode_t prom_childnode(dnode_t nodeid) { cell_t ci[5]; ci[0] = p1275_ptr2cell("child"); /* Service name */ ci[1] = (cell_t)1; /* #argument cells */ ci[2] = (cell_t)1; /* #result cells */ ci[3] = p1275_dnode2cell(nodeid); /* Arg1: input phandle */ ci[4] = p1275_dnode2cell(OBP_NONODE); /* Res1: Prime result */ promif_preprom(); (void) p1275_cif_handler(&ci); promif_postprom(); return (p1275_cell2dnode(ci[4])); /* Res1: child phandle */ } /* * prom_walk_devs() implements a generic walker for the OBP tree; this * implementation uses an explicitly managed stack in order to save the * overhead of a recursive implementation. */ void prom_walk_devs(dnode_t node, int (*cb)(dnode_t, void *, void *), void *arg, void *result) { dnode_t stack[OBP_STACKDEPTH]; int stackidx = 0; if (node == OBP_NONODE || node == OBP_BADNODE) { prom_panic("Invalid node specified as root of prom tree walk"); } stack[0] = node; for (;;) { dnode_t curnode = stack[stackidx]; dnode_t child; /* * We're out of stuff to do at this level, bump back up a level * in the tree, and move to the next node; if the new level * will be level -1, we're done. */ if (curnode == OBP_NONODE || curnode == OBP_BADNODE) { stackidx--; if (stackidx < 0) return; stack[stackidx] = prom_nextnode(stack[stackidx]); continue; } switch ((*cb)(curnode, arg, result)) { case PROM_WALK_TERMINATE: return; case PROM_WALK_CONTINUE: /* * If curnode has a child, traverse to it, * otherwise move to curnode's sibling. */ child = prom_childnode(curnode); if (child != OBP_NONODE && child != OBP_BADNODE) { stackidx++; stack[stackidx] = child; } else { stack[stackidx] = prom_nextnode(stack[stackidx]); } break; default: prom_panic("unrecognized walk directive"); } } } /* * prom_findnode_bydevtype() searches the prom device subtree rooted at 'node' * and returns the first node whose device type property matches the type * supplied in 'devtype'. */ static int bytype_cb(dnode_t node, void *arg, void *result) { if (prom_devicetype(node, (char *)arg)) { *((dnode_t *)result) = node; return (PROM_WALK_TERMINATE); } return (PROM_WALK_CONTINUE); } dnode_t prom_findnode_bydevtype(dnode_t node, char *devtype) { dnode_t result = OBP_NONODE; prom_walk_devs(node, bytype_cb, devtype, &result); return (result); } /* * prom_findnode_byname() searches the prom device subtree rooted at 'node' and * returns the first node whose name matches the name supplied in 'name'. */ static int byname_cb(dnode_t node, void *arg, void *result) { if (prom_getnode_byname(node, (char *)arg)) { *((dnode_t *)result) = node; return (PROM_WALK_TERMINATE); } return (PROM_WALK_CONTINUE); } dnode_t prom_findnode_byname(dnode_t node, char *name) { dnode_t result = OBP_NONODE; prom_walk_devs(node, byname_cb, name, &result); return (result); } /* * Return the root nodeid. * Calling prom_nextnode(0) returns the root nodeid. */ dnode_t prom_rootnode(void) { static dnode_t rootnode; return (rootnode ? rootnode : (rootnode = prom_nextnode(OBP_NONODE))); } dnode_t prom_parentnode(dnode_t nodeid) { cell_t ci[5]; ci[0] = p1275_ptr2cell("parent"); /* Service name */ ci[1] = (cell_t)1; /* #argument cells */ ci[2] = (cell_t)1; /* #result cells */ ci[3] = p1275_dnode2cell(nodeid); /* Arg1: input phandle */ ci[4] = p1275_dnode2cell(OBP_NONODE); /* Res1: Prime result */ promif_preprom(); (void) p1275_cif_handler(&ci); promif_postprom(); return (p1275_cell2dnode(ci[4])); /* Res1: parent phandle */ } dnode_t prom_finddevice(char *path) { cell_t ci[5]; #ifdef PROM_32BIT_ADDRS char *opath = NULL; size_t len; if ((uintptr_t)path > (uint32_t)-1) { opath = path; len = prom_strlen(opath) + 1; /* include terminating NUL */ path = promplat_alloc(len); if (path == NULL) { return (OBP_BADNODE); } (void) prom_strcpy(path, opath); } #endif promif_preprom(); ci[0] = p1275_ptr2cell("finddevice"); /* Service name */ ci[1] = (cell_t)1; /* #argument cells */ ci[2] = (cell_t)1; /* #result cells */ ci[3] = p1275_ptr2cell(path); /* Arg1: pathname */ ci[4] = p1275_dnode2cell(OBP_BADNODE); /* Res1: Prime result */ (void) p1275_cif_handler(&ci); promif_postprom(); #ifdef PROM_32BIT_ADDRS if (opath != NULL) promplat_free(path, len); #endif return ((dnode_t)p1275_cell2dnode(ci[4])); /* Res1: phandle */ } dnode_t prom_chosennode(void) { static dnode_t chosen; dnode_t node; if (chosen) return (chosen); node = prom_finddevice("/chosen"); if (node != OBP_BADNODE) return (chosen = node); prom_fatal_error("prom_chosennode: Can't find \n"); /*NOTREACHED*/ /* * gcc doesn't recognize "NOTREACHED" and puts the warning. * To surpress it, returning an integer value is required. */ return ((dnode_t)0); } /* * Returns the nodeid of /aliases. * /aliases exists in OBP >= 2.4 and in Open Firmware. * Returns OBP_BADNODE if it doesn't exist. */ dnode_t prom_alias_node(void) { static dnode_t node; if (node == 0) node = prom_finddevice("/aliases"); return (node); } /* * Returns the nodeid of /options. * Returns OBP_BADNODE if it doesn't exist. */ dnode_t prom_optionsnode(void) { static dnode_t node; if (node == 0) node = prom_finddevice("/options"); return (node); }