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 #include <sys/prom_emul.h>
  32 #include <sys/obpdefs.h>
  33 #include <sys/sunddi.h>
  34 
  35 static prom_node_t *promif_top;
  36 
  37 static prom_node_t *promif_find_node(pnode_t nodeid);
  38 static int getproplen(prom_node_t *pnp, char *name);
  39 static void *getprop(prom_node_t *pnp, char *name);
  40 
  41 static void
  42 promif_create_prop(prom_node_t *pnp, char *name, void *val, int len, int flags)
  43 {
  44         struct prom_prop *p, *q;
  45 
  46         q = kmem_zalloc(sizeof (*q), KM_SLEEP);
  47         q->pp_name = kmem_zalloc(strlen(name) + 1, KM_SLEEP);
  48         (void) strcpy(q->pp_name, name);
  49         q->pp_val = kmem_alloc(len, KM_SLEEP);
  50         q->pp_len = len;
  51         switch (flags) {
  52         case DDI_PROP_TYPE_INT:
  53         case DDI_PROP_TYPE_INT64:
  54                 /*
  55                  * Technically, we need byte-swapping to conform to 1275.
  56                  * However, the old x86 prom simulator used little endian
  57                  * representation, so we don't swap here either.
  58                  *
  59                  * NOTE: this is inconsistent with ddi_prop_lookup_*()
  60                  * which does byte-swapping when looking up prom properties.
  61                  * Since all kernel nodes are SID nodes, drivers no longer
  62                  * access PROM properties on x86.
  63                  */
  64         default:        /* no byte swapping */
  65                 (void) bcopy(val, q->pp_val, len);
  66                 break;
  67         }
  68 
  69         if (pnp->pn_propp == NULL) {
  70                 pnp->pn_propp = q;
  71                 return;
  72         }
  73 
  74         for (p = pnp->pn_propp; p->pp_next != NULL; p = p->pp_next)
  75                 /* empty */;
  76 
  77         p->pp_next = q;
  78 }
  79 
  80 static prom_node_t *
  81 promif_create_node(dev_info_t *dip)
  82 {
  83         prom_node_t *pnp;
  84         ddi_prop_t *hwprop;
  85         char *nodename;
  86 
  87         pnp = kmem_zalloc(sizeof (prom_node_t), KM_SLEEP);
  88         pnp->pn_nodeid = DEVI(dip)->devi_nodeid;
  89 
  90         hwprop = DEVI(dip)->devi_hw_prop_ptr;
  91         while (hwprop != NULL) {
  92                 /* need to encode to proper endianness */
  93                 promif_create_prop(pnp, hwprop->prop_name, hwprop->prop_val,
  94                     hwprop->prop_len, hwprop->prop_flags & DDI_PROP_TYPE_MASK);
  95                 hwprop = hwprop->prop_next;
  96         }
  97         nodename = ddi_node_name(dip);
  98         promif_create_prop(pnp, "name", nodename, strlen(nodename) + 1,
  99             DDI_PROP_TYPE_STRING);
 100 
 101         return (pnp);
 102 }
 103 
 104 static void promif_create_children(prom_node_t *, dev_info_t *);
 105 
 106 static void
 107 promif_create_peers(prom_node_t *pnp, dev_info_t *dip)
 108 {
 109         dev_info_t *ndip = ddi_get_next_sibling(dip);
 110 
 111         while (ndip) {
 112                 pnp->pn_sibling = promif_create_node(ndip);
 113                 promif_create_children(pnp->pn_sibling, ndip);
 114                 pnp = pnp->pn_sibling;
 115                 ndip = ddi_get_next_sibling(ndip);
 116         }
 117 }
 118 
 119 static void
 120 promif_create_children(prom_node_t *pnp, dev_info_t *dip)
 121 {
 122         dev_info_t *cdip = ddi_get_child(dip);
 123 
 124         while (cdip) {
 125                 pnp->pn_child = promif_create_node(cdip);
 126                 promif_create_peers(pnp->pn_child, cdip);
 127                 pnp = pnp->pn_child;
 128                 cdip = ddi_get_child(cdip);
 129         }
 130 }
 131 
 132 void
 133 promif_create_device_tree(void)
 134 {
 135         promif_top = promif_create_node(ddi_root_node());
 136         promif_create_children(promif_top, ddi_root_node());
 137 }
 138 
 139 static prom_node_t *
 140 find_node_work(prom_node_t *pnp, pnode_t n)
 141 {
 142         prom_node_t *qnp;
 143 
 144         if (pnp->pn_nodeid == n)
 145                 return (pnp);
 146 
 147         if (pnp->pn_child)
 148                 if ((qnp = find_node_work(pnp->pn_child, n)) != NULL)
 149                         return (qnp);
 150 
 151         if (pnp->pn_sibling)
 152                 if ((qnp = find_node_work(pnp->pn_sibling, n)) != NULL)
 153                         return (qnp);
 154 
 155         return (NULL);
 156 }
 157 
 158 static prom_node_t *
 159 promif_find_node(pnode_t nodeid)
 160 {
 161         if (nodeid == OBP_NONODE)
 162                 return (promif_top);
 163 
 164         if (promif_top == NULL)
 165                 return (NULL);
 166 
 167         return (find_node_work(promif_top, nodeid));
 168 }
 169 
 170 pnode_t
 171 promif_nextnode(pnode_t nodeid)
 172 {
 173         prom_node_t *pnp;
 174 
 175         /*
 176          * Note: next(0) returns the root node
 177          */
 178         pnp = promif_find_node(nodeid);
 179         if (pnp && (nodeid == OBP_NONODE))
 180                 return (pnp->pn_nodeid);
 181         if (pnp && pnp->pn_sibling)
 182                 return (pnp->pn_sibling->pn_nodeid);
 183 
 184         return (OBP_NONODE);
 185 }
 186 
 187 pnode_t
 188 promif_childnode(pnode_t nodeid)
 189 {
 190         prom_node_t *pnp;
 191 
 192         pnp = promif_find_node(nodeid);
 193         if (pnp && pnp->pn_child)
 194                 return (pnp->pn_child->pn_nodeid);
 195 
 196         return (OBP_NONODE);
 197 }
 198 
 199 /*
 200  * Retrieve a PROM property (len and value)
 201  */
 202 
 203 static int
 204 getproplen(prom_node_t *pnp, char *name)
 205 {
 206         struct prom_prop *propp;
 207 
 208         for (propp = pnp->pn_propp; propp != NULL; propp = propp->pp_next)
 209                 if (strcmp(propp->pp_name, name) == 0)
 210                         return (propp->pp_len);
 211 
 212         return (-1);
 213 }
 214 
 215 int
 216 promif_getproplen(pnode_t nodeid, char *name)
 217 {
 218         prom_node_t *pnp;
 219 
 220         pnp = promif_find_node(nodeid);
 221         if (pnp == NULL)
 222                 return (-1);
 223 
 224         return (getproplen(pnp, name));
 225 }
 226 
 227 static void *
 228 getprop(prom_node_t *pnp, char *name)
 229 {
 230         struct prom_prop *propp;
 231 
 232         for (propp = pnp->pn_propp; propp != NULL; propp = propp->pp_next)
 233                 if (strcmp(propp->pp_name, name) == 0)
 234                         return (propp->pp_val);
 235 
 236         return (NULL);
 237 }
 238 
 239 int
 240 promif_getprop(pnode_t nodeid, char *name, void *value)
 241 {
 242         prom_node_t *pnp;
 243         void *v;
 244         int len;
 245 
 246         pnp = promif_find_node(nodeid);
 247         if (pnp == NULL)
 248                 return (-1);
 249 
 250         len = getproplen(pnp, name);
 251         if (len > 0) {
 252                 v = getprop(pnp, name);
 253                 bcopy(v, value, len);
 254         }
 255         return (len);
 256 }
 257 
 258 static char *
 259 nextprop(prom_node_t *pnp, char *name)
 260 {
 261         struct prom_prop *propp;
 262 
 263         /*
 264          * getting next of NULL or a null string returns the first prop name
 265          */
 266         if (name == NULL || *name == '\0')
 267                 if (pnp->pn_propp)
 268                         return (pnp->pn_propp->pp_name);
 269 
 270         for (propp = pnp->pn_propp; propp != NULL; propp = propp->pp_next)
 271                 if (strcmp(propp->pp_name, name) == 0)
 272                         if (propp->pp_next)
 273                                 return (propp->pp_next->pp_name);
 274 
 275         return (NULL);
 276 }
 277 
 278 char *
 279 promif_nextprop(pnode_t nodeid, char *name, char *next)
 280 {
 281         prom_node_t *pnp;
 282         char *s;
 283 
 284         next[0] = '\0';
 285 
 286         pnp = promif_find_node(nodeid);
 287         if (pnp == NULL)
 288                 return (NULL);
 289 
 290         s = nextprop(pnp, name);
 291         if (s == NULL)
 292                 return (next);
 293 
 294         (void) strcpy(next, s);
 295         return (next);
 296 }