Home | History | Annotate | Download | only in promif
      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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <sys/promif_impl.h>
     30 #include <sys/kmem.h>
     31 #include <sys/machsystm.h>
     32 
     33 /*
     34  * A property attached to a node in the kernel's
     35  * shadow copy of the PROM device tree.
     36  */
     37 typedef struct prom_prop {
     38 	struct prom_prop *pp_next;
     39 	char		 *pp_name;
     40 	int		 pp_len;
     41 	void		 *pp_val;
     42 } prom_prop_t;
     43 
     44 /*
     45  * A node in the kernel's shadow copy of the PROM
     46  * device tree.
     47  */
     48 typedef struct prom_node {
     49 	pnode_t			pn_nodeid;
     50 	struct prom_prop	*pn_propp;
     51 	struct prom_node	*pn_parent;
     52 	struct prom_node	*pn_child;
     53 	struct prom_node	*pn_sibling;
     54 } prom_node_t;
     55 
     56 static prom_node_t *promif_root;
     57 
     58 static prom_node_t *find_node(pnode_t nodeid);
     59 static prom_node_t *find_node_work(prom_node_t *np, pnode_t node);
     60 static int getproplen(prom_node_t *pnp, char *name);
     61 static void *getprop(prom_node_t *pnp, char *name);
     62 static char *nextprop(prom_node_t *pnp, char *name);
     63 
     64 #ifndef _KMDB
     65 static void create_prop(prom_node_t *pnp, char *name, void *val, int len);
     66 static prom_node_t *create_node(prom_node_t *parent, pnode_t node);
     67 static void create_peers(prom_node_t *pnp, pnode_t node);
     68 static void create_children(prom_node_t *pnp, pnode_t parent);
     69 #endif
     70 
     71 /*
     72  * Hooks for kmdb for accessing the PROM shadow tree. The driver portion
     73  * of kmdb will retrieve the root of the tree and pass it down to the
     74  * debugger portion of kmdb. As the kmdb debugger is standalone, it has
     75  * its own promif_root pointer that it will be set to the value passed by
     76  * the driver so that kmdb points to the shadow tree maintained by the kernel.
     77  * So the "get" function is in the kernel while the "set" function is in kmdb.
     78  */
     79 #ifdef _KMDB
     80 void
     81 promif_stree_setroot(void *root)
     82 {
     83 	promif_root = (prom_node_t *)root;
     84 }
     85 #else
     86 void *
     87 promif_stree_getroot(void)
     88 {
     89 	return (promif_root);
     90 }
     91 #endif
     92 
     93 /*
     94  * Interfaces used internally by promif functions.
     95  * These hide all accesses to the shadow tree.
     96  */
     97 
     98 pnode_t
     99 promif_stree_parentnode(pnode_t nodeid)
    100 {
    101 	prom_node_t *pnp;
    102 
    103 	pnp = find_node(nodeid);
    104 	if (pnp && pnp->pn_parent) {
    105 		return (pnp->pn_parent->pn_nodeid);
    106 	}
    107 
    108 	return (OBP_NONODE);
    109 }
    110 
    111 pnode_t
    112 promif_stree_childnode(pnode_t nodeid)
    113 {
    114 	prom_node_t *pnp;
    115 
    116 	pnp = find_node(nodeid);
    117 	if (pnp && pnp->pn_child)
    118 		return (pnp->pn_child->pn_nodeid);
    119 
    120 	return (OBP_NONODE);
    121 }
    122 
    123 pnode_t
    124 promif_stree_nextnode(pnode_t nodeid)
    125 {
    126 	prom_node_t *pnp;
    127 
    128 	/*
    129 	 * Note: next(0) returns the root node
    130 	 */
    131 	pnp = find_node(nodeid);
    132 	if (pnp && (nodeid == OBP_NONODE))
    133 		return (pnp->pn_nodeid);
    134 	if (pnp && pnp->pn_sibling)
    135 		return (pnp->pn_sibling->pn_nodeid);
    136 
    137 	return (OBP_NONODE);
    138 }
    139 
    140 int
    141 promif_stree_getproplen(pnode_t nodeid, char *name)
    142 {
    143 	prom_node_t *pnp;
    144 
    145 	pnp = find_node(nodeid);
    146 	if (pnp == NULL)
    147 		return (-1);
    148 
    149 	return (getproplen(pnp, name));
    150 }
    151 
    152 int
    153 promif_stree_getprop(pnode_t nodeid, char *name, void *value)
    154 {
    155 	prom_node_t	*pnp;
    156 	void		*prop;
    157 	int		len;
    158 
    159 	pnp = find_node(nodeid);
    160 	if (pnp == NULL) {
    161 		prom_printf("find_node: no node?\n");
    162 		return (-1);
    163 	}
    164 
    165 	len = getproplen(pnp, name);
    166 	if (len > 0) {
    167 		prop = getprop(pnp, name);
    168 		bcopy(prop, value, len);
    169 	} else {
    170 		prom_printf("find_node: getproplen: %d\n", len);
    171 	}
    172 
    173 	return (len);
    174 }
    175 
    176 char *
    177 promif_stree_nextprop(pnode_t nodeid, char *name, char *next)
    178 {
    179 	prom_node_t	*pnp;
    180 	char		*propname;
    181 
    182 	next[0] = '\0';
    183 
    184 	pnp = find_node(nodeid);
    185 	if (pnp == NULL)
    186 		return (NULL);
    187 
    188 	propname = nextprop(pnp, name);
    189 	if (propname == NULL)
    190 		return (next);
    191 
    192 	(void) prom_strcpy(next, propname);
    193 
    194 	return (next);
    195 }
    196 
    197 static prom_node_t *
    198 find_node_work(prom_node_t *np, pnode_t node)
    199 {
    200 	prom_node_t *nnp;
    201 	prom_node_t *snp;
    202 
    203 	for (snp = np; snp != NULL; snp = snp->pn_sibling) {
    204 		if (snp->pn_nodeid == node)
    205 			return (snp);
    206 
    207 		if (snp->pn_child)
    208 			if ((nnp = find_node_work(snp->pn_child, node)) != NULL)
    209 				return (nnp);
    210 	}
    211 
    212 	return (NULL);
    213 }
    214 
    215 static prom_node_t *
    216 find_node(pnode_t nodeid)
    217 {
    218 
    219 	if (nodeid == OBP_NONODE)
    220 		return (promif_root);
    221 
    222 	if (promif_root == NULL)
    223 		return (NULL);
    224 
    225 	return (find_node_work(promif_root, nodeid));
    226 }
    227 
    228 static int
    229 getproplen(prom_node_t *pnp, char *name)
    230 {
    231 	struct prom_prop *propp;
    232 
    233 	for (propp = pnp->pn_propp; propp != NULL; propp = propp->pp_next)
    234 		if (prom_strcmp(propp->pp_name, name) == 0)
    235 			return (propp->pp_len);
    236 
    237 	return (-1);
    238 }
    239 
    240 static void *
    241 getprop(prom_node_t *np, char *name)
    242 {
    243 	struct prom_prop *propp;
    244 
    245 	for (propp = np->pn_propp; propp != NULL; propp = propp->pp_next)
    246 		if (prom_strcmp(propp->pp_name, name) == 0)
    247 			return (propp->pp_val);
    248 
    249 	return (NULL);
    250 }
    251 
    252 static char *
    253 nextprop(prom_node_t *pnp, char *name)
    254 {
    255 	struct prom_prop *propp;
    256 
    257 	/*
    258 	 * getting next of NULL or a null string returns the first prop name
    259 	 */
    260 	if (name == NULL || *name == '\0')
    261 		if (pnp->pn_propp)
    262 			return (pnp->pn_propp->pp_name);
    263 
    264 	for (propp = pnp->pn_propp; propp != NULL; propp = propp->pp_next)
    265 		if (prom_strcmp(propp->pp_name, name) == 0)
    266 			if (propp->pp_next)
    267 				return (propp->pp_next->pp_name);
    268 
    269 	return (NULL);
    270 }
    271 
    272 #ifndef _KMDB
    273 
    274 int
    275 promif_stree_setprop(pnode_t nodeid, char *name, void *value, int len)
    276 {
    277 	prom_node_t		*pnp;
    278 	struct prom_prop	*prop;
    279 
    280 	pnp = find_node(nodeid);
    281 	if (pnp == NULL) {
    282 		prom_printf("find_node: no node?\n");
    283 		return (-1);
    284 	}
    285 
    286 	/*
    287 	 * If a property with this name exists, replace the existing
    288 	 * value.
    289 	 */
    290 	for (prop = pnp->pn_propp; prop; prop = prop->pp_next)
    291 		if (prom_strcmp(prop->pp_name, name) == 0) {
    292 			/*
    293 			 * Make sure we don't get dispatched onto a
    294 			 * different cpu if we happen to sleep.  See
    295 			 * kern_postprom().
    296 			 */
    297 			thread_affinity_set(curthread, CPU->cpu_id);
    298 			kmem_free(prop->pp_val, prop->pp_len);
    299 
    300 			prop->pp_val = NULL;
    301 			if (len > 0) {
    302 				prop->pp_val = kmem_zalloc(len, KM_SLEEP);
    303 				bcopy(value, prop->pp_val, len);
    304 			}
    305 			thread_affinity_clear(curthread);
    306 			prop->pp_len = len;
    307 			return (len);
    308 		}
    309 
    310 	return (-1);
    311 }
    312 
    313 /*
    314  * Create a promif private copy of boot's device tree.
    315  */
    316 void
    317 promif_stree_init(void)
    318 {
    319 	pnode_t		node;
    320 	prom_node_t	*pnp;
    321 
    322 	node = prom_rootnode();
    323 	promif_root = pnp = create_node(OBP_NONODE, node);
    324 
    325 	create_peers(pnp, node);
    326 	create_children(pnp, node);
    327 }
    328 
    329 static void
    330 create_children(prom_node_t *pnp, pnode_t parent)
    331 {
    332 	prom_node_t	*cnp;
    333 	pnode_t		child;
    334 
    335 	_NOTE(CONSTCOND)
    336 	while (1) {
    337 		child = prom_childnode(parent);
    338 		if (child == 0)
    339 			break;
    340 		if (prom_getproplen(child, "name") <= 0) {
    341 			parent = child;
    342 			continue;
    343 		}
    344 		cnp = create_node(pnp, child);
    345 		pnp->pn_child = cnp;
    346 		create_peers(cnp, child);
    347 		pnp = cnp;
    348 		parent = child;
    349 	}
    350 }
    351 
    352 static void
    353 create_peers(prom_node_t *np, pnode_t node)
    354 {
    355 	prom_node_t	*pnp;
    356 	pnode_t		peer;
    357 
    358 	_NOTE(CONSTCOND)
    359 	while (1) {
    360 		peer = prom_nextnode(node);
    361 		if (peer == 0)
    362 			break;
    363 		if (prom_getproplen(peer, "name") <= 0) {
    364 			node = peer;
    365 			continue;
    366 		}
    367 		pnp = create_node(np->pn_parent, peer);
    368 		np->pn_sibling = pnp;
    369 		create_children(pnp, peer);
    370 		np = pnp;
    371 		node = peer;
    372 	}
    373 }
    374 
    375 static prom_node_t *
    376 create_node(prom_node_t *parent, pnode_t node)
    377 {
    378 	prom_node_t	*pnp;
    379 	char		prvname[OBP_MAXPROPNAME];
    380 	char		propname[OBP_MAXPROPNAME];
    381 	int		proplen;
    382 	void		*propval;
    383 
    384 	/*
    385 	 * Make sure we don't get dispatched onto a different
    386 	 * cpu if we happen to sleep.  See kern_postprom().
    387 	 */
    388 	thread_affinity_set(curthread, CPU->cpu_id);
    389 
    390 	pnp = kmem_zalloc(sizeof (prom_node_t), KM_SLEEP);
    391 	pnp->pn_nodeid = node;
    392 	pnp->pn_parent = parent;
    393 
    394 	prvname[0] = '\0';
    395 
    396 	_NOTE(CONSTCOND)
    397 	while (1) {
    398 		(void) prom_nextprop(node, prvname, propname);
    399 		if (prom_strlen(propname) == 0)
    400 			break;
    401 		if ((proplen = prom_getproplen(node, propname)) == -1)
    402 			continue;
    403 		propval = NULL;
    404 		if (proplen != 0) {
    405 			propval = kmem_zalloc(proplen, KM_SLEEP);
    406 			(void) prom_getprop(node, propname, propval);
    407 		}
    408 		create_prop(pnp, propname, propval, proplen);
    409 
    410 		(void) prom_strcpy(prvname, propname);
    411 	}
    412 
    413 	thread_affinity_clear(curthread);
    414 
    415 	return (pnp);
    416 }
    417 
    418 static void
    419 create_prop(prom_node_t *pnp, char *name, void *val, int len)
    420 {
    421 	struct prom_prop	*prop;
    422 	struct prom_prop	*newprop;
    423 
    424 	/*
    425 	 * Make sure we don't get dispatched onto a different
    426 	 * cpu if we happen to sleep.  See kern_postprom().
    427 	 */
    428 	thread_affinity_set(curthread, CPU->cpu_id);
    429 	newprop = kmem_zalloc(sizeof (*newprop), KM_SLEEP);
    430 	newprop->pp_name = kmem_zalloc(prom_strlen(name) + 1, KM_SLEEP);
    431 	thread_affinity_clear(curthread);
    432 
    433 	(void) prom_strcpy(newprop->pp_name, name);
    434 	newprop->pp_val = val;
    435 	newprop->pp_len = len;
    436 
    437 	if (pnp->pn_propp == NULL) {
    438 		pnp->pn_propp = newprop;
    439 		return;
    440 	}
    441 
    442 	/* move to the end of the prop list */
    443 	for (prop = pnp->pn_propp; prop->pp_next != NULL; prop = prop->pp_next)
    444 		/* empty */;
    445 
    446 	/* append the new prop */
    447 	prop->pp_next = newprop;
    448 }
    449 
    450 static void
    451 promif_dump_tree(prom_node_t *pnp)
    452 {
    453 	int		i;
    454 	static int	level = 0;
    455 
    456 	if (pnp == NULL)
    457 		return;
    458 
    459 	for (i = 0; i < level; i++) {
    460 		prom_printf("    ");
    461 	}
    462 
    463 	prom_printf("Node 0x%x (parent=0x%x, sibling=0x%x)\n", pnp->pn_nodeid,
    464 	    (pnp->pn_parent) ? pnp->pn_parent->pn_nodeid : 0,
    465 	    (pnp->pn_sibling) ? pnp->pn_sibling->pn_nodeid : 0);
    466 
    467 	if (pnp->pn_child != NULL) {
    468 		level++;
    469 		promif_dump_tree(pnp->pn_child);
    470 		level--;
    471 	}
    472 
    473 	if (pnp->pn_sibling != NULL)
    474 		promif_dump_tree(pnp->pn_sibling);
    475 }
    476 
    477 #endif
    478