fdt_overlay.c revision 1.1.1.1.4.2 1 /* $NetBSD: fdt_overlay.c,v 1.1.1.1.4.2 2017/07/18 17:08:08 snj 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