1328824Skevans/*
2328824Skevans * libfdt - Flat Device Tree manipulation
3328824Skevans * Copyright (C) 2016 Free Electrons
4328824Skevans * Copyright (C) 2016 NextThing Co.
5328824Skevans *
6328824Skevans * libfdt is dual licensed: you can use it either under the terms of
7328824Skevans * the GPL, or the BSD license, at your option.
8328824Skevans *
9328824Skevans *  a) This library is free software; you can redistribute it and/or
10328824Skevans *     modify it under the terms of the GNU General Public License as
11328824Skevans *     published by the Free Software Foundation; either version 2 of the
12328824Skevans *     License, or (at your option) any later version.
13328824Skevans *
14328824Skevans *     This library is distributed in the hope that it will be useful,
15328824Skevans *     but WITHOUT ANY WARRANTY; without even the implied warranty of
16328824Skevans *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17328824Skevans *     GNU General Public License for more details.
18328824Skevans *
19328824Skevans *     You should have received a copy of the GNU General Public
20328824Skevans *     License along with this library; if not, write to the Free
21328824Skevans *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
22328824Skevans *     MA 02110-1301 USA
23328824Skevans *
24328824Skevans * Alternatively,
25328824Skevans *
26328824Skevans *  b) Redistribution and use in source and binary forms, with or
27328824Skevans *     without modification, are permitted provided that the following
28328824Skevans *     conditions are met:
29328824Skevans *
30328824Skevans *     1. Redistributions of source code must retain the above
31328824Skevans *        copyright notice, this list of conditions and the following
32328824Skevans *        disclaimer.
33328824Skevans *     2. Redistributions in binary form must reproduce the above
34328824Skevans *        copyright notice, this list of conditions and the following
35328824Skevans *        disclaimer in the documentation and/or other materials
36328824Skevans *        provided with the distribution.
37328824Skevans *
38328824Skevans *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
39328824Skevans *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
40328824Skevans *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
41328824Skevans *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42328824Skevans *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
43328824Skevans *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44328824Skevans *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45328824Skevans *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46328824Skevans *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47328824Skevans *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48328824Skevans *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
49328824Skevans *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
50328824Skevans *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51328824Skevans */
52328459Skevans#include "libfdt_env.h"
53328459Skevans
54328459Skevans#include <fdt.h>
55328459Skevans#include <libfdt.h>
56328459Skevans
57328459Skevans#include "libfdt_internal.h"
58328459Skevans
59328459Skevans/**
60328459Skevans * overlay_get_target_phandle - retrieves the target phandle of a fragment
61328459Skevans * @fdto: pointer to the device tree overlay blob
62328459Skevans * @fragment: node offset of the fragment in the overlay
63328459Skevans *
64328459Skevans * overlay_get_target_phandle() retrieves the target phandle of an
65328459Skevans * overlay fragment when that fragment uses a phandle (target
66328459Skevans * property) instead of a path (target-path property).
67328459Skevans *
68328459Skevans * returns:
69328459Skevans *      the phandle pointed by the target property
70328459Skevans *      0, if the phandle was not found
71328459Skevans *	-1, if the phandle was malformed
72328459Skevans */
73328459Skevansstatic uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
74328459Skevans{
75328459Skevans	const fdt32_t *val;
76328459Skevans	int len;
77328459Skevans
78328459Skevans	val = fdt_getprop(fdto, fragment, "target", &len);
79328459Skevans	if (!val)
80328459Skevans		return 0;
81328459Skevans
82328459Skevans	if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
83328459Skevans		return (uint32_t)-1;
84328459Skevans
85328459Skevans	return fdt32_to_cpu(*val);
86328459Skevans}
87328459Skevans
88328459Skevans/**
89328459Skevans * overlay_get_target - retrieves the offset of a fragment's target
90328459Skevans * @fdt: Base device tree blob
91328459Skevans * @fdto: Device tree overlay blob
92328459Skevans * @fragment: node offset of the fragment in the overlay
93328459Skevans * @pathp: pointer which receives the path of the target (or NULL)
94328459Skevans *
95328459Skevans * overlay_get_target() retrieves the target offset in the base
96328459Skevans * device tree of a fragment, no matter how the actual targetting is
97328459Skevans * done (through a phandle or a path)
98328459Skevans *
99328459Skevans * returns:
100328459Skevans *      the targetted node offset in the base device tree
101328459Skevans *      Negative error code on error
102328459Skevans */
103328459Skevansstatic int overlay_get_target(const void *fdt, const void *fdto,
104328459Skevans			      int fragment, char const **pathp)
105328459Skevans{
106328459Skevans	uint32_t phandle;
107328459Skevans	const char *path = NULL;
108328459Skevans	int path_len = 0, ret;
109328459Skevans
110328459Skevans	/* Try first to do a phandle based lookup */
111328459Skevans	phandle = overlay_get_target_phandle(fdto, fragment);
112328459Skevans	if (phandle == (uint32_t)-1)
113328459Skevans		return -FDT_ERR_BADPHANDLE;
114328459Skevans
115328459Skevans	/* no phandle, try path */
116328459Skevans	if (!phandle) {
117328459Skevans		/* And then a path based lookup */
118328459Skevans		path = fdt_getprop(fdto, fragment, "target-path", &path_len);
119328459Skevans		if (path)
120328459Skevans			ret = fdt_path_offset(fdt, path);
121328459Skevans		else
122328459Skevans			ret = path_len;
123328459Skevans	} else
124328459Skevans		ret = fdt_node_offset_by_phandle(fdt, phandle);
125328459Skevans
126328459Skevans	/*
127328459Skevans	* If we haven't found either a target or a
128328459Skevans	* target-path property in a node that contains a
129328459Skevans	* __overlay__ subnode (we wouldn't be called
130328459Skevans	* otherwise), consider it a improperly written
131328459Skevans	* overlay
132328459Skevans	*/
133328459Skevans	if (ret < 0 && path_len == -FDT_ERR_NOTFOUND)
134328459Skevans		ret = -FDT_ERR_BADOVERLAY;
135328459Skevans
136328459Skevans	/* return on error */
137328459Skevans	if (ret < 0)
138328459Skevans		return ret;
139328459Skevans
140328459Skevans	/* return pointer to path (if available) */
141328459Skevans	if (pathp)
142328459Skevans		*pathp = path ? path : NULL;
143328459Skevans
144328459Skevans	return ret;
145328459Skevans}
146328459Skevans
147328459Skevans/**
148328459Skevans * overlay_phandle_add_offset - Increases a phandle by an offset
149328459Skevans * @fdt: Base device tree blob
150328459Skevans * @node: Device tree overlay blob
151328459Skevans * @name: Name of the property to modify (phandle or linux,phandle)
152328459Skevans * @delta: offset to apply
153328459Skevans *
154328459Skevans * overlay_phandle_add_offset() increments a node phandle by a given
155328459Skevans * offset.
156328459Skevans *
157328459Skevans * returns:
158328459Skevans *      0 on success.
159328459Skevans *      Negative error code on error
160328459Skevans */
161328459Skevansstatic int overlay_phandle_add_offset(void *fdt, int node,
162328459Skevans				      const char *name, uint32_t delta)
163328459Skevans{
164328459Skevans	const fdt32_t *val;
165328459Skevans	uint32_t adj_val;
166328459Skevans	int len;
167328459Skevans
168328459Skevans	val = fdt_getprop(fdt, node, name, &len);
169328459Skevans	if (!val)
170328459Skevans		return len;
171328459Skevans
172328459Skevans	if (len != sizeof(*val))
173328459Skevans		return -FDT_ERR_BADPHANDLE;
174328459Skevans
175328459Skevans	adj_val = fdt32_to_cpu(*val);
176328459Skevans	if ((adj_val + delta) < adj_val)
177328459Skevans		return -FDT_ERR_NOPHANDLES;
178328459Skevans
179328459Skevans	adj_val += delta;
180328459Skevans	if (adj_val == (uint32_t)-1)
181328459Skevans		return -FDT_ERR_NOPHANDLES;
182328459Skevans
183328459Skevans	return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
184328459Skevans}
185328459Skevans
186328459Skevans/**
187328459Skevans * overlay_adjust_node_phandles - Offsets the phandles of a node
188328459Skevans * @fdto: Device tree overlay blob
189328459Skevans * @node: Offset of the node we want to adjust
190328459Skevans * @delta: Offset to shift the phandles of
191328459Skevans *
192328459Skevans * overlay_adjust_node_phandles() adds a constant to all the phandles
193328459Skevans * of a given node. This is mainly use as part of the overlay
194328459Skevans * application process, when we want to update all the overlay
195328459Skevans * phandles to not conflict with the overlays of the base device tree.
196328459Skevans *
197328459Skevans * returns:
198328459Skevans *      0 on success
199328459Skevans *      Negative error code on failure
200328459Skevans */
201328459Skevansstatic int overlay_adjust_node_phandles(void *fdto, int node,
202328459Skevans					uint32_t delta)
203328459Skevans{
204328459Skevans	int child;
205328459Skevans	int ret;
206328459Skevans
207328459Skevans	ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
208328459Skevans	if (ret && ret != -FDT_ERR_NOTFOUND)
209328459Skevans		return ret;
210328459Skevans
211328459Skevans	ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
212328459Skevans	if (ret && ret != -FDT_ERR_NOTFOUND)
213328459Skevans		return ret;
214328459Skevans
215328459Skevans	fdt_for_each_subnode(child, fdto, node) {
216328459Skevans		ret = overlay_adjust_node_phandles(fdto, child, delta);
217328459Skevans		if (ret)
218328459Skevans			return ret;
219328459Skevans	}
220328459Skevans
221328459Skevans	return 0;
222328459Skevans}
223328459Skevans
224328459Skevans/**
225328459Skevans * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
226328459Skevans * @fdto: Device tree overlay blob
227328459Skevans * @delta: Offset to shift the phandles of
228328459Skevans *
229328459Skevans * overlay_adjust_local_phandles() adds a constant to all the
230328459Skevans * phandles of an overlay. This is mainly use as part of the overlay
231328459Skevans * application process, when we want to update all the overlay
232328459Skevans * phandles to not conflict with the overlays of the base device tree.
233328459Skevans *
234328459Skevans * returns:
235328459Skevans *      0 on success
236328459Skevans *      Negative error code on failure
237328459Skevans */
238328459Skevansstatic int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
239328459Skevans{
240328459Skevans	/*
241328459Skevans	 * Start adjusting the phandles from the overlay root
242328459Skevans	 */
243328459Skevans	return overlay_adjust_node_phandles(fdto, 0, delta);
244328459Skevans}
245328459Skevans
246328459Skevans/**
247328459Skevans * overlay_update_local_node_references - Adjust the overlay references
248328459Skevans * @fdto: Device tree overlay blob
249328459Skevans * @tree_node: Node offset of the node to operate on
250328459Skevans * @fixup_node: Node offset of the matching local fixups node
251328459Skevans * @delta: Offset to shift the phandles of
252328459Skevans *
253328459Skevans * overlay_update_local_nodes_references() update the phandles
254328459Skevans * pointing to a node within the device tree overlay by adding a
255328459Skevans * constant delta.
256328459Skevans *
257328459Skevans * This is mainly used as part of a device tree application process,
258328459Skevans * where you want the device tree overlays phandles to not conflict
259328459Skevans * with the ones from the base device tree before merging them.
260328459Skevans *
261328459Skevans * returns:
262328459Skevans *      0 on success
263328459Skevans *      Negative error code on failure
264328459Skevans */
265328459Skevansstatic int overlay_update_local_node_references(void *fdto,
266328459Skevans						int tree_node,
267328459Skevans						int fixup_node,
268328459Skevans						uint32_t delta)
269328459Skevans{
270328459Skevans	int fixup_prop;
271328459Skevans	int fixup_child;
272328459Skevans	int ret;
273328459Skevans
274328459Skevans	fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
275328459Skevans		const fdt32_t *fixup_val;
276328459Skevans		const char *tree_val;
277328459Skevans		const char *name;
278328459Skevans		int fixup_len;
279328459Skevans		int tree_len;
280328459Skevans		int i;
281328459Skevans
282328459Skevans		fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
283328459Skevans						  &name, &fixup_len);
284328459Skevans		if (!fixup_val)
285328459Skevans			return fixup_len;
286328459Skevans
287328459Skevans		if (fixup_len % sizeof(uint32_t))
288328459Skevans			return -FDT_ERR_BADOVERLAY;
289328459Skevans
290328459Skevans		tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
291328459Skevans		if (!tree_val) {
292328459Skevans			if (tree_len == -FDT_ERR_NOTFOUND)
293328459Skevans				return -FDT_ERR_BADOVERLAY;
294328459Skevans
295328459Skevans			return tree_len;
296328459Skevans		}
297328459Skevans
298328459Skevans		for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
299328459Skevans			fdt32_t adj_val;
300328459Skevans			uint32_t poffset;
301328459Skevans
302328459Skevans			poffset = fdt32_to_cpu(fixup_val[i]);
303328459Skevans
304328459Skevans			/*
305328459Skevans			 * phandles to fixup can be unaligned.
306328459Skevans			 *
307328459Skevans			 * Use a memcpy for the architectures that do
308328459Skevans			 * not support unaligned accesses.
309328459Skevans			 */
310328459Skevans			memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
311328459Skevans
312328459Skevans			adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta);
313328459Skevans
314328459Skevans			ret = fdt_setprop_inplace_namelen_partial(fdto,
315328459Skevans								  tree_node,
316328459Skevans								  name,
317328459Skevans								  strlen(name),
318328459Skevans								  poffset,
319328459Skevans								  &adj_val,
320328459Skevans								  sizeof(adj_val));
321328459Skevans			if (ret == -FDT_ERR_NOSPACE)
322328459Skevans				return -FDT_ERR_BADOVERLAY;
323328459Skevans
324328459Skevans			if (ret)
325328459Skevans				return ret;
326328459Skevans		}
327328459Skevans	}
328328459Skevans
329328459Skevans	fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
330328459Skevans		const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
331328459Skevans							    NULL);
332328459Skevans		int tree_child;
333328459Skevans
334328459Skevans		tree_child = fdt_subnode_offset(fdto, tree_node,
335328459Skevans						fixup_child_name);
336328459Skevans		if (tree_child == -FDT_ERR_NOTFOUND)
337328459Skevans			return -FDT_ERR_BADOVERLAY;
338328459Skevans		if (tree_child < 0)
339328459Skevans			return tree_child;
340328459Skevans
341328459Skevans		ret = overlay_update_local_node_references(fdto,
342328459Skevans							   tree_child,
343328459Skevans							   fixup_child,
344328459Skevans							   delta);
345328459Skevans		if (ret)
346328459Skevans			return ret;
347328459Skevans	}
348328459Skevans
349328459Skevans	return 0;
350328459Skevans}
351328459Skevans
352328459Skevans/**
353328459Skevans * overlay_update_local_references - Adjust the overlay references
354328459Skevans * @fdto: Device tree overlay blob
355328459Skevans * @delta: Offset to shift the phandles of
356328459Skevans *
357328459Skevans * overlay_update_local_references() update all the phandles pointing
358328459Skevans * to a node within the device tree overlay by adding a constant
359328459Skevans * delta to not conflict with the base overlay.
360328459Skevans *
361328459Skevans * This is mainly used as part of a device tree application process,
362328459Skevans * where you want the device tree overlays phandles to not conflict
363328459Skevans * with the ones from the base device tree before merging them.
364328459Skevans *
365328459Skevans * returns:
366328459Skevans *      0 on success
367328459Skevans *      Negative error code on failure
368328459Skevans */
369328459Skevansstatic int overlay_update_local_references(void *fdto, uint32_t delta)
370328459Skevans{
371328459Skevans	int fixups;
372328459Skevans
373328459Skevans	fixups = fdt_path_offset(fdto, "/__local_fixups__");
374328459Skevans	if (fixups < 0) {
375328459Skevans		/* There's no local phandles to adjust, bail out */
376328459Skevans		if (fixups == -FDT_ERR_NOTFOUND)
377328459Skevans			return 0;
378328459Skevans
379328459Skevans		return fixups;
380328459Skevans	}
381328459Skevans
382328459Skevans	/*
383328459Skevans	 * Update our local references from the root of the tree
384328459Skevans	 */
385328459Skevans	return overlay_update_local_node_references(fdto, 0, fixups,
386328459Skevans						    delta);
387328459Skevans}
388328459Skevans
389328459Skevans/**
390328459Skevans * overlay_fixup_one_phandle - Set an overlay phandle to the base one
391328459Skevans * @fdt: Base Device Tree blob
392328459Skevans * @fdto: Device tree overlay blob
393328459Skevans * @symbols_off: Node offset of the symbols node in the base device tree
394328459Skevans * @path: Path to a node holding a phandle in the overlay
395328459Skevans * @path_len: number of path characters to consider
396328459Skevans * @name: Name of the property holding the phandle reference in the overlay
397328459Skevans * @name_len: number of name characters to consider
398328459Skevans * @poffset: Offset within the overlay property where the phandle is stored
399328459Skevans * @label: Label of the node referenced by the phandle
400328459Skevans *
401328459Skevans * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
402328459Skevans * a node in the base device tree.
403328459Skevans *
404328459Skevans * This is part of the device tree overlay application process, when
405328459Skevans * you want all the phandles in the overlay to point to the actual
406328459Skevans * base dt nodes.
407328459Skevans *
408328459Skevans * returns:
409328459Skevans *      0 on success
410328459Skevans *      Negative error code on failure
411328459Skevans */
412328459Skevansstatic int overlay_fixup_one_phandle(void *fdt, void *fdto,
413328459Skevans				     int symbols_off,
414328459Skevans				     const char *path, uint32_t path_len,
415328459Skevans				     const char *name, uint32_t name_len,
416328459Skevans				     int poffset, const char *label)
417328459Skevans{
418328459Skevans	const char *symbol_path;
419328459Skevans	uint32_t phandle;
420328459Skevans	fdt32_t phandle_prop;
421328459Skevans	int symbol_off, fixup_off;
422328459Skevans	int prop_len;
423328459Skevans
424328459Skevans	if (symbols_off < 0)
425328459Skevans		return symbols_off;
426328459Skevans
427328459Skevans	symbol_path = fdt_getprop(fdt, symbols_off, label,
428328459Skevans				  &prop_len);
429328459Skevans	if (!symbol_path)
430328459Skevans		return prop_len;
431328459Skevans
432328459Skevans	symbol_off = fdt_path_offset(fdt, symbol_path);
433328459Skevans	if (symbol_off < 0)
434328459Skevans		return symbol_off;
435328459Skevans
436328459Skevans	phandle = fdt_get_phandle(fdt, symbol_off);
437328459Skevans	if (!phandle)
438328459Skevans		return -FDT_ERR_NOTFOUND;
439328459Skevans
440328459Skevans	fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
441328459Skevans	if (fixup_off == -FDT_ERR_NOTFOUND)
442328459Skevans		return -FDT_ERR_BADOVERLAY;
443328459Skevans	if (fixup_off < 0)
444328459Skevans		return fixup_off;
445328459Skevans
446328459Skevans	phandle_prop = cpu_to_fdt32(phandle);
447328459Skevans	return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
448328459Skevans						   name, name_len, poffset,
449328459Skevans						   &phandle_prop,
450328459Skevans						   sizeof(phandle_prop));
451328459Skevans};
452328459Skevans
453328459Skevans/**
454328459Skevans * overlay_fixup_phandle - Set an overlay phandle to the base one
455328459Skevans * @fdt: Base Device Tree blob
456328459Skevans * @fdto: Device tree overlay blob
457328459Skevans * @symbols_off: Node offset of the symbols node in the base device tree
458328459Skevans * @property: Property offset in the overlay holding the list of fixups
459328459Skevans *
460328459Skevans * overlay_fixup_phandle() resolves all the overlay phandles pointed
461328459Skevans * to in a __fixups__ property, and updates them to match the phandles
462328459Skevans * in use in the base device tree.
463328459Skevans *
464328459Skevans * This is part of the device tree overlay application process, when
465328459Skevans * you want all the phandles in the overlay to point to the actual
466328459Skevans * base dt nodes.
467328459Skevans *
468328459Skevans * returns:
469328459Skevans *      0 on success
470328459Skevans *      Negative error code on failure
471328459Skevans */
472328459Skevansstatic int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
473328459Skevans				 int property)
474328459Skevans{
475328459Skevans	const char *value;
476328459Skevans	const char *label;
477328459Skevans	int len;
478328459Skevans
479328459Skevans	value = fdt_getprop_by_offset(fdto, property,
480328459Skevans				      &label, &len);
481328459Skevans	if (!value) {
482328459Skevans		if (len == -FDT_ERR_NOTFOUND)
483328459Skevans			return -FDT_ERR_INTERNAL;
484328459Skevans
485328459Skevans		return len;
486328459Skevans	}
487328459Skevans
488328459Skevans	do {
489328459Skevans		const char *path, *name, *fixup_end;
490328459Skevans		const char *fixup_str = value;
491328459Skevans		uint32_t path_len, name_len;
492328459Skevans		uint32_t fixup_len;
493328459Skevans		char *sep, *endptr;
494328459Skevans		int poffset, ret;
495328459Skevans
496328459Skevans		fixup_end = memchr(value, '\0', len);
497328459Skevans		if (!fixup_end)
498328459Skevans			return -FDT_ERR_BADOVERLAY;
499328459Skevans		fixup_len = fixup_end - fixup_str;
500328459Skevans
501328459Skevans		len -= fixup_len + 1;
502328459Skevans		value += fixup_len + 1;
503328459Skevans
504328459Skevans		path = fixup_str;
505328459Skevans		sep = memchr(fixup_str, ':', fixup_len);
506328459Skevans		if (!sep || *sep != ':')
507328459Skevans			return -FDT_ERR_BADOVERLAY;
508328459Skevans
509328459Skevans		path_len = sep - path;
510328459Skevans		if (path_len == (fixup_len - 1))
511328459Skevans			return -FDT_ERR_BADOVERLAY;
512328459Skevans
513328459Skevans		fixup_len -= path_len + 1;
514328459Skevans		name = sep + 1;
515328459Skevans		sep = memchr(name, ':', fixup_len);
516328459Skevans		if (!sep || *sep != ':')
517328459Skevans			return -FDT_ERR_BADOVERLAY;
518328459Skevans
519328459Skevans		name_len = sep - name;
520328459Skevans		if (!name_len)
521328459Skevans			return -FDT_ERR_BADOVERLAY;
522328459Skevans
523328459Skevans		poffset = strtoul(sep + 1, &endptr, 10);
524328459Skevans		if ((*endptr != '\0') || (endptr <= (sep + 1)))
525328459Skevans			return -FDT_ERR_BADOVERLAY;
526328459Skevans
527328459Skevans		ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
528328459Skevans						path, path_len, name, name_len,
529328459Skevans						poffset, label);
530328459Skevans		if (ret)
531328459Skevans			return ret;
532328459Skevans	} while (len > 0);
533328459Skevans
534328459Skevans	return 0;
535328459Skevans}
536328459Skevans
537328459Skevans/**
538328459Skevans * overlay_fixup_phandles - Resolve the overlay phandles to the base
539328459Skevans *                          device tree
540328459Skevans * @fdt: Base Device Tree blob
541328459Skevans * @fdto: Device tree overlay blob
542328459Skevans *
543328459Skevans * overlay_fixup_phandles() resolves all the overlay phandles pointing
544328459Skevans * to nodes in the base device tree.
545328459Skevans *
546328459Skevans * This is one of the steps of the device tree overlay application
547328459Skevans * process, when you want all the phandles in the overlay to point to
548328459Skevans * the actual base dt nodes.
549328459Skevans *
550328459Skevans * returns:
551328459Skevans *      0 on success
552328459Skevans *      Negative error code on failure
553328459Skevans */
554328459Skevansstatic int overlay_fixup_phandles(void *fdt, void *fdto)
555328459Skevans{
556328459Skevans	int fixups_off, symbols_off;
557328459Skevans	int property;
558328459Skevans
559328459Skevans	/* We can have overlays without any fixups */
560328459Skevans	fixups_off = fdt_path_offset(fdto, "/__fixups__");
561328459Skevans	if (fixups_off == -FDT_ERR_NOTFOUND)
562328459Skevans		return 0; /* nothing to do */
563328459Skevans	if (fixups_off < 0)
564328459Skevans		return fixups_off;
565328459Skevans
566328459Skevans	/* And base DTs without symbols */
567328459Skevans	symbols_off = fdt_path_offset(fdt, "/__symbols__");
568328459Skevans	if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
569328459Skevans		return symbols_off;
570328459Skevans
571328459Skevans	fdt_for_each_property_offset(property, fdto, fixups_off) {
572328459Skevans		int ret;
573328459Skevans
574328459Skevans		ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
575328459Skevans		if (ret)
576328459Skevans			return ret;
577328459Skevans	}
578328459Skevans
579328459Skevans	return 0;
580328459Skevans}
581328459Skevans
582328459Skevans/**
583328459Skevans * overlay_apply_node - Merges a node into the base device tree
584328459Skevans * @fdt: Base Device Tree blob
585328459Skevans * @target: Node offset in the base device tree to apply the fragment to
586328459Skevans * @fdto: Device tree overlay blob
587328459Skevans * @node: Node offset in the overlay holding the changes to merge
588328459Skevans *
589328459Skevans * overlay_apply_node() merges a node into a target base device tree
590328459Skevans * node pointed.
591328459Skevans *
592328459Skevans * This is part of the final step in the device tree overlay
593328459Skevans * application process, when all the phandles have been adjusted and
594328459Skevans * resolved and you just have to merge overlay into the base device
595328459Skevans * tree.
596328459Skevans *
597328459Skevans * returns:
598328459Skevans *      0 on success
599328459Skevans *      Negative error code on failure
600328459Skevans */
601328459Skevansstatic int overlay_apply_node(void *fdt, int target,
602328459Skevans			      void *fdto, int node)
603328459Skevans{
604328459Skevans	int property;
605328459Skevans	int subnode;
606328459Skevans
607328459Skevans	fdt_for_each_property_offset(property, fdto, node) {
608328459Skevans		const char *name;
609328459Skevans		const void *prop;
610328459Skevans		int prop_len;
611328459Skevans		int ret;
612328459Skevans
613328459Skevans		prop = fdt_getprop_by_offset(fdto, property, &name,
614328459Skevans					     &prop_len);
615328459Skevans		if (prop_len == -FDT_ERR_NOTFOUND)
616328459Skevans			return -FDT_ERR_INTERNAL;
617328459Skevans		if (prop_len < 0)
618328459Skevans			return prop_len;
619328459Skevans
620328459Skevans		ret = fdt_setprop(fdt, target, name, prop, prop_len);
621328459Skevans		if (ret)
622328459Skevans			return ret;
623328459Skevans	}
624328459Skevans
625328459Skevans	fdt_for_each_subnode(subnode, fdto, node) {
626328459Skevans		const char *name = fdt_get_name(fdto, subnode, NULL);
627328459Skevans		int nnode;
628328459Skevans		int ret;
629328459Skevans
630328459Skevans		nnode = fdt_add_subnode(fdt, target, name);
631328459Skevans		if (nnode == -FDT_ERR_EXISTS) {
632328459Skevans			nnode = fdt_subnode_offset(fdt, target, name);
633328459Skevans			if (nnode == -FDT_ERR_NOTFOUND)
634328459Skevans				return -FDT_ERR_INTERNAL;
635328459Skevans		}
636328459Skevans
637328459Skevans		if (nnode < 0)
638328459Skevans			return nnode;
639328459Skevans
640328459Skevans		ret = overlay_apply_node(fdt, nnode, fdto, subnode);
641328459Skevans		if (ret)
642328459Skevans			return ret;
643328459Skevans	}
644328459Skevans
645328459Skevans	return 0;
646328459Skevans}
647328459Skevans
648328459Skevans/**
649328459Skevans * overlay_merge - Merge an overlay into its base device tree
650328459Skevans * @fdt: Base Device Tree blob
651328459Skevans * @fdto: Device tree overlay blob
652328459Skevans *
653328459Skevans * overlay_merge() merges an overlay into its base device tree.
654328459Skevans *
655328459Skevans * This is the next to last step in the device tree overlay application
656328459Skevans * process, when all the phandles have been adjusted and resolved and
657328459Skevans * you just have to merge overlay into the base device tree.
658328459Skevans *
659328459Skevans * returns:
660328459Skevans *      0 on success
661328459Skevans *      Negative error code on failure
662328459Skevans */
663328459Skevansstatic int overlay_merge(void *fdt, void *fdto)
664328459Skevans{
665328459Skevans	int fragment;
666328459Skevans
667328459Skevans	fdt_for_each_subnode(fragment, fdto, 0) {
668328459Skevans		int overlay;
669328459Skevans		int target;
670328459Skevans		int ret;
671328459Skevans
672328459Skevans		/*
673328459Skevans		 * Each fragments will have an __overlay__ node. If
674328459Skevans		 * they don't, it's not supposed to be merged
675328459Skevans		 */
676328459Skevans		overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
677328459Skevans		if (overlay == -FDT_ERR_NOTFOUND)
678328459Skevans			continue;
679328459Skevans
680328459Skevans		if (overlay < 0)
681328459Skevans			return overlay;
682328459Skevans
683328459Skevans		target = overlay_get_target(fdt, fdto, fragment, NULL);
684328459Skevans		if (target < 0)
685328459Skevans			return target;
686328459Skevans
687328459Skevans		ret = overlay_apply_node(fdt, target, fdto, overlay);
688328459Skevans		if (ret)
689328459Skevans			return ret;
690328459Skevans	}
691328459Skevans
692328459Skevans	return 0;
693328459Skevans}
694328459Skevans
695328459Skevansstatic int get_path_len(const void *fdt, int nodeoffset)
696328459Skevans{
697328459Skevans	int len = 0, namelen;
698328459Skevans	const char *name;
699328459Skevans
700328459Skevans	FDT_CHECK_HEADER(fdt);
701328459Skevans
702328459Skevans	for (;;) {
703328459Skevans		name = fdt_get_name(fdt, nodeoffset, &namelen);
704328459Skevans		if (!name)
705328459Skevans			return namelen;
706328459Skevans
707328459Skevans		/* root? we're done */
708328459Skevans		if (namelen == 0)
709328459Skevans			break;
710328459Skevans
711328459Skevans		nodeoffset = fdt_parent_offset(fdt, nodeoffset);
712328459Skevans		if (nodeoffset < 0)
713328459Skevans			return nodeoffset;
714328459Skevans		len += namelen + 1;
715328459Skevans	}
716328459Skevans
717328459Skevans	/* in case of root pretend it's "/" */
718328459Skevans	if (len == 0)
719328459Skevans		len++;
720328459Skevans	return len;
721328459Skevans}
722328459Skevans
723328459Skevans/**
724328459Skevans * overlay_symbol_update - Update the symbols of base tree after a merge
725328459Skevans * @fdt: Base Device Tree blob
726328459Skevans * @fdto: Device tree overlay blob
727328459Skevans *
728328459Skevans * overlay_symbol_update() updates the symbols of the base tree with the
729328459Skevans * symbols of the applied overlay
730328459Skevans *
731328459Skevans * This is the last step in the device tree overlay application
732328459Skevans * process, allowing the reference of overlay symbols by subsequent
733328459Skevans * overlay operations.
734328459Skevans *
735328459Skevans * returns:
736328459Skevans *      0 on success
737328459Skevans *      Negative error code on failure
738328459Skevans */
739328459Skevansstatic int overlay_symbol_update(void *fdt, void *fdto)
740328459Skevans{
741328459Skevans	int root_sym, ov_sym, prop, path_len, fragment, target;
742328459Skevans	int len, frag_name_len, ret, rel_path_len;
743328459Skevans	const char *s, *e;
744328459Skevans	const char *path;
745328459Skevans	const char *name;
746328459Skevans	const char *frag_name;
747328459Skevans	const char *rel_path;
748328459Skevans	const char *target_path;
749328459Skevans	char *buf;
750328459Skevans	void *p;
751328459Skevans
752328459Skevans	ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__");
753328459Skevans
754328459Skevans	/* if no overlay symbols exist no problem */
755328459Skevans	if (ov_sym < 0)
756328459Skevans		return 0;
757328459Skevans
758328459Skevans	root_sym = fdt_subnode_offset(fdt, 0, "__symbols__");
759328459Skevans
760328459Skevans	/* it no root symbols exist we should create them */
761328459Skevans	if (root_sym == -FDT_ERR_NOTFOUND)
762328459Skevans		root_sym = fdt_add_subnode(fdt, 0, "__symbols__");
763328459Skevans
764328459Skevans	/* any error is fatal now */
765328459Skevans	if (root_sym < 0)
766328459Skevans		return root_sym;
767328459Skevans
768328459Skevans	/* iterate over each overlay symbol */
769328459Skevans	fdt_for_each_property_offset(prop, fdto, ov_sym) {
770328459Skevans		path = fdt_getprop_by_offset(fdto, prop, &name, &path_len);
771328459Skevans		if (!path)
772328459Skevans			return path_len;
773328459Skevans
774328459Skevans		/* verify it's a string property (terminated by a single \0) */
775328459Skevans		if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1])
776328459Skevans			return -FDT_ERR_BADVALUE;
777328459Skevans
778328459Skevans		/* keep end marker to avoid strlen() */
779328459Skevans		e = path + path_len;
780328459Skevans
781328459Skevans		/* format: /<fragment-name>/__overlay__/<relative-subnode-path> */
782328459Skevans
783328459Skevans		if (*path != '/')
784328459Skevans			return -FDT_ERR_BADVALUE;
785328459Skevans
786328459Skevans		/* get fragment name first */
787328459Skevans		s = strchr(path + 1, '/');
788328459Skevans		if (!s)
789328459Skevans			return -FDT_ERR_BADOVERLAY;
790328459Skevans
791328459Skevans		frag_name = path + 1;
792328459Skevans		frag_name_len = s - path - 1;
793328459Skevans
794328459Skevans		/* verify format; safe since "s" lies in \0 terminated prop */
795328459Skevans		len = sizeof("/__overlay__/") - 1;
796328459Skevans		if ((e - s) < len || memcmp(s, "/__overlay__/", len))
797328459Skevans			return -FDT_ERR_BADOVERLAY;
798328459Skevans
799328459Skevans		rel_path = s + len;
800328459Skevans		rel_path_len = e - rel_path;
801328459Skevans
802328459Skevans		/* find the fragment index in which the symbol lies */
803328459Skevans		ret = fdt_subnode_offset_namelen(fdto, 0, frag_name,
804328459Skevans					       frag_name_len);
805328459Skevans		/* not found? */
806328459Skevans		if (ret < 0)
807328459Skevans			return -FDT_ERR_BADOVERLAY;
808328459Skevans		fragment = ret;
809328459Skevans
810328459Skevans		/* an __overlay__ subnode must exist */
811328459Skevans		ret = fdt_subnode_offset(fdto, fragment, "__overlay__");
812328459Skevans		if (ret < 0)
813328459Skevans			return -FDT_ERR_BADOVERLAY;
814328459Skevans
815328459Skevans		/* get the target of the fragment */
816328459Skevans		ret = overlay_get_target(fdt, fdto, fragment, &target_path);
817328459Skevans		if (ret < 0)
818328459Skevans			return ret;
819328459Skevans		target = ret;
820328459Skevans
821328459Skevans		/* if we have a target path use */
822328459Skevans		if (!target_path) {
823328459Skevans			ret = get_path_len(fdt, target);
824328459Skevans			if (ret < 0)
825328459Skevans				return ret;
826328459Skevans			len = ret;
827328459Skevans		} else {
828328459Skevans			len = strlen(target_path);
829328459Skevans		}
830328459Skevans
831328459Skevans		ret = fdt_setprop_placeholder(fdt, root_sym, name,
832328459Skevans				len + (len > 1) + rel_path_len + 1, &p);
833328459Skevans		if (ret < 0)
834328459Skevans			return ret;
835328459Skevans
836328459Skevans		if (!target_path) {
837328459Skevans			/* again in case setprop_placeholder changed it */
838328459Skevans			ret = overlay_get_target(fdt, fdto, fragment, &target_path);
839328459Skevans			if (ret < 0)
840328459Skevans				return ret;
841328459Skevans			target = ret;
842328459Skevans		}
843328459Skevans
844328459Skevans		buf = p;
845328459Skevans		if (len > 1) { /* target is not root */
846328459Skevans			if (!target_path) {
847328459Skevans				ret = fdt_get_path(fdt, target, buf, len + 1);
848328459Skevans				if (ret < 0)
849328459Skevans					return ret;
850328459Skevans			} else
851328459Skevans				memcpy(buf, target_path, len + 1);
852328459Skevans
853328459Skevans		} else
854328459Skevans			len--;
855328459Skevans
856328459Skevans		buf[len] = '/';
857328459Skevans		memcpy(buf + len + 1, rel_path, rel_path_len);
858328459Skevans		buf[len + 1 + rel_path_len] = '\0';
859328459Skevans	}
860328459Skevans
861328459Skevans	return 0;
862328459Skevans}
863328459Skevans
864328459Skevansint fdt_overlay_apply(void *fdt, void *fdto)
865328459Skevans{
866328459Skevans	uint32_t delta = fdt_get_max_phandle(fdt);
867328459Skevans	int ret;
868328459Skevans
869328459Skevans	FDT_CHECK_HEADER(fdt);
870328459Skevans	FDT_CHECK_HEADER(fdto);
871328459Skevans
872328459Skevans	ret = overlay_adjust_local_phandles(fdto, delta);
873328459Skevans	if (ret)
874328459Skevans		goto err;
875328459Skevans
876328459Skevans	ret = overlay_update_local_references(fdto, delta);
877328459Skevans	if (ret)
878328459Skevans		goto err;
879328459Skevans
880328459Skevans	ret = overlay_fixup_phandles(fdt, fdto);
881328459Skevans	if (ret)
882328459Skevans		goto err;
883328459Skevans
884328459Skevans	ret = overlay_merge(fdt, fdto);
885328459Skevans	if (ret)
886328459Skevans		goto err;
887328459Skevans
888328459Skevans	ret = overlay_symbol_update(fdt, fdto);
889328459Skevans	if (ret)
890328459Skevans		goto err;
891328459Skevans
892328459Skevans	/*
893328459Skevans	 * The overlay has been damaged, erase its magic.
894328459Skevans	 */
895328459Skevans	fdt_set_magic(fdto, ~0);
896328459Skevans
897328459Skevans	return 0;
898328459Skevans
899328459Skevanserr:
900328459Skevans	/*
901328459Skevans	 * The overlay might have been damaged, erase its magic.
902328459Skevans	 */
903328459Skevans	fdt_set_magic(fdto, ~0);
904328459Skevans
905328459Skevans	/*
906328459Skevans	 * The base device tree might have been damaged, erase its
907328459Skevans	 * magic.
908328459Skevans	 */
909328459Skevans	fdt_set_magic(fdt, ~0);
910328459Skevans
911328459Skevans	return ret;
912328459Skevans}
913