diff options
Diffstat (limited to 'usr/src/psm/promif/ieee1275/common/prom_node.c')
| -rw-r--r-- | usr/src/psm/promif/ieee1275/common/prom_node.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/usr/src/psm/promif/ieee1275/common/prom_node.c b/usr/src/psm/promif/ieee1275/common/prom_node.c new file mode 100644 index 0000000000..95ff877149 --- /dev/null +++ b/usr/src/psm/promif/ieee1275/common/prom_node.c @@ -0,0 +1,294 @@ +/* + * 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 1994,1998-2000,2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/promif.h> +#include <sys/promimpl.h> + +/* + * 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 </chosen>\n"); + /*NOTREACHED*/ +} + +/* + * 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); +} |
