xref: /openbmc/u-boot/lib/of_live.c (revision 79df00fd)
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 		if (!mem)
223 			return NULL;
224 	}
225 
226 	if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) {
227 		debug("unflatten: error %d processing FDT\n", *poffset);
228 		return NULL;
229 	}
230 
231 	/*
232 	 * Reverse the child list. Some drivers assumes node order matches .dts
233 	 * node order
234 	 */
235 	if (!dryrun && np->child) {
236 		struct device_node *child = np->child;
237 		np->child = NULL;
238 		while (child) {
239 			struct device_node *next = child->sibling;
240 
241 			child->sibling = np->child;
242 			np->child = child;
243 			child = next;
244 		}
245 	}
246 
247 	if (nodepp)
248 		*nodepp = np;
249 
250 	return mem;
251 }
252 
253 /**
254  * unflatten_device_tree() - create tree of device_nodes from flat blob
255  *
256  * unflattens a device-tree, creating the
257  * tree of struct device_node. It also fills the "name" and "type"
258  * pointers of the nodes so the normal device-tree walking functions
259  * can be used.
260  * @blob: The blob to expand
261  * @mynodes: The device_node tree created by the call
262  * @return 0 if OK, -ve on error
263  */
264 static int unflatten_device_tree(const void *blob,
265 				 struct device_node **mynodes)
266 {
267 	unsigned long size;
268 	int start;
269 	void *mem;
270 
271 	debug(" -> unflatten_device_tree()\n");
272 
273 	if (!blob) {
274 		debug("No device tree pointer\n");
275 		return -EINVAL;
276 	}
277 
278 	debug("Unflattening device tree:\n");
279 	debug("magic: %08x\n", fdt_magic(blob));
280 	debug("size: %08x\n", fdt_totalsize(blob));
281 	debug("version: %08x\n", fdt_version(blob));
282 
283 	if (fdt_check_header(blob)) {
284 		debug("Invalid device tree blob header\n");
285 		return -EINVAL;
286 	}
287 
288 	/* First pass, scan for size */
289 	start = 0;
290 	size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL,
291 						0, true);
292 	if (!size)
293 		return -EFAULT;
294 	size = ALIGN(size, 4);
295 
296 	debug("  size is %lx, allocating...\n", size);
297 
298 	/* Allocate memory for the expanded device tree */
299 	mem = malloc(size + 4);
300 	memset(mem, '\0', size);
301 
302 	*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
303 
304 	debug("  unflattening %p...\n", mem);
305 
306 	/* Second pass, do actual unflattening */
307 	start = 0;
308 	unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
309 	if (be32_to_cpup(mem + size) != 0xdeadbeef) {
310 		debug("End of tree marker overwritten: %08x\n",
311 		      be32_to_cpup(mem + size));
312 		return -ENOSPC;
313 	}
314 
315 	debug(" <- unflatten_device_tree()\n");
316 
317 	return 0;
318 }
319 
320 int of_live_build(const void *fdt_blob, struct device_node **rootp)
321 {
322 	int ret;
323 
324 	debug("%s: start\n", __func__);
325 	ret = unflatten_device_tree(fdt_blob, rootp);
326 	if (ret) {
327 		debug("Failed to create live tree: err=%d\n", ret);
328 		return ret;
329 	}
330 	ret = of_alias_scan();
331 	if (ret) {
332 		debug("Failed to scan live tree aliases: err=%d\n", ret);
333 		return ret;
334 	}
335 	debug("%s: stop\n", __func__);
336 
337 	return ret;
338 }
339