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
promif_create_prop(prom_node_t * pnp,char * name,void * val,int len,int flags)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 *
promif_create_node(dev_info_t * dip)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
promif_create_peers(prom_node_t * pnp,dev_info_t * dip)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
promif_create_children(prom_node_t * pnp,dev_info_t * dip)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
promif_create_device_tree(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 *
promif_find_node(pnode_t nodeid)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
promif_nextnode(pnode_t nodeid)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
promif_childnode(pnode_t nodeid)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
getproplen(prom_node_t * pnp,char * name)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
promif_getproplen(pnode_t nodeid,char * name)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 *
getprop(prom_node_t * pnp,char * name)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
promif_getprop(pnode_t nodeid,char * name,void * value)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 *
nextprop(prom_node_t * pnp,char * name)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 *
promif_nextprop(pnode_t nodeid,char * name,char * next)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