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 } 299