1314985Sgonzo#include "libfdt_env.h"
2314985Sgonzo
3314985Sgonzo#include <fdt.h>
4314985Sgonzo#include <libfdt.h>
5314985Sgonzo
6314985Sgonzo#include "libfdt_internal.h"
7314985Sgonzo
8314985Sgonzo/**
9314985Sgonzo * overlay_get_target_phandle - retrieves the target phandle of a fragment
10314985Sgonzo * @fdto: pointer to the device tree overlay blob
11314985Sgonzo * @fragment: node offset of the fragment in the overlay
12314985Sgonzo *
13314985Sgonzo * overlay_get_target_phandle() retrieves the target phandle of an
14314985Sgonzo * overlay fragment when that fragment uses a phandle (target
15314985Sgonzo * property) instead of a path (target-path property).
16314985Sgonzo *
17314985Sgonzo * returns:
18314985Sgonzo *      the phandle pointed by the target property
19314985Sgonzo *      0, if the phandle was not found
20314985Sgonzo *	-1, if the phandle was malformed
21314985Sgonzo */
22314985Sgonzostatic uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
23314985Sgonzo{
24314985Sgonzo	const uint32_t *val;
25314985Sgonzo	int len;
26314985Sgonzo
27314985Sgonzo	val = fdt_getprop(fdto, fragment, "target", &len);
28314985Sgonzo	if (!val)
29314985Sgonzo		return 0;
30314985Sgonzo
31314985Sgonzo	if ((len != sizeof(*val)) || (*val == (uint32_t)-1))
32314985Sgonzo		return (uint32_t)-1;
33314985Sgonzo
34314985Sgonzo	return fdt32_to_cpu(*val);
35314985Sgonzo}
36314985Sgonzo
37314985Sgonzo/**
38314985Sgonzo * overlay_get_target - retrieves the offset of a fragment's target
39314985Sgonzo * @fdt: Base device tree blob
40314985Sgonzo * @fdto: Device tree overlay blob
41314985Sgonzo * @fragment: node offset of the fragment in the overlay
42314985Sgonzo *
43314985Sgonzo * overlay_get_target() retrieves the target offset in the base
44314985Sgonzo * device tree of a fragment, no matter how the actual targetting is
45314985Sgonzo * done (through a phandle or a path)
46314985Sgonzo *
47314985Sgonzo * returns:
48314985Sgonzo *      the targetted node offset in the base device tree
49314985Sgonzo *      Negative error code on error
50314985Sgonzo */
51314985Sgonzostatic int overlay_get_target(const void *fdt, const void *fdto,
52314985Sgonzo			      int fragment)
53314985Sgonzo{
54314985Sgonzo	uint32_t phandle;
55314985Sgonzo	const char *path;
56314985Sgonzo	int path_len;
57314985Sgonzo
58314985Sgonzo	/* Try first to do a phandle based lookup */
59314985Sgonzo	phandle = overlay_get_target_phandle(fdto, fragment);
60314985Sgonzo	if (phandle == (uint32_t)-1)
61314985Sgonzo		return -FDT_ERR_BADPHANDLE;
62314985Sgonzo
63314985Sgonzo	if (phandle)
64314985Sgonzo		return fdt_node_offset_by_phandle(fdt, phandle);
65314985Sgonzo
66314985Sgonzo	/* And then a path based lookup */
67314985Sgonzo	path = fdt_getprop(fdto, fragment, "target-path", &path_len);
68314985Sgonzo	if (!path) {
69314985Sgonzo		/*
70314985Sgonzo		 * If we haven't found either a target or a
71314985Sgonzo		 * target-path property in a node that contains a
72314985Sgonzo		 * __overlay__ subnode (we wouldn't be called
73314985Sgonzo		 * otherwise), consider it a improperly written
74314985Sgonzo		 * overlay
75314985Sgonzo		 */
76314985Sgonzo		if (path_len == -FDT_ERR_NOTFOUND)
77314985Sgonzo			return -FDT_ERR_BADOVERLAY;
78314985Sgonzo
79314985Sgonzo		return path_len;
80314985Sgonzo	}
81314985Sgonzo
82314985Sgonzo	return fdt_path_offset(fdt, path);
83314985Sgonzo}
84314985Sgonzo
85314985Sgonzo/**
86314985Sgonzo * overlay_phandle_add_offset - Increases a phandle by an offset
87314985Sgonzo * @fdt: Base device tree blob
88314985Sgonzo * @node: Device tree overlay blob
89314985Sgonzo * @name: Name of the property to modify (phandle or linux,phandle)
90314985Sgonzo * @delta: offset to apply
91314985Sgonzo *
92314985Sgonzo * overlay_phandle_add_offset() increments a node phandle by a given
93314985Sgonzo * offset.
94314985Sgonzo *
95314985Sgonzo * returns:
96314985Sgonzo *      0 on success.
97314985Sgonzo *      Negative error code on error
98314985Sgonzo */
99314985Sgonzostatic int overlay_phandle_add_offset(void *fdt, int node,
100314985Sgonzo				      const char *name, uint32_t delta)
101314985Sgonzo{
102314985Sgonzo	const uint32_t *val;
103314985Sgonzo	uint32_t adj_val;
104314985Sgonzo	int len;
105314985Sgonzo
106314985Sgonzo	val = fdt_getprop(fdt, node, name, &len);
107314985Sgonzo	if (!val)
108314985Sgonzo		return len;
109314985Sgonzo
110314985Sgonzo	if (len != sizeof(*val))
111314985Sgonzo		return -FDT_ERR_BADPHANDLE;
112314985Sgonzo
113314985Sgonzo	adj_val = fdt32_to_cpu(*val);
114314985Sgonzo	if ((adj_val + delta) < adj_val)
115314985Sgonzo		return -FDT_ERR_NOPHANDLES;
116314985Sgonzo
117314985Sgonzo	adj_val += delta;
118314985Sgonzo	if (adj_val == (uint32_t)-1)
119314985Sgonzo		return -FDT_ERR_NOPHANDLES;
120314985Sgonzo
121314985Sgonzo	return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
122314985Sgonzo}
123314985Sgonzo
124314985Sgonzo/**
125314985Sgonzo * overlay_adjust_node_phandles - Offsets the phandles of a node
126314985Sgonzo * @fdto: Device tree overlay blob
127314985Sgonzo * @node: Offset of the node we want to adjust
128314985Sgonzo * @delta: Offset to shift the phandles of
129314985Sgonzo *
130314985Sgonzo * overlay_adjust_node_phandles() adds a constant to all the phandles
131314985Sgonzo * of a given node. This is mainly use as part of the overlay
132314985Sgonzo * application process, when we want to update all the overlay
133314985Sgonzo * phandles to not conflict with the overlays of the base device tree.
134314985Sgonzo *
135314985Sgonzo * returns:
136314985Sgonzo *      0 on success
137314985Sgonzo *      Negative error code on failure
138314985Sgonzo */
139314985Sgonzostatic int overlay_adjust_node_phandles(void *fdto, int node,
140314985Sgonzo					uint32_t delta)
141314985Sgonzo{
142314985Sgonzo	int child;
143314985Sgonzo	int ret;
144314985Sgonzo
145314985Sgonzo	ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
146314985Sgonzo	if (ret && ret != -FDT_ERR_NOTFOUND)
147314985Sgonzo		return ret;
148314985Sgonzo
149314985Sgonzo	ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
150314985Sgonzo	if (ret && ret != -FDT_ERR_NOTFOUND)
151314985Sgonzo		return ret;
152314985Sgonzo
153314985Sgonzo	fdt_for_each_subnode(child, fdto, node) {
154314985Sgonzo		ret = overlay_adjust_node_phandles(fdto, child, delta);
155314985Sgonzo		if (ret)
156314985Sgonzo			return ret;
157314985Sgonzo	}
158314985Sgonzo
159314985Sgonzo	return 0;
160314985Sgonzo}
161314985Sgonzo
162314985Sgonzo/**
163314985Sgonzo * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
164314985Sgonzo * @fdto: Device tree overlay blob
165314985Sgonzo * @delta: Offset to shift the phandles of
166314985Sgonzo *
167314985Sgonzo * overlay_adjust_local_phandles() adds a constant to all the
168314985Sgonzo * phandles of an overlay. This is mainly use as part of the overlay
169314985Sgonzo * application process, when we want to update all the overlay
170314985Sgonzo * phandles to not conflict with the overlays of the base device tree.
171314985Sgonzo *
172314985Sgonzo * returns:
173314985Sgonzo *      0 on success
174314985Sgonzo *      Negative error code on failure
175314985Sgonzo */
176314985Sgonzostatic int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
177314985Sgonzo{
178314985Sgonzo	/*
179314985Sgonzo	 * Start adjusting the phandles from the overlay root
180314985Sgonzo	 */
181314985Sgonzo	return overlay_adjust_node_phandles(fdto, 0, delta);
182314985Sgonzo}
183314985Sgonzo
184314985Sgonzo/**
185314985Sgonzo * overlay_update_local_node_references - Adjust the overlay references
186314985Sgonzo * @fdto: Device tree overlay blob
187314985Sgonzo * @tree_node: Node offset of the node to operate on
188314985Sgonzo * @fixup_node: Node offset of the matching local fixups node
189314985Sgonzo * @delta: Offset to shift the phandles of
190314985Sgonzo *
191314985Sgonzo * overlay_update_local_nodes_references() update the phandles
192314985Sgonzo * pointing to a node within the device tree overlay by adding a
193314985Sgonzo * constant delta.
194314985Sgonzo *
195314985Sgonzo * This is mainly used as part of a device tree application process,
196314985Sgonzo * where you want the device tree overlays phandles to not conflict
197314985Sgonzo * with the ones from the base device tree before merging them.
198314985Sgonzo *
199314985Sgonzo * returns:
200314985Sgonzo *      0 on success
201314985Sgonzo *      Negative error code on failure
202314985Sgonzo */
203314985Sgonzostatic int overlay_update_local_node_references(void *fdto,
204314985Sgonzo						int tree_node,
205314985Sgonzo						int fixup_node,
206314985Sgonzo						uint32_t delta)
207314985Sgonzo{
208314985Sgonzo	int fixup_prop;
209314985Sgonzo	int fixup_child;
210314985Sgonzo	int ret;
211314985Sgonzo
212314985Sgonzo	fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
213314985Sgonzo		const uint32_t *fixup_val;
214314985Sgonzo		const char *tree_val;
215314985Sgonzo		const char *name;
216314985Sgonzo		int fixup_len;
217314985Sgonzo		int tree_len;
218314985Sgonzo		int i;
219314985Sgonzo
220314985Sgonzo		fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
221314985Sgonzo						  &name, &fixup_len);
222314985Sgonzo		if (!fixup_val)
223314985Sgonzo			return fixup_len;
224314985Sgonzo
225314985Sgonzo		if (fixup_len % sizeof(uint32_t))
226314985Sgonzo			return -FDT_ERR_BADOVERLAY;
227314985Sgonzo
228314985Sgonzo		tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
229314985Sgonzo		if (!tree_val) {
230314985Sgonzo			if (tree_len == -FDT_ERR_NOTFOUND)
231314985Sgonzo				return -FDT_ERR_BADOVERLAY;
232314985Sgonzo
233314985Sgonzo			return tree_len;
234314985Sgonzo		}
235314985Sgonzo
236314985Sgonzo		for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
237314985Sgonzo			uint32_t adj_val, poffset;
238314985Sgonzo
239314985Sgonzo			poffset = fdt32_to_cpu(fixup_val[i]);
240314985Sgonzo
241314985Sgonzo			/*
242314985Sgonzo			 * phandles to fixup can be unaligned.
243314985Sgonzo			 *
244314985Sgonzo			 * Use a memcpy for the architectures that do
245314985Sgonzo			 * not support unaligned accesses.
246314985Sgonzo			 */
247314985Sgonzo			memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
248314985Sgonzo
249314985Sgonzo			adj_val = fdt32_to_cpu(adj_val);
250314985Sgonzo			adj_val += delta;
251314985Sgonzo			adj_val = cpu_to_fdt32(adj_val);
252314985Sgonzo
253314985Sgonzo			ret = fdt_setprop_inplace_namelen_partial(fdto,
254314985Sgonzo								  tree_node,
255314985Sgonzo								  name,
256314985Sgonzo								  strlen(name),
257314985Sgonzo								  poffset,
258314985Sgonzo								  &adj_val,
259314985Sgonzo								  sizeof(adj_val));
260314985Sgonzo			if (ret == -FDT_ERR_NOSPACE)
261314985Sgonzo				return -FDT_ERR_BADOVERLAY;
262314985Sgonzo
263314985Sgonzo			if (ret)
264314985Sgonzo				return ret;
265314985Sgonzo		}
266314985Sgonzo	}
267314985Sgonzo
268314985Sgonzo	fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
269314985Sgonzo		const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
270314985Sgonzo							    NULL);
271314985Sgonzo		int tree_child;
272314985Sgonzo
273314985Sgonzo		tree_child = fdt_subnode_offset(fdto, tree_node,
274314985Sgonzo						fixup_child_name);
275314985Sgonzo		if (ret == -FDT_ERR_NOTFOUND)
276314985Sgonzo			return -FDT_ERR_BADOVERLAY;
277314985Sgonzo		if (tree_child < 0)
278314985Sgonzo			return tree_child;
279314985Sgonzo
280314985Sgonzo		ret = overlay_update_local_node_references(fdto,
281314985Sgonzo							   tree_child,
282314985Sgonzo							   fixup_child,
283314985Sgonzo							   delta);
284314985Sgonzo		if (ret)
285314985Sgonzo			return ret;
286314985Sgonzo	}
287314985Sgonzo
288314985Sgonzo	return 0;
289314985Sgonzo}
290314985Sgonzo
291314985Sgonzo/**
292314985Sgonzo * overlay_update_local_references - Adjust the overlay references
293314985Sgonzo * @fdto: Device tree overlay blob
294314985Sgonzo * @delta: Offset to shift the phandles of
295314985Sgonzo *
296314985Sgonzo * overlay_update_local_references() update all the phandles pointing
297314985Sgonzo * to a node within the device tree overlay by adding a constant
298314985Sgonzo * delta to not conflict with the base overlay.
299314985Sgonzo *
300314985Sgonzo * This is mainly used as part of a device tree application process,
301314985Sgonzo * where you want the device tree overlays phandles to not conflict
302314985Sgonzo * with the ones from the base device tree before merging them.
303314985Sgonzo *
304314985Sgonzo * returns:
305314985Sgonzo *      0 on success
306314985Sgonzo *      Negative error code on failure
307314985Sgonzo */
308314985Sgonzostatic int overlay_update_local_references(void *fdto, uint32_t delta)
309314985Sgonzo{
310314985Sgonzo	int fixups;
311314985Sgonzo
312314985Sgonzo	fixups = fdt_path_offset(fdto, "/__local_fixups__");
313314985Sgonzo	if (fixups < 0) {
314314985Sgonzo		/* There's no local phandles to adjust, bail out */
315314985Sgonzo		if (fixups == -FDT_ERR_NOTFOUND)
316314985Sgonzo			return 0;
317314985Sgonzo
318314985Sgonzo		return fixups;
319314985Sgonzo	}
320314985Sgonzo
321314985Sgonzo	/*
322314985Sgonzo	 * Update our local references from the root of the tree
323314985Sgonzo	 */
324314985Sgonzo	return overlay_update_local_node_references(fdto, 0, fixups,
325314985Sgonzo						    delta);
326314985Sgonzo}
327314985Sgonzo
328314985Sgonzo/**
329314985Sgonzo * overlay_fixup_one_phandle - Set an overlay phandle to the base one
330314985Sgonzo * @fdt: Base Device Tree blob
331314985Sgonzo * @fdto: Device tree overlay blob
332314985Sgonzo * @symbols_off: Node offset of the symbols node in the base device tree
333314985Sgonzo * @path: Path to a node holding a phandle in the overlay
334314985Sgonzo * @path_len: number of path characters to consider
335314985Sgonzo * @name: Name of the property holding the phandle reference in the overlay
336314985Sgonzo * @name_len: number of name characters to consider
337314985Sgonzo * @poffset: Offset within the overlay property where the phandle is stored
338314985Sgonzo * @label: Label of the node referenced by the phandle
339314985Sgonzo *
340314985Sgonzo * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
341314985Sgonzo * a node in the base device tree.
342314985Sgonzo *
343314985Sgonzo * This is part of the device tree overlay application process, when
344314985Sgonzo * you want all the phandles in the overlay to point to the actual
345314985Sgonzo * base dt nodes.
346314985Sgonzo *
347314985Sgonzo * returns:
348314985Sgonzo *      0 on success
349314985Sgonzo *      Negative error code on failure
350314985Sgonzo */
351314985Sgonzostatic int overlay_fixup_one_phandle(void *fdt, void *fdto,
352314985Sgonzo				     int symbols_off,
353314985Sgonzo				     const char *path, uint32_t path_len,
354314985Sgonzo				     const char *name, uint32_t name_len,
355314985Sgonzo				     int poffset, const char *label)
356314985Sgonzo{
357314985Sgonzo	const char *symbol_path;
358314985Sgonzo	uint32_t phandle;
359314985Sgonzo	int symbol_off, fixup_off;
360314985Sgonzo	int prop_len;
361314985Sgonzo
362314985Sgonzo	if (symbols_off < 0)
363314985Sgonzo		return symbols_off;
364314985Sgonzo
365314985Sgonzo	symbol_path = fdt_getprop(fdt, symbols_off, label,
366314985Sgonzo				  &prop_len);
367314985Sgonzo	if (!symbol_path)
368314985Sgonzo		return prop_len;
369314985Sgonzo
370314985Sgonzo	symbol_off = fdt_path_offset(fdt, symbol_path);
371314985Sgonzo	if (symbol_off < 0)
372314985Sgonzo		return symbol_off;
373314985Sgonzo
374314985Sgonzo	phandle = fdt_get_phandle(fdt, symbol_off);
375314985Sgonzo	if (!phandle)
376314985Sgonzo		return -FDT_ERR_NOTFOUND;
377314985Sgonzo
378314985Sgonzo	fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
379314985Sgonzo	if (fixup_off == -FDT_ERR_NOTFOUND)
380314985Sgonzo		return -FDT_ERR_BADOVERLAY;
381314985Sgonzo	if (fixup_off < 0)
382314985Sgonzo		return fixup_off;
383314985Sgonzo
384314985Sgonzo	phandle = cpu_to_fdt32(phandle);
385314985Sgonzo	return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
386314985Sgonzo						   name, name_len, poffset,
387314985Sgonzo						   &phandle, sizeof(phandle));
388314985Sgonzo};
389314985Sgonzo
390314985Sgonzo/**
391314985Sgonzo * overlay_fixup_phandle - Set an overlay phandle to the base one
392314985Sgonzo * @fdt: Base Device Tree blob
393314985Sgonzo * @fdto: Device tree overlay blob
394314985Sgonzo * @symbols_off: Node offset of the symbols node in the base device tree
395314985Sgonzo * @property: Property offset in the overlay holding the list of fixups
396314985Sgonzo *
397314985Sgonzo * overlay_fixup_phandle() resolves all the overlay phandles pointed
398314985Sgonzo * to in a __fixups__ property, and updates them to match the phandles
399314985Sgonzo * in use in the base device tree.
400314985Sgonzo *
401314985Sgonzo * This is part of the device tree overlay application process, when
402314985Sgonzo * you want all the phandles in the overlay to point to the actual
403314985Sgonzo * base dt nodes.
404314985Sgonzo *
405314985Sgonzo * returns:
406314985Sgonzo *      0 on success
407314985Sgonzo *      Negative error code on failure
408314985Sgonzo */
409314985Sgonzostatic int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
410314985Sgonzo				 int property)
411314985Sgonzo{
412314985Sgonzo	const char *value;
413314985Sgonzo	const char *label;
414314985Sgonzo	int len;
415314985Sgonzo
416314985Sgonzo	value = fdt_getprop_by_offset(fdto, property,
417314985Sgonzo				      &label, &len);
418314985Sgonzo	if (!value) {
419314985Sgonzo		if (len == -FDT_ERR_NOTFOUND)
420314985Sgonzo			return -FDT_ERR_INTERNAL;
421314985Sgonzo
422314985Sgonzo		return len;
423314985Sgonzo	}
424314985Sgonzo
425314985Sgonzo	do {
426314985Sgonzo		const char *path, *name, *fixup_end;
427314985Sgonzo		const char *fixup_str = value;
428314985Sgonzo		uint32_t path_len, name_len;
429314985Sgonzo		uint32_t fixup_len;
430314985Sgonzo		char *sep, *endptr;
431314985Sgonzo		int poffset, ret;
432314985Sgonzo
433314985Sgonzo		fixup_end = memchr(value, '\0', len);
434314985Sgonzo		if (!fixup_end)
435314985Sgonzo			return -FDT_ERR_BADOVERLAY;
436314985Sgonzo		fixup_len = fixup_end - fixup_str;
437314985Sgonzo
438314985Sgonzo		len -= fixup_len + 1;
439314985Sgonzo		value += fixup_len + 1;
440314985Sgonzo
441314985Sgonzo		path = fixup_str;
442314985Sgonzo		sep = memchr(fixup_str, ':', fixup_len);
443314985Sgonzo		if (!sep || *sep != ':')
444314985Sgonzo			return -FDT_ERR_BADOVERLAY;
445314985Sgonzo
446314985Sgonzo		path_len = sep - path;
447314985Sgonzo		if (path_len == (fixup_len - 1))
448314985Sgonzo			return -FDT_ERR_BADOVERLAY;
449314985Sgonzo
450314985Sgonzo		fixup_len -= path_len + 1;
451314985Sgonzo		name = sep + 1;
452314985Sgonzo		sep = memchr(name, ':', fixup_len);
453314985Sgonzo		if (!sep || *sep != ':')
454314985Sgonzo			return -FDT_ERR_BADOVERLAY;
455314985Sgonzo
456314985Sgonzo		name_len = sep - name;
457314985Sgonzo		if (!name_len)
458314985Sgonzo			return -FDT_ERR_BADOVERLAY;
459314985Sgonzo
460314985Sgonzo		poffset = strtoul(sep + 1, &endptr, 10);
461314985Sgonzo		if ((*endptr != '\0') || (endptr <= (sep + 1)))
462314985Sgonzo			return -FDT_ERR_BADOVERLAY;
463314985Sgonzo
464314985Sgonzo		ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
465314985Sgonzo						path, path_len, name, name_len,
466314985Sgonzo						poffset, label);
467314985Sgonzo		if (ret)
468314985Sgonzo			return ret;
469314985Sgonzo	} while (len > 0);
470314985Sgonzo
471314985Sgonzo	return 0;
472314985Sgonzo}
473314985Sgonzo
474314985Sgonzo/**
475314985Sgonzo * overlay_fixup_phandles - Resolve the overlay phandles to the base
476314985Sgonzo *                          device tree
477314985Sgonzo * @fdt: Base Device Tree blob
478314985Sgonzo * @fdto: Device tree overlay blob
479314985Sgonzo *
480314985Sgonzo * overlay_fixup_phandles() resolves all the overlay phandles pointing
481314985Sgonzo * to nodes in the base device tree.
482314985Sgonzo *
483314985Sgonzo * This is one of the steps of the device tree overlay application
484314985Sgonzo * process, when you want all the phandles in the overlay to point to
485314985Sgonzo * the actual base dt nodes.
486314985Sgonzo *
487314985Sgonzo * returns:
488314985Sgonzo *      0 on success
489314985Sgonzo *      Negative error code on failure
490314985Sgonzo */
491314985Sgonzostatic int overlay_fixup_phandles(void *fdt, void *fdto)
492314985Sgonzo{
493314985Sgonzo	int fixups_off, symbols_off;
494314985Sgonzo	int property;
495314985Sgonzo
496314985Sgonzo	/* We can have overlays without any fixups */
497314985Sgonzo	fixups_off = fdt_path_offset(fdto, "/__fixups__");
498314985Sgonzo	if (fixups_off == -FDT_ERR_NOTFOUND)
499314985Sgonzo		return 0; /* nothing to do */
500314985Sgonzo	if (fixups_off < 0)
501314985Sgonzo		return fixups_off;
502314985Sgonzo
503314985Sgonzo	/* And base DTs without symbols */
504314985Sgonzo	symbols_off = fdt_path_offset(fdt, "/__symbols__");
505314985Sgonzo	if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
506314985Sgonzo		return symbols_off;
507314985Sgonzo
508314985Sgonzo	fdt_for_each_property_offset(property, fdto, fixups_off) {
509314985Sgonzo		int ret;
510314985Sgonzo
511314985Sgonzo		ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
512314985Sgonzo		if (ret)
513314985Sgonzo			return ret;
514314985Sgonzo	}
515314985Sgonzo
516314985Sgonzo	return 0;
517314985Sgonzo}
518314985Sgonzo
519314985Sgonzo/**
520314985Sgonzo * overlay_apply_node - Merges a node into the base device tree
521314985Sgonzo * @fdt: Base Device Tree blob
522314985Sgonzo * @target: Node offset in the base device tree to apply the fragment to
523314985Sgonzo * @fdto: Device tree overlay blob
524314985Sgonzo * @node: Node offset in the overlay holding the changes to merge
525314985Sgonzo *
526314985Sgonzo * overlay_apply_node() merges a node into a target base device tree
527314985Sgonzo * node pointed.
528314985Sgonzo *
529314985Sgonzo * This is part of the final step in the device tree overlay
530314985Sgonzo * application process, when all the phandles have been adjusted and
531314985Sgonzo * resolved and you just have to merge overlay into the base device
532314985Sgonzo * tree.
533314985Sgonzo *
534314985Sgonzo * returns:
535314985Sgonzo *      0 on success
536314985Sgonzo *      Negative error code on failure
537314985Sgonzo */
538314985Sgonzostatic int overlay_apply_node(void *fdt, int target,
539314985Sgonzo			      void *fdto, int node)
540314985Sgonzo{
541314985Sgonzo	int property;
542314985Sgonzo	int subnode;
543314985Sgonzo
544314985Sgonzo	fdt_for_each_property_offset(property, fdto, node) {
545314985Sgonzo		const char *name;
546314985Sgonzo		const void *prop;
547314985Sgonzo		int prop_len;
548314985Sgonzo		int ret;
549314985Sgonzo
550314985Sgonzo		prop = fdt_getprop_by_offset(fdto, property, &name,
551314985Sgonzo					     &prop_len);
552314985Sgonzo		if (prop_len == -FDT_ERR_NOTFOUND)
553314985Sgonzo			return -FDT_ERR_INTERNAL;
554314985Sgonzo		if (prop_len < 0)
555314985Sgonzo			return prop_len;
556314985Sgonzo
557314985Sgonzo		ret = fdt_setprop(fdt, target, name, prop, prop_len);
558314985Sgonzo		if (ret)
559314985Sgonzo			return ret;
560314985Sgonzo	}
561314985Sgonzo
562314985Sgonzo	fdt_for_each_subnode(subnode, fdto, node) {
563314985Sgonzo		const char *name = fdt_get_name(fdto, subnode, NULL);
564314985Sgonzo		int nnode;
565314985Sgonzo		int ret;
566314985Sgonzo
567314985Sgonzo		nnode = fdt_add_subnode(fdt, target, name);
568314985Sgonzo		if (nnode == -FDT_ERR_EXISTS) {
569314985Sgonzo			nnode = fdt_subnode_offset(fdt, target, name);
570314985Sgonzo			if (nnode == -FDT_ERR_NOTFOUND)
571314985Sgonzo				return -FDT_ERR_INTERNAL;
572314985Sgonzo		}
573314985Sgonzo
574314985Sgonzo		if (nnode < 0)
575314985Sgonzo			return nnode;
576314985Sgonzo
577314985Sgonzo		ret = overlay_apply_node(fdt, nnode, fdto, subnode);
578314985Sgonzo		if (ret)
579314985Sgonzo			return ret;
580314985Sgonzo	}
581314985Sgonzo
582314985Sgonzo	return 0;
583314985Sgonzo}
584314985Sgonzo
585314985Sgonzo/**
586314985Sgonzo * overlay_merge - Merge an overlay into its base device tree
587314985Sgonzo * @fdt: Base Device Tree blob
588314985Sgonzo * @fdto: Device tree overlay blob
589314985Sgonzo *
590314985Sgonzo * overlay_merge() merges an overlay into its base device tree.
591314985Sgonzo *
592314985Sgonzo * This is the final step in the device tree overlay application
593314985Sgonzo * process, when all the phandles have been adjusted and resolved and
594314985Sgonzo * you just have to merge overlay into the base device tree.
595314985Sgonzo *
596314985Sgonzo * returns:
597314985Sgonzo *      0 on success
598314985Sgonzo *      Negative error code on failure
599314985Sgonzo */
600314985Sgonzostatic int overlay_merge(void *fdt, void *fdto)
601314985Sgonzo{
602314985Sgonzo	int fragment;
603314985Sgonzo
604314985Sgonzo	fdt_for_each_subnode(fragment, fdto, 0) {
605314985Sgonzo		int overlay;
606314985Sgonzo		int target;
607314985Sgonzo		int ret;
608314985Sgonzo
609314985Sgonzo		/*
610314985Sgonzo		 * Each fragments will have an __overlay__ node. If
611314985Sgonzo		 * they don't, it's not supposed to be merged
612314985Sgonzo		 */
613314985Sgonzo		overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
614314985Sgonzo		if (overlay == -FDT_ERR_NOTFOUND)
615314985Sgonzo			continue;
616314985Sgonzo
617314985Sgonzo		if (overlay < 0)
618314985Sgonzo			return overlay;
619314985Sgonzo
620314985Sgonzo		target = overlay_get_target(fdt, fdto, fragment);
621314985Sgonzo		if (target < 0)
622314985Sgonzo			return target;
623314985Sgonzo
624314985Sgonzo		ret = overlay_apply_node(fdt, target, fdto, overlay);
625314985Sgonzo		if (ret)
626314985Sgonzo			return ret;
627314985Sgonzo	}
628314985Sgonzo
629314985Sgonzo	return 0;
630314985Sgonzo}
631314985Sgonzo
632314985Sgonzoint fdt_overlay_apply(void *fdt, void *fdto)
633314985Sgonzo{
634314985Sgonzo	uint32_t delta = fdt_get_max_phandle(fdt);
635314985Sgonzo	int ret;
636314985Sgonzo
637314985Sgonzo	FDT_CHECK_HEADER(fdt);
638314985Sgonzo	FDT_CHECK_HEADER(fdto);
639314985Sgonzo
640314985Sgonzo	ret = overlay_adjust_local_phandles(fdto, delta);
641314985Sgonzo	if (ret)
642314985Sgonzo		goto err;
643314985Sgonzo
644314985Sgonzo	ret = overlay_update_local_references(fdto, delta);
645314985Sgonzo	if (ret)
646314985Sgonzo		goto err;
647314985Sgonzo
648314985Sgonzo	ret = overlay_fixup_phandles(fdt, fdto);
649314985Sgonzo	if (ret)
650314985Sgonzo		goto err;
651314985Sgonzo
652314985Sgonzo	ret = overlay_merge(fdt, fdto);
653314985Sgonzo	if (ret)
654314985Sgonzo		goto err;
655314985Sgonzo
656314985Sgonzo	/*
657314985Sgonzo	 * The overlay has been damaged, erase its magic.
658314985Sgonzo	 */
659314985Sgonzo	fdt_set_magic(fdto, ~0);
660314985Sgonzo
661314985Sgonzo	return 0;
662314985Sgonzo
663314985Sgonzoerr:
664314985Sgonzo	/*
665314985Sgonzo	 * The overlay might have been damaged, erase its magic.
666314985Sgonzo	 */
667314985Sgonzo	fdt_set_magic(fdto, ~0);
668314985Sgonzo
669314985Sgonzo	/*
670314985Sgonzo	 * The base device tree might have been damaged, erase its
671314985Sgonzo	 * magic.
672314985Sgonzo	 */
673314985Sgonzo	fdt_set_magic(fdt, ~0);
674314985Sgonzo
675314985Sgonzo	return ret;
676314985Sgonzo}
677