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