xref: /netbsd-src/sys/external/bsd/libfdt/dist/fdt_overlay.c (revision 404ee5b9334f618040b6cdef96a0ff35a6fc4636)
1 /*	$NetBSD: fdt_overlay.c,v 1.1.1.1 2017/06/08 15:53:12 skrll Exp $	*/
2 
3 #include "libfdt_env.h"
4 
5 #include <fdt.h>
6 #include <libfdt.h>
7 
8 #include "libfdt_internal.h"
9 
10 /**
11  * overlay_get_target_phandle - retrieves the target phandle of a fragment
12  * @fdto: pointer to the device tree overlay blob
13  * @fragment: node offset of the fragment in the overlay
14  *
15  * overlay_get_target_phandle() retrieves the target phandle of an
16  * overlay fragment when that fragment uses a phandle (target
17  * property) instead of a path (target-path property).
18  *
19  * returns:
20  *      the phandle pointed by the target property
21  *      0, if the phandle was not found
22  *	-1, if the phandle was malformed
23  */
24 static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
25 {
26 	const fdt32_t *val;
27 	int len;
28 
29 	val = fdt_getprop(fdto, fragment, "target", &len);
30 	if (!val)
31 		return 0;
32 
33 	if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
34 		return (uint32_t)-1;
35 
36 	return fdt32_to_cpu(*val);
37 }
38 
39 /**
40  * overlay_get_target - retrieves the offset of a fragment's target
41  * @fdt: Base device tree blob
42  * @fdto: Device tree overlay blob
43  * @fragment: node offset of the fragment in the overlay
44  *
45  * overlay_get_target() retrieves the target offset in the base
46  * device tree of a fragment, no matter how the actual targetting is
47  * done (through a phandle or a path)
48  *
49  * returns:
50  *      the targetted node offset in the base device tree
51  *      Negative error code on error
52  */
53 static int overlay_get_target(const void *fdt, const void *fdto,
54 			      int fragment)
55 {
56 	uint32_t phandle;
57 	const char *path;
58 	int path_len;
59 
60 	/* Try first to do a phandle based lookup */
61 	phandle = overlay_get_target_phandle(fdto, fragment);
62 	if (phandle == (uint32_t)-1)
63 		return -FDT_ERR_BADPHANDLE;
64 
65 	if (phandle)
66 		return fdt_node_offset_by_phandle(fdt, phandle);
67 
68 	/* And then a path based lookup */
69 	path = fdt_getprop(fdto, fragment, "target-path", &path_len);
70 	if (!path) {
71 		/*
72 		 * If we haven't found either a target or a
73 		 * target-path property in a node that contains a
74 		 * __overlay__ subnode (we wouldn't be called
75 		 * otherwise), consider it a improperly written
76 		 * overlay
77 		 */
78 		if (path_len == -FDT_ERR_NOTFOUND)
79 			return -FDT_ERR_BADOVERLAY;
80 
81 		return path_len;
82 	}
83 
84 	return fdt_path_offset(fdt, path);
85 }
86 
87 /**
88  * overlay_phandle_add_offset - Increases a phandle by an offset
89  * @fdt: Base device tree blob
90  * @node: Device tree overlay blob
91  * @name: Name of the property to modify (phandle or linux,phandle)
92  * @delta: offset to apply
93  *
94  * overlay_phandle_add_offset() increments a node phandle by a given
95  * offset.
96  *
97  * returns:
98  *      0 on success.
99  *      Negative error code on error
100  */
101 static int overlay_phandle_add_offset(void *fdt, int node,
102 				      const char *name, uint32_t delta)
103 {
104 	const fdt32_t *val;
105 	uint32_t adj_val;
106 	int len;
107 
108 	val = fdt_getprop(fdt, node, name, &len);
109 	if (!val)
110 		return len;
111 
112 	if (len != sizeof(*val))
113 		return -FDT_ERR_BADPHANDLE;
114 
115 	adj_val = fdt32_to_cpu(*val);
116 	if ((adj_val + delta) < adj_val)
117 		return -FDT_ERR_NOPHANDLES;
118 
119 	adj_val += delta;
120 	if (adj_val == (uint32_t)-1)
121 		return -FDT_ERR_NOPHANDLES;
122 
123 	return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
124 }
125 
126 /**
127  * overlay_adjust_node_phandles - Offsets the phandles of a node
128  * @fdto: Device tree overlay blob
129  * @node: Offset of the node we want to adjust
130  * @delta: Offset to shift the phandles of
131  *
132  * overlay_adjust_node_phandles() adds a constant to all the phandles
133  * of a given node. This is mainly use as part of the overlay
134  * application process, when we want to update all the overlay
135  * phandles to not conflict with the overlays of the base device tree.
136  *
137  * returns:
138  *      0 on success
139  *      Negative error code on failure
140  */
141 static int overlay_adjust_node_phandles(void *fdto, int node,
142 					uint32_t delta)
143 {
144 	int child;
145 	int ret;
146 
147 	ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
148 	if (ret && ret != -FDT_ERR_NOTFOUND)
149 		return ret;
150 
151 	ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
152 	if (ret && ret != -FDT_ERR_NOTFOUND)
153 		return ret;
154 
155 	fdt_for_each_subnode(child, fdto, node) {
156 		ret = overlay_adjust_node_phandles(fdto, child, delta);
157 		if (ret)
158 			return ret;
159 	}
160 
161 	return 0;
162 }
163 
164 /**
165  * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
166  * @fdto: Device tree overlay blob
167  * @delta: Offset to shift the phandles of
168  *
169  * overlay_adjust_local_phandles() adds a constant to all the
170  * phandles of an overlay. This is mainly use as part of the overlay
171  * application process, when we want to update all the overlay
172  * phandles to not conflict with the overlays of the base device tree.
173  *
174  * returns:
175  *      0 on success
176  *      Negative error code on failure
177  */
178 static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
179 {
180 	/*
181 	 * Start adjusting the phandles from the overlay root
182 	 */
183 	return overlay_adjust_node_phandles(fdto, 0, delta);
184 }
185 
186 /**
187  * overlay_update_local_node_references - Adjust the overlay references
188  * @fdto: Device tree overlay blob
189  * @tree_node: Node offset of the node to operate on
190  * @fixup_node: Node offset of the matching local fixups node
191  * @delta: Offset to shift the phandles of
192  *
193  * overlay_update_local_nodes_references() update the phandles
194  * pointing to a node within the device tree overlay by adding a
195  * constant delta.
196  *
197  * This is mainly used as part of a device tree application process,
198  * where you want the device tree overlays phandles to not conflict
199  * with the ones from the base device tree before merging them.
200  *
201  * returns:
202  *      0 on success
203  *      Negative error code on failure
204  */
205 static int overlay_update_local_node_references(void *fdto,
206 						int tree_node,
207 						int fixup_node,
208 						uint32_t delta)
209 {
210 	int fixup_prop;
211 	int fixup_child;
212 	int ret;
213 
214 	fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
215 		const fdt32_t *fixup_val;
216 		const char *tree_val;
217 		const char *name;
218 		int fixup_len;
219 		int tree_len;
220 		int i;
221 
222 		fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
223 						  &name, &fixup_len);
224 		if (!fixup_val)
225 			return fixup_len;
226 
227 		if (fixup_len % sizeof(uint32_t))
228 			return -FDT_ERR_BADOVERLAY;
229 
230 		tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
231 		if (!tree_val) {
232 			if (tree_len == -FDT_ERR_NOTFOUND)
233 				return -FDT_ERR_BADOVERLAY;
234 
235 			return tree_len;
236 		}
237 
238 		for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
239 			fdt32_t adj_val;
240 			uint32_t poffset;
241 
242 			poffset = fdt32_to_cpu(fixup_val[i]);
243 
244 			/*
245 			 * phandles to fixup can be unaligned.
246 			 *
247 			 * Use a memcpy for the architectures that do
248 			 * not support unaligned accesses.
249 			 */
250 			memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
251 
252 			adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta);
253 
254 			ret = fdt_setprop_inplace_namelen_partial(fdto,
255 								  tree_node,
256 								  name,
257 								  strlen(name),
258 								  poffset,
259 								  &adj_val,
260 								  sizeof(adj_val));
261 			if (ret == -FDT_ERR_NOSPACE)
262 				return -FDT_ERR_BADOVERLAY;
263 
264 			if (ret)
265 				return ret;
266 		}
267 	}
268 
269 	fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
270 		const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
271 							    NULL);
272 		int tree_child;
273 
274 		tree_child = fdt_subnode_offset(fdto, tree_node,
275 						fixup_child_name);
276 		if (tree_child == -FDT_ERR_NOTFOUND)
277 			return -FDT_ERR_BADOVERLAY;
278 		if (tree_child < 0)
279 			return tree_child;
280 
281 		ret = overlay_update_local_node_references(fdto,
282 							   tree_child,
283 							   fixup_child,
284 							   delta);
285 		if (ret)
286 			return ret;
287 	}
288 
289 	return 0;
290 }
291 
292 /**
293  * overlay_update_local_references - Adjust the overlay references
294  * @fdto: Device tree overlay blob
295  * @delta: Offset to shift the phandles of
296  *
297  * overlay_update_local_references() update all the phandles pointing
298  * to a node within the device tree overlay by adding a constant
299  * delta to not conflict with the base overlay.
300  *
301  * This is mainly used as part of a device tree application process,
302  * where you want the device tree overlays phandles to not conflict
303  * with the ones from the base device tree before merging them.
304  *
305  * returns:
306  *      0 on success
307  *      Negative error code on failure
308  */
309 static int overlay_update_local_references(void *fdto, uint32_t delta)
310 {
311 	int fixups;
312 
313 	fixups = fdt_path_offset(fdto, "/__local_fixups__");
314 	if (fixups < 0) {
315 		/* There's no local phandles to adjust, bail out */
316 		if (fixups == -FDT_ERR_NOTFOUND)
317 			return 0;
318 
319 		return fixups;
320 	}
321 
322 	/*
323 	 * Update our local references from the root of the tree
324 	 */
325 	return overlay_update_local_node_references(fdto, 0, fixups,
326 						    delta);
327 }
328 
329 /**
330  * overlay_fixup_one_phandle - Set an overlay phandle to the base one
331  * @fdt: Base Device Tree blob
332  * @fdto: Device tree overlay blob
333  * @symbols_off: Node offset of the symbols node in the base device tree
334  * @path: Path to a node holding a phandle in the overlay
335  * @path_len: number of path characters to consider
336  * @name: Name of the property holding the phandle reference in the overlay
337  * @name_len: number of name characters to consider
338  * @poffset: Offset within the overlay property where the phandle is stored
339  * @label: Label of the node referenced by the phandle
340  *
341  * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
342  * a node in the base device tree.
343  *
344  * This is part of the device tree overlay application process, when
345  * you want all the phandles in the overlay to point to the actual
346  * base dt nodes.
347  *
348  * returns:
349  *      0 on success
350  *      Negative error code on failure
351  */
352 static int overlay_fixup_one_phandle(void *fdt, void *fdto,
353 				     int symbols_off,
354 				     const char *path, uint32_t path_len,
355 				     const char *name, uint32_t name_len,
356 				     int poffset, const char *label)
357 {
358 	const char *symbol_path;
359 	uint32_t phandle;
360 	fdt32_t phandle_prop;
361 	int symbol_off, fixup_off;
362 	int prop_len;
363 
364 	if (symbols_off < 0)
365 		return symbols_off;
366 
367 	symbol_path = fdt_getprop(fdt, symbols_off, label,
368 				  &prop_len);
369 	if (!symbol_path)
370 		return prop_len;
371 
372 	symbol_off = fdt_path_offset(fdt, symbol_path);
373 	if (symbol_off < 0)
374 		return symbol_off;
375 
376 	phandle = fdt_get_phandle(fdt, symbol_off);
377 	if (!phandle)
378 		return -FDT_ERR_NOTFOUND;
379 
380 	fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
381 	if (fixup_off == -FDT_ERR_NOTFOUND)
382 		return -FDT_ERR_BADOVERLAY;
383 	if (fixup_off < 0)
384 		return fixup_off;
385 
386 	phandle_prop = cpu_to_fdt32(phandle);
387 	return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
388 						   name, name_len, poffset,
389 						   &phandle_prop,
390 						   sizeof(phandle_prop));
391 };
392 
393 /**
394  * overlay_fixup_phandle - Set an overlay phandle to the base one
395  * @fdt: Base Device Tree blob
396  * @fdto: Device tree overlay blob
397  * @symbols_off: Node offset of the symbols node in the base device tree
398  * @property: Property offset in the overlay holding the list of fixups
399  *
400  * overlay_fixup_phandle() resolves all the overlay phandles pointed
401  * to in a __fixups__ property, and updates them to match the phandles
402  * in use in the base device tree.
403  *
404  * This is part of the device tree overlay application process, when
405  * you want all the phandles in the overlay to point to the actual
406  * base dt nodes.
407  *
408  * returns:
409  *      0 on success
410  *      Negative error code on failure
411  */
412 static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
413 				 int property)
414 {
415 	const char *value;
416 	const char *label;
417 	int len;
418 
419 	value = fdt_getprop_by_offset(fdto, property,
420 				      &label, &len);
421 	if (!value) {
422 		if (len == -FDT_ERR_NOTFOUND)
423 			return -FDT_ERR_INTERNAL;
424 
425 		return len;
426 	}
427 
428 	do {
429 		const char *path, *name, *fixup_end;
430 		const char *fixup_str = value;
431 		uint32_t path_len, name_len;
432 		uint32_t fixup_len;
433 		char *sep, *endptr;
434 		int poffset, ret;
435 
436 		fixup_end = memchr(value, '\0', len);
437 		if (!fixup_end)
438 			return -FDT_ERR_BADOVERLAY;
439 		fixup_len = fixup_end - fixup_str;
440 
441 		len -= fixup_len + 1;
442 		value += fixup_len + 1;
443 
444 		path = fixup_str;
445 		sep = memchr(fixup_str, ':', fixup_len);
446 		if (!sep || *sep != ':')
447 			return -FDT_ERR_BADOVERLAY;
448 
449 		path_len = sep - path;
450 		if (path_len == (fixup_len - 1))
451 			return -FDT_ERR_BADOVERLAY;
452 
453 		fixup_len -= path_len + 1;
454 		name = sep + 1;
455 		sep = memchr(name, ':', fixup_len);
456 		if (!sep || *sep != ':')
457 			return -FDT_ERR_BADOVERLAY;
458 
459 		name_len = sep - name;
460 		if (!name_len)
461 			return -FDT_ERR_BADOVERLAY;
462 
463 		poffset = strtoul(sep + 1, &endptr, 10);
464 		if ((*endptr != '\0') || (endptr <= (sep + 1)))
465 			return -FDT_ERR_BADOVERLAY;
466 
467 		ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
468 						path, path_len, name, name_len,
469 						poffset, label);
470 		if (ret)
471 			return ret;
472 	} while (len > 0);
473 
474 	return 0;
475 }
476 
477 /**
478  * overlay_fixup_phandles - Resolve the overlay phandles to the base
479  *                          device tree
480  * @fdt: Base Device Tree blob
481  * @fdto: Device tree overlay blob
482  *
483  * overlay_fixup_phandles() resolves all the overlay phandles pointing
484  * to nodes in the base device tree.
485  *
486  * This is one of the steps of the device tree overlay application
487  * process, when you want all the phandles in the overlay to point to
488  * the actual base dt nodes.
489  *
490  * returns:
491  *      0 on success
492  *      Negative error code on failure
493  */
494 static int overlay_fixup_phandles(void *fdt, void *fdto)
495 {
496 	int fixups_off, symbols_off;
497 	int property;
498 
499 	/* We can have overlays without any fixups */
500 	fixups_off = fdt_path_offset(fdto, "/__fixups__");
501 	if (fixups_off == -FDT_ERR_NOTFOUND)
502 		return 0; /* nothing to do */
503 	if (fixups_off < 0)
504 		return fixups_off;
505 
506 	/* And base DTs without symbols */
507 	symbols_off = fdt_path_offset(fdt, "/__symbols__");
508 	if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
509 		return symbols_off;
510 
511 	fdt_for_each_property_offset(property, fdto, fixups_off) {
512 		int ret;
513 
514 		ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
515 		if (ret)
516 			return ret;
517 	}
518 
519 	return 0;
520 }
521 
522 /**
523  * overlay_apply_node - Merges a node into the base device tree
524  * @fdt: Base Device Tree blob
525  * @target: Node offset in the base device tree to apply the fragment to
526  * @fdto: Device tree overlay blob
527  * @node: Node offset in the overlay holding the changes to merge
528  *
529  * overlay_apply_node() merges a node into a target base device tree
530  * node pointed.
531  *
532  * This is part of the final step in the device tree overlay
533  * application process, when all the phandles have been adjusted and
534  * resolved and you just have to merge overlay into the base device
535  * tree.
536  *
537  * returns:
538  *      0 on success
539  *      Negative error code on failure
540  */
541 static int overlay_apply_node(void *fdt, int target,
542 			      void *fdto, int node)
543 {
544 	int property;
545 	int subnode;
546 
547 	fdt_for_each_property_offset(property, fdto, node) {
548 		const char *name;
549 		const void *prop;
550 		int prop_len;
551 		int ret;
552 
553 		prop = fdt_getprop_by_offset(fdto, property, &name,
554 					     &prop_len);
555 		if (prop_len == -FDT_ERR_NOTFOUND)
556 			return -FDT_ERR_INTERNAL;
557 		if (prop_len < 0)
558 			return prop_len;
559 
560 		ret = fdt_setprop(fdt, target, name, prop, prop_len);
561 		if (ret)
562 			return ret;
563 	}
564 
565 	fdt_for_each_subnode(subnode, fdto, node) {
566 		const char *name = fdt_get_name(fdto, subnode, NULL);
567 		int nnode;
568 		int ret;
569 
570 		nnode = fdt_add_subnode(fdt, target, name);
571 		if (nnode == -FDT_ERR_EXISTS) {
572 			nnode = fdt_subnode_offset(fdt, target, name);
573 			if (nnode == -FDT_ERR_NOTFOUND)
574 				return -FDT_ERR_INTERNAL;
575 		}
576 
577 		if (nnode < 0)
578 			return nnode;
579 
580 		ret = overlay_apply_node(fdt, nnode, fdto, subnode);
581 		if (ret)
582 			return ret;
583 	}
584 
585 	return 0;
586 }
587 
588 /**
589  * overlay_merge - Merge an overlay into its base device tree
590  * @fdt: Base Device Tree blob
591  * @fdto: Device tree overlay blob
592  *
593  * overlay_merge() merges an overlay into its base device tree.
594  *
595  * This is the final step in the device tree overlay application
596  * process, when all the phandles have been adjusted and resolved and
597  * you just have to merge overlay into the base device tree.
598  *
599  * returns:
600  *      0 on success
601  *      Negative error code on failure
602  */
603 static int overlay_merge(void *fdt, void *fdto)
604 {
605 	int fragment;
606 
607 	fdt_for_each_subnode(fragment, fdto, 0) {
608 		int overlay;
609 		int target;
610 		int ret;
611 
612 		/*
613 		 * Each fragments will have an __overlay__ node. If
614 		 * they don't, it's not supposed to be merged
615 		 */
616 		overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
617 		if (overlay == -FDT_ERR_NOTFOUND)
618 			continue;
619 
620 		if (overlay < 0)
621 			return overlay;
622 
623 		target = overlay_get_target(fdt, fdto, fragment);
624 		if (target < 0)
625 			return target;
626 
627 		ret = overlay_apply_node(fdt, target, fdto, overlay);
628 		if (ret)
629 			return ret;
630 	}
631 
632 	return 0;
633 }
634 
635 int fdt_overlay_apply(void *fdt, void *fdto)
636 {
637 	uint32_t delta = fdt_get_max_phandle(fdt);
638 	int ret;
639 
640 	FDT_CHECK_HEADER(fdt);
641 	FDT_CHECK_HEADER(fdto);
642 
643 	ret = overlay_adjust_local_phandles(fdto, delta);
644 	if (ret)
645 		goto err;
646 
647 	ret = overlay_update_local_references(fdto, delta);
648 	if (ret)
649 		goto err;
650 
651 	ret = overlay_fixup_phandles(fdt, fdto);
652 	if (ret)
653 		goto err;
654 
655 	ret = overlay_merge(fdt, fdto);
656 	if (ret)
657 		goto err;
658 
659 	/*
660 	 * The overlay has been damaged, erase its magic.
661 	 */
662 	fdt_set_magic(fdto, ~0);
663 
664 	return 0;
665 
666 err:
667 	/*
668 	 * The overlay might have been damaged, erase its magic.
669 	 */
670 	fdt_set_magic(fdto, ~0);
671 
672 	/*
673 	 * The base device tree might have been damaged, erase its
674 	 * magic.
675 	 */
676 	fdt_set_magic(fdt, ~0);
677 
678 	return ret;
679 }
680