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 2007 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 kmem_free(prop->pp_val, prop->pp_len); 293 prop->pp_val = NULL; 294 if (len > 0) { 295 /* 296 * Make sure we don't get dispatched onto a 297 * different cpu if we happen to sleep. See 298 * kern_postprom(). 299 */ 300 thread_affinity_set(curthread, CPU_CURRENT); 301 prop->pp_val = kmem_zalloc(len, KM_SLEEP); 302 thread_affinity_clear(curthread); 303 304 bcopy(value, prop->pp_val, len); 305 } 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_CURRENT); 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_CURRENT); 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