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