fdt_overlay.c revision 328459
1328459Skevans#include "libfdt_env.h"
2328459Skevans
3328459Skevans#include <fdt.h>
4328459Skevans#include <libfdt.h>
5328459Skevans
6328459Skevans#include "libfdt_internal.h"
7328459Skevans
8328459Skevans/**
9328459Skevans * overlay_get_target_phandle - retrieves the target phandle of a fragment
10328459Skevans * @fdto: pointer to the device tree overlay blob
11328459Skevans * @fragment: node offset of the fragment in the overlay
12328459Skevans *
13328459Skevans * overlay_get_target_phandle() retrieves the target phandle of an
14328459Skevans * overlay fragment when that fragment uses a phandle (target
15328459Skevans * property) instead of a path (target-path property).
16328459Skevans *
17328459Skevans * returns:
18328459Skevans *      the phandle pointed by the target property
19328459Skevans *      0, if the phandle was not found
20328459Skevans *	-1, if the phandle was malformed
21328459Skevans */
22328459Skevansstatic uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
23328459Skevans{
24328459Skevans	const fdt32_t *val;
25328459Skevans	int len;
26328459Skevans
27328459Skevans	val = fdt_getprop(fdto, fragment, "target", &len);
28328459Skevans	if (!val)
29328459Skevans		return 0;
30328459Skevans
31328459Skevans	if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
32328459Skevans		return (uint32_t)-1;
33328459Skevans
34328459Skevans	return fdt32_to_cpu(*val);
35328459Skevans}
36328459Skevans
37328459Skevans/**
38328459Skevans * overlay_get_target - retrieves the offset of a fragment's target
39328459Skevans * @fdt: Base device tree blob
40328459Skevans * @fdto: Device tree overlay blob
41328459Skevans * @fragment: node offset of the fragment in the overlay
42328459Skevans * @pathp: pointer which receives the path of the target (or NULL)
43328459Skevans *
44328459Skevans * overlay_get_target() retrieves the target offset in the base
45328459Skevans * device tree of a fragment, no matter how the actual targetting is
46328459Skevans * done (through a phandle or a path)
47328459Skevans *
48328459Skevans * returns:
49328459Skevans *      the targetted node offset in the base device tree
50328459Skevans *      Negative error code on error
51328459Skevans */
52328459Skevansstatic int overlay_get_target(const void *fdt, const void *fdto,
53328459Skevans			      int fragment, char const **pathp)
54328459Skevans{
55328459Skevans	uint32_t phandle;
56328459Skevans	const char *path = NULL;
57328459Skevans	int path_len = 0, ret;
58328459Skevans
59328459Skevans	/* Try first to do a phandle based lookup */
60328459Skevans	phandle = overlay_get_target_phandle(fdto, fragment);
61328459Skevans	if (phandle == (uint32_t)-1)
62328459Skevans		return -FDT_ERR_BADPHANDLE;
63328459Skevans
64328459Skevans	/* no phandle, try path */
65328459Skevans	if (!phandle) {
66328459Skevans		/* And then a path based lookup */
67328459Skevans		path = fdt_getprop(fdto, fragment, "target-path", &path_len);
68328459Skevans		if (path)
69328459Skevans			ret = fdt_path_offset(fdt, path);
70328459Skevans		else
71328459Skevans			ret = path_len;
72328459Skevans	} else
73328459Skevans		ret = fdt_node_offset_by_phandle(fdt, phandle);
74328459Skevans
75328459Skevans	/*
76328459Skevans	* If we haven't found either a target or a
77328459Skevans	* target-path property in a node that contains a
78328459Skevans	* __overlay__ subnode (we wouldn't be called
79328459Skevans	* otherwise), consider it a improperly written
80328459Skevans	* overlay
81328459Skevans	*/
82328459Skevans	if (ret < 0 && path_len == -FDT_ERR_NOTFOUND)
83328459Skevans		ret = -FDT_ERR_BADOVERLAY;
84328459Skevans
85328459Skevans	/* return on error */
86328459Skevans	if (ret < 0)
87328459Skevans		return ret;
88328459Skevans
89328459Skevans	/* return pointer to path (if available) */
90328459Skevans	if (pathp)
91328459Skevans		*pathp = path ? path : NULL;
92328459Skevans
93328459Skevans	return ret;
94328459Skevans}
95328459Skevans
96328459Skevans/**
97328459Skevans * overlay_phandle_add_offset - Increases a phandle by an offset
98328459Skevans * @fdt: Base device tree blob
99328459Skevans * @node: Device tree overlay blob
100328459Skevans * @name: Name of the property to modify (phandle or linux,phandle)
101328459Skevans * @delta: offset to apply
102328459Skevans *
103328459Skevans * overlay_phandle_add_offset() increments a node phandle by a given
104328459Skevans * offset.
105328459Skevans *
106328459Skevans * returns:
107328459Skevans *      0 on success.
108328459Skevans *      Negative error code on error
109328459Skevans */
110328459Skevansstatic int overlay_phandle_add_offset(void *fdt, int node,
111328459Skevans				      const char *name, uint32_t delta)
112328459Skevans{
113328459Skevans	const fdt32_t *val;
114328459Skevans	uint32_t adj_val;
115328459Skevans	int len;
116328459Skevans
117328459Skevans	val = fdt_getprop(fdt, node, name, &len);
118328459Skevans	if (!val)
119328459Skevans		return len;
120328459Skevans
121328459Skevans	if (len != sizeof(*val))
122328459Skevans		return -FDT_ERR_BADPHANDLE;
123328459Skevans
124328459Skevans	adj_val = fdt32_to_cpu(*val);
125328459Skevans	if ((adj_val + delta) < adj_val)
126328459Skevans		return -FDT_ERR_NOPHANDLES;
127328459Skevans
128328459Skevans	adj_val += delta;
129328459Skevans	if (adj_val == (uint32_t)-1)
130328459Skevans		return -FDT_ERR_NOPHANDLES;
131328459Skevans
132328459Skevans	return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
133328459Skevans}
134328459Skevans
135328459Skevans/**
136328459Skevans * overlay_adjust_node_phandles - Offsets the phandles of a node
137328459Skevans * @fdto: Device tree overlay blob
138328459Skevans * @node: Offset of the node we want to adjust
139328459Skevans * @delta: Offset to shift the phandles of
140328459Skevans *
141328459Skevans * overlay_adjust_node_phandles() adds a constant to all the phandles
142328459Skevans * of a given node. This is mainly use as part of the overlay
143328459Skevans * application process, when we want to update all the overlay
144328459Skevans * phandles to not conflict with the overlays of the base device tree.
145328459Skevans *
146328459Skevans * returns:
147328459Skevans *      0 on success
148328459Skevans *      Negative error code on failure
149328459Skevans */
150328459Skevansstatic int overlay_adjust_node_phandles(void *fdto, int node,
151328459Skevans					uint32_t delta)
152328459Skevans{
153328459Skevans	int child;
154328459Skevans	int ret;
155328459Skevans
156328459Skevans	ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
157328459Skevans	if (ret && ret != -FDT_ERR_NOTFOUND)
158328459Skevans		return ret;
159328459Skevans
160328459Skevans	ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
161328459Skevans	if (ret && ret != -FDT_ERR_NOTFOUND)
162328459Skevans		return ret;
163328459Skevans
164328459Skevans	fdt_for_each_subnode(child, fdto, node) {
165328459Skevans		ret = overlay_adjust_node_phandles(fdto, child, delta);
166328459Skevans		if (ret)
167328459Skevans			return ret;
168328459Skevans	}
169328459Skevans
170328459Skevans	return 0;
171328459Skevans}
172328459Skevans
173328459Skevans/**
174328459Skevans * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
175328459Skevans * @fdto: Device tree overlay blob
176328459Skevans * @delta: Offset to shift the phandles of
177328459Skevans *
178328459Skevans * overlay_adjust_local_phandles() adds a constant to all the
179328459Skevans * phandles of an overlay. This is mainly use as part of the overlay
180328459Skevans * application process, when we want to update all the overlay
181328459Skevans * phandles to not conflict with the overlays of the base device tree.
182328459Skevans *
183328459Skevans * returns:
184328459Skevans *      0 on success
185328459Skevans *      Negative error code on failure
186328459Skevans */
187328459Skevansstatic int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
188328459Skevans{
189328459Skevans	/*
190328459Skevans	 * Start adjusting the phandles from the overlay root
191328459Skevans	 */
192328459Skevans	return overlay_adjust_node_phandles(fdto, 0, delta);
193328459Skevans}
194328459Skevans
195328459Skevans/**
196328459Skevans * overlay_update_local_node_references - Adjust the overlay references
197328459Skevans * @fdto: Device tree overlay blob
198328459Skevans * @tree_node: Node offset of the node to operate on
199328459Skevans * @fixup_node: Node offset of the matching local fixups node
200328459Skevans * @delta: Offset to shift the phandles of
201328459Skevans *
202328459Skevans * overlay_update_local_nodes_references() update the phandles
203328459Skevans * pointing to a node within the device tree overlay by adding a
204328459Skevans * constant delta.
205328459Skevans *
206328459Skevans * This is mainly used as part of a device tree application process,
207328459Skevans * where you want the device tree overlays phandles to not conflict
208328459Skevans * with the ones from the base device tree before merging them.
209328459Skevans *
210328459Skevans * returns:
211328459Skevans *      0 on success
212328459Skevans *      Negative error code on failure
213328459Skevans */
214328459Skevansstatic int overlay_update_local_node_references(void *fdto,
215328459Skevans						int tree_node,
216328459Skevans						int fixup_node,
217328459Skevans						uint32_t delta)
218328459Skevans{
219328459Skevans	int fixup_prop;
220328459Skevans	int fixup_child;
221328459Skevans	int ret;
222328459Skevans
223328459Skevans	fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
224328459Skevans		const fdt32_t *fixup_val;
225328459Skevans		const char *tree_val;
226328459Skevans		const char *name;
227328459Skevans		int fixup_len;
228328459Skevans		int tree_len;
229328459Skevans		int i;
230328459Skevans
231328459Skevans		fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
232328459Skevans						  &name, &fixup_len);
233328459Skevans		if (!fixup_val)
234328459Skevans			return fixup_len;
235328459Skevans
236328459Skevans		if (fixup_len % sizeof(uint32_t))
237328459Skevans			return -FDT_ERR_BADOVERLAY;
238328459Skevans
239328459Skevans		tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
240328459Skevans		if (!tree_val) {
241328459Skevans			if (tree_len == -FDT_ERR_NOTFOUND)
242328459Skevans				return -FDT_ERR_BADOVERLAY;
243328459Skevans
244328459Skevans			return tree_len;
245328459Skevans		}
246328459Skevans
247328459Skevans		for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
248328459Skevans			fdt32_t adj_val;
249328459Skevans			uint32_t poffset;
250328459Skevans
251328459Skevans			poffset = fdt32_to_cpu(fixup_val[i]);
252328459Skevans
253328459Skevans			/*
254328459Skevans			 * phandles to fixup can be unaligned.
255328459Skevans			 *
256328459Skevans			 * Use a memcpy for the architectures that do
257328459Skevans			 * not support unaligned accesses.
258328459Skevans			 */
259328459Skevans			memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
260328459Skevans
261328459Skevans			adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta);
262328459Skevans
263328459Skevans			ret = fdt_setprop_inplace_namelen_partial(fdto,
264328459Skevans								  tree_node,
265328459Skevans								  name,
266328459Skevans								  strlen(name),
267328459Skevans								  poffset,
268328459Skevans								  &adj_val,
269328459Skevans								  sizeof(adj_val));
270328459Skevans			if (ret == -FDT_ERR_NOSPACE)
271328459Skevans				return -FDT_ERR_BADOVERLAY;
272328459Skevans
273328459Skevans			if (ret)
274328459Skevans				return ret;
275328459Skevans		}
276328459Skevans	}
277328459Skevans
278328459Skevans	fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
279328459Skevans		const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
280328459Skevans							    NULL);
281328459Skevans		int tree_child;
282328459Skevans
283328459Skevans		tree_child = fdt_subnode_offset(fdto, tree_node,
284328459Skevans						fixup_child_name);
285328459Skevans		if (tree_child == -FDT_ERR_NOTFOUND)
286328459Skevans			return -FDT_ERR_BADOVERLAY;
287328459Skevans		if (tree_child < 0)
288328459Skevans			return tree_child;
289328459Skevans
290328459Skevans		ret = overlay_update_local_node_references(fdto,
291328459Skevans							   tree_child,
292328459Skevans							   fixup_child,
293328459Skevans							   delta);
294328459Skevans		if (ret)
295328459Skevans			return ret;
296328459Skevans	}
297328459Skevans
298328459Skevans	return 0;
299328459Skevans}
300328459Skevans
301328459Skevans/**
302328459Skevans * overlay_update_local_references - Adjust the overlay references
303328459Skevans * @fdto: Device tree overlay blob
304328459Skevans * @delta: Offset to shift the phandles of
305328459Skevans *
306328459Skevans * overlay_update_local_references() update all the phandles pointing
307328459Skevans * to a node within the device tree overlay by adding a constant
308328459Skevans * delta to not conflict with the base overlay.
309328459Skevans *
310328459Skevans * This is mainly used as part of a device tree application process,
311328459Skevans * where you want the device tree overlays phandles to not conflict
312328459Skevans * with the ones from the base device tree before merging them.
313328459Skevans *
314328459Skevans * returns:
315328459Skevans *      0 on success
316328459Skevans *      Negative error code on failure
317328459Skevans */
318328459Skevansstatic int overlay_update_local_references(void *fdto, uint32_t delta)
319328459Skevans{
320328459Skevans	int fixups;
321328459Skevans
322328459Skevans	fixups = fdt_path_offset(fdto, "/__local_fixups__");
323328459Skevans	if (fixups < 0) {
324328459Skevans		/* There's no local phandles to adjust, bail out */
325328459Skevans		if (fixups == -FDT_ERR_NOTFOUND)
326328459Skevans			return 0;
327328459Skevans
328328459Skevans		return fixups;
329328459Skevans	}
330328459Skevans
331328459Skevans	/*
332328459Skevans	 * Update our local references from the root of the tree
333328459Skevans	 */
334328459Skevans	return overlay_update_local_node_references(fdto, 0, fixups,
335328459Skevans						    delta);
336328459Skevans}
337328459Skevans
338328459Skevans/**
339328459Skevans * overlay_fixup_one_phandle - Set an overlay phandle to the base one
340328459Skevans * @fdt: Base Device Tree blob
341328459Skevans * @fdto: Device tree overlay blob
342328459Skevans * @symbols_off: Node offset of the symbols node in the base device tree
343328459Skevans * @path: Path to a node holding a phandle in the overlay
344328459Skevans * @path_len: number of path characters to consider
345328459Skevans * @name: Name of the property holding the phandle reference in the overlay
346328459Skevans * @name_len: number of name characters to consider
347328459Skevans * @poffset: Offset within the overlay property where the phandle is stored
348328459Skevans * @label: Label of the node referenced by the phandle
349328459Skevans *
350328459Skevans * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
351328459Skevans * a node in the base device tree.
352328459Skevans *
353328459Skevans * This is part of the device tree overlay application process, when
354328459Skevans * you want all the phandles in the overlay to point to the actual
355328459Skevans * base dt nodes.
356328459Skevans *
357328459Skevans * returns:
358328459Skevans *      0 on success
359328459Skevans *      Negative error code on failure
360328459Skevans */
361328459Skevansstatic int overlay_fixup_one_phandle(void *fdt, void *fdto,
362328459Skevans				     int symbols_off,
363328459Skevans				     const char *path, uint32_t path_len,
364328459Skevans				     const char *name, uint32_t name_len,
365328459Skevans				     int poffset, const char *label)
366328459Skevans{
367328459Skevans	const char *symbol_path;
368328459Skevans	uint32_t phandle;
369328459Skevans	fdt32_t phandle_prop;
370328459Skevans	int symbol_off, fixup_off;
371328459Skevans	int prop_len;
372328459Skevans
373328459Skevans	if (symbols_off < 0)
374328459Skevans		return symbols_off;
375328459Skevans
376328459Skevans	symbol_path = fdt_getprop(fdt, symbols_off, label,
377328459Skevans				  &prop_len);
378328459Skevans	if (!symbol_path)
379328459Skevans		return prop_len;
380328459Skevans
381328459Skevans	symbol_off = fdt_path_offset(fdt, symbol_path);
382328459Skevans	if (symbol_off < 0)
383328459Skevans		return symbol_off;
384328459Skevans
385328459Skevans	phandle = fdt_get_phandle(fdt, symbol_off);
386328459Skevans	if (!phandle)
387328459Skevans		return -FDT_ERR_NOTFOUND;
388328459Skevans
389328459Skevans	fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
390328459Skevans	if (fixup_off == -FDT_ERR_NOTFOUND)
391328459Skevans		return -FDT_ERR_BADOVERLAY;
392328459Skevans	if (fixup_off < 0)
393328459Skevans		return fixup_off;
394328459Skevans
395328459Skevans	phandle_prop = cpu_to_fdt32(phandle);
396328459Skevans	return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
397328459Skevans						   name, name_len, poffset,
398328459Skevans						   &phandle_prop,
399328459Skevans						   sizeof(phandle_prop));
400328459Skevans};
401328459Skevans
402328459Skevans/**
403328459Skevans * overlay_fixup_phandle - Set an overlay phandle to the base one
404328459Skevans * @fdt: Base Device Tree blob
405328459Skevans * @fdto: Device tree overlay blob
406328459Skevans * @symbols_off: Node offset of the symbols node in the base device tree
407328459Skevans * @property: Property offset in the overlay holding the list of fixups
408328459Skevans *
409328459Skevans * overlay_fixup_phandle() resolves all the overlay phandles pointed
410328459Skevans * to in a __fixups__ property, and updates them to match the phandles
411328459Skevans * in use in the base device tree.
412328459Skevans *
413328459Skevans * This is part of the device tree overlay application process, when
414328459Skevans * you want all the phandles in the overlay to point to the actual
415328459Skevans * base dt nodes.
416328459Skevans *
417328459Skevans * returns:
418328459Skevans *      0 on success
419328459Skevans *      Negative error code on failure
420328459Skevans */
421328459Skevansstatic int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
422328459Skevans				 int property)
423328459Skevans{
424328459Skevans	const char *value;
425328459Skevans	const char *label;
426328459Skevans	int len;
427328459Skevans
428328459Skevans	value = fdt_getprop_by_offset(fdto, property,
429328459Skevans				      &label, &len);
430328459Skevans	if (!value) {
431328459Skevans		if (len == -FDT_ERR_NOTFOUND)
432328459Skevans			return -FDT_ERR_INTERNAL;
433328459Skevans
434328459Skevans		return len;
435328459Skevans	}
436328459Skevans
437328459Skevans	do {
438328459Skevans		const char *path, *name, *fixup_end;
439328459Skevans		const char *fixup_str = value;
440328459Skevans		uint32_t path_len, name_len;
441328459Skevans		uint32_t fixup_len;
442328459Skevans		char *sep, *endptr;
443328459Skevans		int poffset, ret;
444328459Skevans
445328459Skevans		fixup_end = memchr(value, '\0', len);
446328459Skevans		if (!fixup_end)
447328459Skevans			return -FDT_ERR_BADOVERLAY;
448328459Skevans		fixup_len = fixup_end - fixup_str;
449328459Skevans
450328459Skevans		len -= fixup_len + 1;
451328459Skevans		value += fixup_len + 1;
452328459Skevans
453328459Skevans		path = fixup_str;
454328459Skevans		sep = memchr(fixup_str, ':', fixup_len);
455328459Skevans		if (!sep || *sep != ':')
456328459Skevans			return -FDT_ERR_BADOVERLAY;
457328459Skevans
458328459Skevans		path_len = sep - path;
459328459Skevans		if (path_len == (fixup_len - 1))
460328459Skevans			return -FDT_ERR_BADOVERLAY;
461328459Skevans
462328459Skevans		fixup_len -= path_len + 1;
463328459Skevans		name = sep + 1;
464328459Skevans		sep = memchr(name, ':', fixup_len);
465328459Skevans		if (!sep || *sep != ':')
466328459Skevans			return -FDT_ERR_BADOVERLAY;
467328459Skevans
468328459Skevans		name_len = sep - name;
469328459Skevans		if (!name_len)
470328459Skevans			return -FDT_ERR_BADOVERLAY;
471328459Skevans
472328459Skevans		poffset = strtoul(sep + 1, &endptr, 10);
473328459Skevans		if ((*endptr != '\0') || (endptr <= (sep + 1)))
474328459Skevans			return -FDT_ERR_BADOVERLAY;
475328459Skevans
476328459Skevans		ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
477328459Skevans						path, path_len, name, name_len,
478328459Skevans						poffset, label);
479328459Skevans		if (ret)
480328459Skevans			return ret;
481328459Skevans	} while (len > 0);
482328459Skevans
483328459Skevans	return 0;
484328459Skevans}
485328459Skevans
486328459Skevans/**
487328459Skevans * overlay_fixup_phandles - Resolve the overlay phandles to the base
488328459Skevans *                          device tree
489328459Skevans * @fdt: Base Device Tree blob
490328459Skevans * @fdto: Device tree overlay blob
491328459Skevans *
492328459Skevans * overlay_fixup_phandles() resolves all the overlay phandles pointing
493328459Skevans * to nodes in the base device tree.
494328459Skevans *
495328459Skevans * This is one of the steps of the device tree overlay application
496328459Skevans * process, when you want all the phandles in the overlay to point to
497328459Skevans * the actual base dt nodes.
498328459Skevans *
499328459Skevans * returns:
500328459Skevans *      0 on success
501328459Skevans *      Negative error code on failure
502328459Skevans */
503328459Skevansstatic int overlay_fixup_phandles(void *fdt, void *fdto)
504328459Skevans{
505328459Skevans	int fixups_off, symbols_off;
506328459Skevans	int property;
507328459Skevans
508328459Skevans	/* We can have overlays without any fixups */
509328459Skevans	fixups_off = fdt_path_offset(fdto, "/__fixups__");
510328459Skevans	if (fixups_off == -FDT_ERR_NOTFOUND)
511328459Skevans		return 0; /* nothing to do */
512328459Skevans	if (fixups_off < 0)
513328459Skevans		return fixups_off;
514328459Skevans
515328459Skevans	/* And base DTs without symbols */
516328459Skevans	symbols_off = fdt_path_offset(fdt, "/__symbols__");
517328459Skevans	if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
518328459Skevans		return symbols_off;
519328459Skevans
520328459Skevans	fdt_for_each_property_offset(property, fdto, fixups_off) {
521328459Skevans		int ret;
522328459Skevans
523328459Skevans		ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
524328459Skevans		if (ret)
525328459Skevans			return ret;
526328459Skevans	}
527328459Skevans
528328459Skevans	return 0;
529328459Skevans}
530328459Skevans
531328459Skevans/**
532328459Skevans * overlay_apply_node - Merges a node into the base device tree
533328459Skevans * @fdt: Base Device Tree blob
534328459Skevans * @target: Node offset in the base device tree to apply the fragment to
535328459Skevans * @fdto: Device tree overlay blob
536328459Skevans * @node: Node offset in the overlay holding the changes to merge
537328459Skevans *
538328459Skevans * overlay_apply_node() merges a node into a target base device tree
539328459Skevans * node pointed.
540328459Skevans *
541328459Skevans * This is part of the final step in the device tree overlay
542328459Skevans * application process, when all the phandles have been adjusted and
543328459Skevans * resolved and you just have to merge overlay into the base device
544328459Skevans * tree.
545328459Skevans *
546328459Skevans * returns:
547328459Skevans *      0 on success
548328459Skevans *      Negative error code on failure
549328459Skevans */
550328459Skevansstatic int overlay_apply_node(void *fdt, int target,
551328459Skevans			      void *fdto, int node)
552328459Skevans{
553328459Skevans	int property;
554328459Skevans	int subnode;
555328459Skevans
556328459Skevans	fdt_for_each_property_offset(property, fdto, node) {
557328459Skevans		const char *name;
558328459Skevans		const void *prop;
559328459Skevans		int prop_len;
560328459Skevans		int ret;
561328459Skevans
562328459Skevans		prop = fdt_getprop_by_offset(fdto, property, &name,
563328459Skevans					     &prop_len);
564328459Skevans		if (prop_len == -FDT_ERR_NOTFOUND)
565328459Skevans			return -FDT_ERR_INTERNAL;
566328459Skevans		if (prop_len < 0)
567328459Skevans			return prop_len;
568328459Skevans
569328459Skevans		ret = fdt_setprop(fdt, target, name, prop, prop_len);
570328459Skevans		if (ret)
571328459Skevans			return ret;
572328459Skevans	}
573328459Skevans
574328459Skevans	fdt_for_each_subnode(subnode, fdto, node) {
575328459Skevans		const char *name = fdt_get_name(fdto, subnode, NULL);
576328459Skevans		int nnode;
577328459Skevans		int ret;
578328459Skevans
579328459Skevans		nnode = fdt_add_subnode(fdt, target, name);
580328459Skevans		if (nnode == -FDT_ERR_EXISTS) {
581328459Skevans			nnode = fdt_subnode_offset(fdt, target, name);
582328459Skevans			if (nnode == -FDT_ERR_NOTFOUND)
583328459Skevans				return -FDT_ERR_INTERNAL;
584328459Skevans		}
585328459Skevans
586328459Skevans		if (nnode < 0)
587328459Skevans			return nnode;
588328459Skevans
589328459Skevans		ret = overlay_apply_node(fdt, nnode, fdto, subnode);
590328459Skevans		if (ret)
591328459Skevans			return ret;
592328459Skevans	}
593328459Skevans
594328459Skevans	return 0;
595328459Skevans}
596328459Skevans
597328459Skevans/**
598328459Skevans * overlay_merge - Merge an overlay into its base device tree
599328459Skevans * @fdt: Base Device Tree blob
600328459Skevans * @fdto: Device tree overlay blob
601328459Skevans *
602328459Skevans * overlay_merge() merges an overlay into its base device tree.
603328459Skevans *
604328459Skevans * This is the next to last step in the device tree overlay application
605328459Skevans * process, when all the phandles have been adjusted and resolved and
606328459Skevans * you just have to merge overlay into the base device tree.
607328459Skevans *
608328459Skevans * returns:
609328459Skevans *      0 on success
610328459Skevans *      Negative error code on failure
611328459Skevans */
612328459Skevansstatic int overlay_merge(void *fdt, void *fdto)
613328459Skevans{
614328459Skevans	int fragment;
615328459Skevans
616328459Skevans	fdt_for_each_subnode(fragment, fdto, 0) {
617328459Skevans		int overlay;
618328459Skevans		int target;
619328459Skevans		int ret;
620328459Skevans
621328459Skevans		/*
622328459Skevans		 * Each fragments will have an __overlay__ node. If
623328459Skevans		 * they don't, it's not supposed to be merged
624328459Skevans		 */
625328459Skevans		overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
626328459Skevans		if (overlay == -FDT_ERR_NOTFOUND)
627328459Skevans			continue;
628328459Skevans
629328459Skevans		if (overlay < 0)
630328459Skevans			return overlay;
631328459Skevans
632328459Skevans		target = overlay_get_target(fdt, fdto, fragment, NULL);
633328459Skevans		if (target < 0)
634328459Skevans			return target;
635328459Skevans
636328459Skevans		ret = overlay_apply_node(fdt, target, fdto, overlay);
637328459Skevans		if (ret)
638328459Skevans			return ret;
639328459Skevans	}
640328459Skevans
641328459Skevans	return 0;
642328459Skevans}
643328459Skevans
644328459Skevansstatic int get_path_len(const void *fdt, int nodeoffset)
645328459Skevans{
646328459Skevans	int len = 0, namelen;
647328459Skevans	const char *name;
648328459Skevans
649328459Skevans	FDT_CHECK_HEADER(fdt);
650328459Skevans
651328459Skevans	for (;;) {
652328459Skevans		name = fdt_get_name(fdt, nodeoffset, &namelen);
653328459Skevans		if (!name)
654328459Skevans			return namelen;
655328459Skevans
656328459Skevans		/* root? we're done */
657328459Skevans		if (namelen == 0)
658328459Skevans			break;
659328459Skevans
660328459Skevans		nodeoffset = fdt_parent_offset(fdt, nodeoffset);
661328459Skevans		if (nodeoffset < 0)
662328459Skevans			return nodeoffset;
663328459Skevans		len += namelen + 1;
664328459Skevans	}
665328459Skevans
666328459Skevans	/* in case of root pretend it's "/" */
667328459Skevans	if (len == 0)
668328459Skevans		len++;
669328459Skevans	return len;
670328459Skevans}
671328459Skevans
672328459Skevans/**
673328459Skevans * overlay_symbol_update - Update the symbols of base tree after a merge
674328459Skevans * @fdt: Base Device Tree blob
675328459Skevans * @fdto: Device tree overlay blob
676328459Skevans *
677328459Skevans * overlay_symbol_update() updates the symbols of the base tree with the
678328459Skevans * symbols of the applied overlay
679328459Skevans *
680328459Skevans * This is the last step in the device tree overlay application
681328459Skevans * process, allowing the reference of overlay symbols by subsequent
682328459Skevans * overlay operations.
683328459Skevans *
684328459Skevans * returns:
685328459Skevans *      0 on success
686328459Skevans *      Negative error code on failure
687328459Skevans */
688328459Skevansstatic int overlay_symbol_update(void *fdt, void *fdto)
689328459Skevans{
690328459Skevans	int root_sym, ov_sym, prop, path_len, fragment, target;
691328459Skevans	int len, frag_name_len, ret, rel_path_len;
692328459Skevans	const char *s, *e;
693328459Skevans	const char *path;
694328459Skevans	const char *name;
695328459Skevans	const char *frag_name;
696328459Skevans	const char *rel_path;
697328459Skevans	const char *target_path;
698328459Skevans	char *buf;
699328459Skevans	void *p;
700328459Skevans
701328459Skevans	ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__");
702328459Skevans
703328459Skevans	/* if no overlay symbols exist no problem */
704328459Skevans	if (ov_sym < 0)
705328459Skevans		return 0;
706328459Skevans
707328459Skevans	root_sym = fdt_subnode_offset(fdt, 0, "__symbols__");
708328459Skevans
709328459Skevans	/* it no root symbols exist we should create them */
710328459Skevans	if (root_sym == -FDT_ERR_NOTFOUND)
711328459Skevans		root_sym = fdt_add_subnode(fdt, 0, "__symbols__");
712328459Skevans
713328459Skevans	/* any error is fatal now */
714328459Skevans	if (root_sym < 0)
715328459Skevans		return root_sym;
716328459Skevans
717328459Skevans	/* iterate over each overlay symbol */
718328459Skevans	fdt_for_each_property_offset(prop, fdto, ov_sym) {
719328459Skevans		path = fdt_getprop_by_offset(fdto, prop, &name, &path_len);
720328459Skevans		if (!path)
721328459Skevans			return path_len;
722328459Skevans
723328459Skevans		/* verify it's a string property (terminated by a single \0) */
724328459Skevans		if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1])
725328459Skevans			return -FDT_ERR_BADVALUE;
726328459Skevans
727328459Skevans		/* keep end marker to avoid strlen() */
728328459Skevans		e = path + path_len;
729328459Skevans
730328459Skevans		/* format: /<fragment-name>/__overlay__/<relative-subnode-path> */
731328459Skevans
732328459Skevans		if (*path != '/')
733328459Skevans			return -FDT_ERR_BADVALUE;
734328459Skevans
735328459Skevans		/* get fragment name first */
736328459Skevans		s = strchr(path + 1, '/');
737328459Skevans		if (!s)
738328459Skevans			return -FDT_ERR_BADOVERLAY;
739328459Skevans
740328459Skevans		frag_name = path + 1;
741328459Skevans		frag_name_len = s - path - 1;
742328459Skevans
743328459Skevans		/* verify format; safe since "s" lies in \0 terminated prop */
744328459Skevans		len = sizeof("/__overlay__/") - 1;
745328459Skevans		if ((e - s) < len || memcmp(s, "/__overlay__/", len))
746328459Skevans			return -FDT_ERR_BADOVERLAY;
747328459Skevans
748328459Skevans		rel_path = s + len;
749328459Skevans		rel_path_len = e - rel_path;
750328459Skevans
751328459Skevans		/* find the fragment index in which the symbol lies */
752328459Skevans		ret = fdt_subnode_offset_namelen(fdto, 0, frag_name,
753328459Skevans					       frag_name_len);
754328459Skevans		/* not found? */
755328459Skevans		if (ret < 0)
756328459Skevans			return -FDT_ERR_BADOVERLAY;
757328459Skevans		fragment = ret;
758328459Skevans
759328459Skevans		/* an __overlay__ subnode must exist */
760328459Skevans		ret = fdt_subnode_offset(fdto, fragment, "__overlay__");
761328459Skevans		if (ret < 0)
762328459Skevans			return -FDT_ERR_BADOVERLAY;
763328459Skevans
764328459Skevans		/* get the target of the fragment */
765328459Skevans		ret = overlay_get_target(fdt, fdto, fragment, &target_path);
766328459Skevans		if (ret < 0)
767328459Skevans			return ret;
768328459Skevans		target = ret;
769328459Skevans
770328459Skevans		/* if we have a target path use */
771328459Skevans		if (!target_path) {
772328459Skevans			ret = get_path_len(fdt, target);
773328459Skevans			if (ret < 0)
774328459Skevans				return ret;
775328459Skevans			len = ret;
776328459Skevans		} else {
777328459Skevans			len = strlen(target_path);
778328459Skevans		}
779328459Skevans
780328459Skevans		ret = fdt_setprop_placeholder(fdt, root_sym, name,
781328459Skevans				len + (len > 1) + rel_path_len + 1, &p);
782328459Skevans		if (ret < 0)
783328459Skevans			return ret;
784328459Skevans
785328459Skevans		if (!target_path) {
786328459Skevans			/* again in case setprop_placeholder changed it */
787328459Skevans			ret = overlay_get_target(fdt, fdto, fragment, &target_path);
788328459Skevans			if (ret < 0)
789328459Skevans				return ret;
790328459Skevans			target = ret;
791328459Skevans		}
792328459Skevans
793328459Skevans		buf = p;
794328459Skevans		if (len > 1) { /* target is not root */
795328459Skevans			if (!target_path) {
796328459Skevans				ret = fdt_get_path(fdt, target, buf, len + 1);
797328459Skevans				if (ret < 0)
798328459Skevans					return ret;
799328459Skevans			} else
800328459Skevans				memcpy(buf, target_path, len + 1);
801328459Skevans
802328459Skevans		} else
803328459Skevans			len--;
804328459Skevans
805328459Skevans		buf[len] = '/';
806328459Skevans		memcpy(buf + len + 1, rel_path, rel_path_len);
807328459Skevans		buf[len + 1 + rel_path_len] = '\0';
808328459Skevans	}
809328459Skevans
810328459Skevans	return 0;
811328459Skevans}
812328459Skevans
813328459Skevansint fdt_overlay_apply(void *fdt, void *fdto)
814328459Skevans{
815328459Skevans	uint32_t delta = fdt_get_max_phandle(fdt);
816328459Skevans	int ret;
817328459Skevans
818328459Skevans	FDT_CHECK_HEADER(fdt);
819328459Skevans	FDT_CHECK_HEADER(fdto);
820328459Skevans
821328459Skevans	ret = overlay_adjust_local_phandles(fdto, delta);
822328459Skevans	if (ret)
823328459Skevans		goto err;
824328459Skevans
825328459Skevans	ret = overlay_update_local_references(fdto, delta);
826328459Skevans	if (ret)
827328459Skevans		goto err;
828328459Skevans
829328459Skevans	ret = overlay_fixup_phandles(fdt, fdto);
830328459Skevans	if (ret)
831328459Skevans		goto err;
832328459Skevans
833328459Skevans	ret = overlay_merge(fdt, fdto);
834328459Skevans	if (ret)
835328459Skevans		goto err;
836328459Skevans
837328459Skevans	ret = overlay_symbol_update(fdt, fdto);
838328459Skevans	if (ret)
839328459Skevans		goto err;
840328459Skevans
841328459Skevans	/*
842328459Skevans	 * The overlay has been damaged, erase its magic.
843328459Skevans	 */
844328459Skevans	fdt_set_magic(fdto, ~0);
845328459Skevans
846328459Skevans	return 0;
847328459Skevans
848328459Skevanserr:
849328459Skevans	/*
850328459Skevans	 * The overlay might have been damaged, erase its magic.
851328459Skevans	 */
852328459Skevans	fdt_set_magic(fdto, ~0);
853328459Skevans
854328459Skevans	/*
855328459Skevans	 * The base device tree might have been damaged, erase its
856328459Skevans	 * magic.
857328459Skevans	 */
858328459Skevans	fdt_set_magic(fdt, ~0);
859328459Skevans
860328459Skevans	return ret;
861328459Skevans}
862