xref: /illumos-gate/usr/src/uts/intel/promif/prom_emul.c (revision 780be066c453f83f5c595e366f197251c69d2f71)
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