xref: /openbmc/u-boot/lib/of_live.c (revision 54c57ae0)
1 /*
2  * Copyright 2009 Benjamin Herrenschmidt, IBM Corp
3  * benh@kernel.crashing.org
4  *
5  * Based on parts of drivers/of/fdt.c from Linux v4.9
6  * Modifications for U-Boot
7  * Copyright (c) 2017 Google, Inc
8  *
9  * SPDX-License-Identifier:	GPL-2.0+
10  */
11 
12 #include <common.h>
13 #include <libfdt.h>
14 #include <of_live.h>
15 #include <malloc.h>
16 #include <dm/of_access.h>
17 #include <linux/err.h>
18 
19 DECLARE_GLOBAL_DATA_PTR;
20 
21 static void *unflatten_dt_alloc(void **mem, unsigned long size,
22 				unsigned long align)
23 {
24 	void *res;
25 
26 	*mem = PTR_ALIGN(*mem, align);
27 	res = *mem;
28 	*mem += size;
29 
30 	return res;
31 }
32 
33 /**
34  * unflatten_dt_node() - Alloc and populate a device_node from the flat tree
35  * @blob: The parent device tree blob
36  * @mem: Memory chunk to use for allocating device nodes and properties
37  * @poffset: pointer to node in flat tree
38  * @dad: Parent struct device_node
39  * @nodepp: The device_node tree created by the call
40  * @fpsize: Size of the node path up at t05he current depth.
41  * @dryrun: If true, do not allocate device nodes but still calculate needed
42  * memory size
43  */
44 static void *unflatten_dt_node(const void *blob, void *mem, int *poffset,
45 			       struct device_node *dad,
46 			       struct device_node **nodepp,
47 			       unsigned long fpsize, bool dryrun)
48 {
49 	const __be32 *p;
50 	struct device_node *np;
51 	struct property *pp, **prev_pp = NULL;
52 	const char *pathp;
53 	int l;
54 	unsigned int allocl;
55 	static int depth;
56 	int old_depth;
57 	int offset;
58 	int has_name = 0;
59 	int new_format = 0;
60 
61 	pathp = fdt_get_name(blob, *poffset, &l);
62 	if (!pathp)
63 		return mem;
64 
65 	allocl = ++l;
66 
67 	/*
68 	 * version 0x10 has a more compact unit name here instead of the full
69 	 * path. we accumulate the full path size using "fpsize", we'll rebuild
70 	 * it later. We detect this because the first character of the name is
71 	 * not '/'.
72 	 */
73 	if ((*pathp) != '/') {
74 		new_format = 1;
75 		if (fpsize == 0) {
76 			/*
77 			 * root node: special case. fpsize accounts for path
78 			 * plus terminating zero. root node only has '/', so
79 			 * fpsize should be 2, but we want to avoid the first
80 			 * level nodes to have two '/' so we use fpsize 1 here
81 			 */
82 			fpsize = 1;
83 			allocl = 2;
84 			l = 1;
85 			pathp = "";
86 		} else {
87 			/*
88 			 * account for '/' and path size minus terminal 0
89 			 * already in 'l'
90 			 */
91 			fpsize += l;
92 			allocl = fpsize;
93 		}
94 	}
95 
96 	np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
97 				__alignof__(struct device_node));
98 	if (!dryrun) {
99 		char *fn;
100 
101 		fn = (char *)np + sizeof(*np);
102 		np->full_name = fn;
103 		if (new_format) {
104 			/* rebuild full path for new format */
105 			if (dad && dad->parent) {
106 				strcpy(fn, dad->full_name);
107 #ifdef DEBUG
108 				if ((strlen(fn) + l + 1) != allocl) {
109 					debug("%s: p: %d, l: %d, a: %d\n",
110 					      pathp, (int)strlen(fn), l,
111 					      allocl);
112 				}
113 #endif
114 				fn += strlen(fn);
115 			}
116 			*(fn++) = '/';
117 		}
118 		memcpy(fn, pathp, l);
119 
120 		prev_pp = &np->properties;
121 		if (dad != NULL) {
122 			np->parent = dad;
123 			np->sibling = dad->child;
124 			dad->child = np;
125 		}
126 	}
127 	/* process properties */
128 	for (offset = fdt_first_property_offset(blob, *poffset);
129 	     (offset >= 0);
130 	     (offset = fdt_next_property_offset(blob, offset))) {
131 		const char *pname;
132 		int sz;
133 
134 		p = fdt_getprop_by_offset(blob, offset, &pname, &sz);
135 		if (!p) {
136 			offset = -FDT_ERR_INTERNAL;
137 			break;
138 		}
139 
140 		if (pname == NULL) {
141 			debug("Can't find property name in list !\n");
142 			break;
143 		}
144 		if (strcmp(pname, "name") == 0)
145 			has_name = 1;
146 		pp = unflatten_dt_alloc(&mem, sizeof(struct property),
147 					__alignof__(struct property));
148 		if (!dryrun) {
149 			/*
150 			 * We accept flattened tree phandles either in
151 			 * ePAPR-style "phandle" properties, or the
152 			 * legacy "linux,phandle" properties.  If both
153 			 * appear and have different values, things
154 			 * will get weird.  Don't do that. */
155 			if ((strcmp(pname, "phandle") == 0) ||
156 			    (strcmp(pname, "linux,phandle") == 0)) {
157 				if (np->phandle == 0)
158 					np->phandle = be32_to_cpup(p);
159 			}
160 			/*
161 			 * And we process the "ibm,phandle" property
162 			 * used in pSeries dynamic device tree
163 			 * stuff */
164 			if (strcmp(pname, "ibm,phandle") == 0)
165 				np->phandle = be32_to_cpup(p);
166 			pp->name = (char *)pname;
167 			pp->length = sz;
168 			pp->value = (__be32 *)p;
169 			*prev_pp = pp;
170 			prev_pp = &pp->next;
171 		}
172 	}
173 	/*
174 	 * with version 0x10 we may not have the name property, recreate
175 	 * it here from the unit name if absent
176 	 */
177 	if (!has_name) {
178 		const char *p1 = pathp, *ps = pathp, *pa = NULL;
179 		int sz;
180 
181 		while (*p1) {
182 			if ((*p1) == '@')
183 				pa = p1;
184 			if ((*p1) == '/')
185 				ps = p1 + 1;
186 			p1++;
187 		}
188 		if (pa < ps)
189 			pa = p1;
190 		sz = (pa - ps) + 1;
191 		pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
192 					__alignof__(struct property));
193 		if (!dryrun) {
194 			pp->name = "name";
195 			pp->length = sz;
196 			pp->value = pp + 1;
197 			*prev_pp = pp;
198 			prev_pp = &pp->next;
199 			memcpy(pp->value, ps, sz - 1);
200 			((char *)pp->value)[sz - 1] = 0;
201 			debug("fixed up name for %s -> %s\n", pathp,
202 			      (char *)pp->value);
203 		}
204 	}
205 	if (!dryrun) {
206 		*prev_pp = NULL;
207 		np->name = of_get_property(np, "name", NULL);
208 		np->type = of_get_property(np, "device_type", NULL);
209 
210 		if (!np->name)
211 			np->name = "<NULL>";
212 		if (!np->type)
213 			np->type = "<NULL>";	}
214 
215 	old_depth = depth;
216 	*poffset = fdt_next_node(blob, *poffset, &depth);
217 	if (depth < 0)
218 		depth = 0;
219 	while (*poffset > 0 && depth > old_depth)
220 		mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
221 					fpsize, dryrun);
222 
223 	if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) {
224 		debug("unflatten: error %d processing FDT\n", *poffset);
225 		return NULL;
226 	}
227 
228 	/*
229 	 * Reverse the child list. Some drivers assumes node order matches .dts
230 	 * node order
231 	 */
232 	if (!dryrun && np->child) {
233 		struct device_node *child = np->child;
234 		np->child = NULL;
235 		while (child) {
236 			struct device_node *next = child->sibling;
237 
238 			child->sibling = np->child;
239 			np->child = child;
240 			child = next;
241 		}
242 	}
243 
244 	if (nodepp)
245 		*nodepp = np;
246 
247 	return mem;
248 }
249 
250 /**
251  * unflatten_device_tree() - create tree of device_nodes from flat blob
252  *
253  * unflattens a device-tree, creating the
254  * tree of struct device_node. It also fills the "name" and "type"
255  * pointers of the nodes so the normal device-tree walking functions
256  * can be used.
257  * @blob: The blob to expand
258  * @mynodes: The device_node tree created by the call
259  * @return 0 if OK, -ve on error
260  */
261 static int unflatten_device_tree(const void *blob,
262 				 struct device_node **mynodes)
263 {
264 	unsigned long size;
265 	int start;
266 	void *mem;
267 
268 	debug(" -> unflatten_device_tree()\n");
269 
270 	if (!blob) {
271 		debug("No device tree pointer\n");
272 		return -EINVAL;
273 	}
274 
275 	debug("Unflattening device tree:\n");
276 	debug("magic: %08x\n", fdt_magic(blob));
277 	debug("size: %08x\n", fdt_totalsize(blob));
278 	debug("version: %08x\n", fdt_version(blob));
279 
280 	if (fdt_check_header(blob)) {
281 		debug("Invalid device tree blob header\n");
282 		return -EINVAL;
283 	}
284 
285 	/* First pass, scan for size */
286 	start = 0;
287 	size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL,
288 						0, true);
289 	size = ALIGN(size, 4);
290 
291 	debug("  size is %lx, allocating...\n", size);
292 
293 	/* Allocate memory for the expanded device tree */
294 	mem = malloc(size + 4);
295 	memset(mem, '\0', size);
296 
297 	*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
298 
299 	debug("  unflattening %p...\n", mem);
300 
301 	/* Second pass, do actual unflattening */
302 	start = 0;
303 	unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
304 	if (be32_to_cpup(mem + size) != 0xdeadbeef) {
305 		debug("End of tree marker overwritten: %08x\n",
306 		      be32_to_cpup(mem + size));
307 		return -ENOSPC;
308 	}
309 
310 	debug(" <- unflatten_device_tree()\n");
311 
312 	return 0;
313 }
314 
315 int of_live_build(const void *fdt_blob, struct device_node **rootp)
316 {
317 	int ret;
318 
319 	debug("%s: start\n", __func__);
320 	ret = unflatten_device_tree(fdt_blob, rootp);
321 	if (ret) {
322 		debug("Failed to create live tree: err=%d\n", ret);
323 		return ret;
324 	}
325 	ret = of_alias_scan();
326 	if (ret) {
327 		debug("Failed to scan live tree aliases: err=%d\n", ret);
328 		return ret;
329 	}
330 	debug("%s: stop\n", __func__);
331 
332 	return ret;
333 }
334