~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/scripts/dtc/libfdt/fdt_overlay.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
  2 /*
  3  * libfdt - Flat Device Tree manipulation
  4  * Copyright (C) 2016 Free Electrons
  5  * Copyright (C) 2016 NextThing Co.
  6  */
  7 #include "libfdt_env.h"
  8 
  9 #include <fdt.h>
 10 #include <libfdt.h>
 11 
 12 #include "libfdt_internal.h"
 13 
 14 /**
 15  * overlay_get_target_phandle - retrieves the target phandle of a fragment
 16  * @fdto: pointer to the device tree overlay blob
 17  * @fragment: node offset of the fragment in the overlay
 18  *
 19  * overlay_get_target_phandle() retrieves the target phandle of an
 20  * overlay fragment when that fragment uses a phandle (target
 21  * property) instead of a path (target-path property).
 22  *
 23  * returns:
 24  *      the phandle pointed by the target property
 25  *      0, if the phandle was not found
 26  *      -1, if the phandle was malformed
 27  */
 28 static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
 29 {
 30         const fdt32_t *val;
 31         int len;
 32 
 33         val = fdt_getprop(fdto, fragment, "target", &len);
 34         if (!val)
 35                 return 0;
 36 
 37         if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
 38                 return (uint32_t)-1;
 39 
 40         return fdt32_to_cpu(*val);
 41 }
 42 
 43 int fdt_overlay_target_offset(const void *fdt, const void *fdto,
 44                               int fragment_offset, char const **pathp)
 45 {
 46         uint32_t phandle;
 47         const char *path = NULL;
 48         int path_len = 0, ret;
 49 
 50         /* Try first to do a phandle based lookup */
 51         phandle = overlay_get_target_phandle(fdto, fragment_offset);
 52         if (phandle == (uint32_t)-1)
 53                 return -FDT_ERR_BADPHANDLE;
 54 
 55         /* no phandle, try path */
 56         if (!phandle) {
 57                 /* And then a path based lookup */
 58                 path = fdt_getprop(fdto, fragment_offset, "target-path", &path_len);
 59                 if (path)
 60                         ret = fdt_path_offset(fdt, path);
 61                 else
 62                         ret = path_len;
 63         } else
 64                 ret = fdt_node_offset_by_phandle(fdt, phandle);
 65 
 66         /*
 67         * If we haven't found either a target or a
 68         * target-path property in a node that contains a
 69         * __overlay__ subnode (we wouldn't be called
 70         * otherwise), consider it a improperly written
 71         * overlay
 72         */
 73         if (ret < 0 && path_len == -FDT_ERR_NOTFOUND)
 74                 ret = -FDT_ERR_BADOVERLAY;
 75 
 76         /* return on error */
 77         if (ret < 0)
 78                 return ret;
 79 
 80         /* return pointer to path (if available) */
 81         if (pathp)
 82                 *pathp = path ? path : NULL;
 83 
 84         return ret;
 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         fdt32_t *valp, val;
105         int len;
106 
107         valp = fdt_getprop_w(fdt, node, name, &len);
108         if (!valp)
109                 return len;
110 
111         if (len != sizeof(val))
112                 return -FDT_ERR_BADPHANDLE;
113 
114         val = fdt32_ld(valp);
115         if (val + delta < val || val + delta == (uint32_t)-1)
116                 return -FDT_ERR_NOPHANDLES;
117 
118         fdt32_st(valp, val + delta);
119         return 0;
120 }
121 
122 /**
123  * overlay_adjust_node_phandles - Offsets the phandles of a node
124  * @fdto: Device tree overlay blob
125  * @node: Offset of the node we want to adjust
126  * @delta: Offset to shift the phandles of
127  *
128  * overlay_adjust_node_phandles() adds a constant to all the phandles
129  * of a given node. This is mainly use as part of the overlay
130  * application process, when we want to update all the overlay
131  * phandles to not conflict with the overlays of the base device tree.
132  *
133  * returns:
134  *      0 on success
135  *      Negative error code on failure
136  */
137 static int overlay_adjust_node_phandles(void *fdto, int node,
138                                         uint32_t delta)
139 {
140         int child;
141         int ret;
142 
143         ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
144         if (ret && ret != -FDT_ERR_NOTFOUND)
145                 return ret;
146 
147         ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
148         if (ret && ret != -FDT_ERR_NOTFOUND)
149                 return ret;
150 
151         fdt_for_each_subnode(child, fdto, node) {
152                 ret = overlay_adjust_node_phandles(fdto, child, delta);
153                 if (ret)
154                         return ret;
155         }
156 
157         return 0;
158 }
159 
160 /**
161  * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
162  * @fdto: Device tree overlay blob
163  * @delta: Offset to shift the phandles of
164  *
165  * overlay_adjust_local_phandles() adds a constant to all the
166  * phandles of an overlay. This is mainly use as part of the overlay
167  * application process, when we want to update all the overlay
168  * phandles to not conflict with the overlays of the base device tree.
169  *
170  * returns:
171  *      0 on success
172  *      Negative error code on failure
173  */
174 static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
175 {
176         /*
177          * Start adjusting the phandles from the overlay root
178          */
179         return overlay_adjust_node_phandles(fdto, 0, delta);
180 }
181 
182 /**
183  * overlay_update_local_node_references - Adjust the overlay references
184  * @fdto: Device tree overlay blob
185  * @tree_node: Node offset of the node to operate on
186  * @fixup_node: Node offset of the matching local fixups node
187  * @delta: Offset to shift the phandles of
188  *
189  * overlay_update_local_nodes_references() update the phandles
190  * pointing to a node within the device tree overlay by adding a
191  * constant delta.
192  *
193  * This is mainly used as part of a device tree application process,
194  * where you want the device tree overlays phandles to not conflict
195  * with the ones from the base device tree before merging them.
196  *
197  * returns:
198  *      0 on success
199  *      Negative error code on failure
200  */
201 static int overlay_update_local_node_references(void *fdto,
202                                                 int tree_node,
203                                                 int fixup_node,
204                                                 uint32_t delta)
205 {
206         int fixup_prop;
207         int fixup_child;
208         int ret;
209 
210         fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
211                 const fdt32_t *fixup_val;
212                 const char *name;
213                 char *tree_val;
214                 int fixup_len;
215                 int tree_len;
216                 int i;
217 
218                 fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
219                                                   &name, &fixup_len);
220                 if (!fixup_val)
221                         return fixup_len;
222 
223                 if (fixup_len % sizeof(uint32_t))
224                         return -FDT_ERR_BADOVERLAY;
225                 fixup_len /= sizeof(uint32_t);
226 
227                 tree_val = fdt_getprop_w(fdto, tree_node, name, &tree_len);
228                 if (!tree_val) {
229                         if (tree_len == -FDT_ERR_NOTFOUND)
230                                 return -FDT_ERR_BADOVERLAY;
231 
232                         return tree_len;
233                 }
234 
235                 for (i = 0; i < fixup_len; i++) {
236                         fdt32_t *refp;
237 
238                         refp = (fdt32_t *)(tree_val + fdt32_ld_(fixup_val + i));
239 
240                         /*
241                          * phandles to fixup can be unaligned, so use
242                          * fdt32_{ld,st}() to read/write them.
243                          */
244                         fdt32_st(refp, fdt32_ld(refp) + delta);
245                 }
246         }
247 
248         fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
249                 const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
250                                                             NULL);
251                 int tree_child;
252 
253                 tree_child = fdt_subnode_offset(fdto, tree_node,
254                                                 fixup_child_name);
255                 if (tree_child == -FDT_ERR_NOTFOUND)
256                         return -FDT_ERR_BADOVERLAY;
257                 if (tree_child < 0)
258                         return tree_child;
259 
260                 ret = overlay_update_local_node_references(fdto,
261                                                            tree_child,
262                                                            fixup_child,
263                                                            delta);
264                 if (ret)
265                         return ret;
266         }
267 
268         return 0;
269 }
270 
271 /**
272  * overlay_update_local_references - Adjust the overlay references
273  * @fdto: Device tree overlay blob
274  * @delta: Offset to shift the phandles of
275  *
276  * overlay_update_local_references() update all the phandles pointing
277  * to a node within the device tree overlay by adding a constant
278  * delta to not conflict with the base overlay.
279  *
280  * This is mainly used as part of a device tree application process,
281  * where you want the device tree overlays phandles to not conflict
282  * with the ones from the base device tree before merging them.
283  *
284  * returns:
285  *      0 on success
286  *      Negative error code on failure
287  */
288 static int overlay_update_local_references(void *fdto, uint32_t delta)
289 {
290         int fixups;
291 
292         fixups = fdt_path_offset(fdto, "/__local_fixups__");
293         if (fixups < 0) {
294                 /* There's no local phandles to adjust, bail out */
295                 if (fixups == -FDT_ERR_NOTFOUND)
296                         return 0;
297 
298                 return fixups;
299         }
300 
301         /*
302          * Update our local references from the root of the tree
303          */
304         return overlay_update_local_node_references(fdto, 0, fixups,
305                                                     delta);
306 }
307 
308 /**
309  * overlay_fixup_one_phandle - Set an overlay phandle to the base one
310  * @fdt: Base Device Tree blob
311  * @fdto: Device tree overlay blob
312  * @symbols_off: Node offset of the symbols node in the base device tree
313  * @path: Path to a node holding a phandle in the overlay
314  * @path_len: number of path characters to consider
315  * @name: Name of the property holding the phandle reference in the overlay
316  * @name_len: number of name characters to consider
317  * @poffset: Offset within the overlay property where the phandle is stored
318  * @phandle: Phandle referencing the node
319  *
320  * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
321  * a node in the base device tree.
322  *
323  * This is part of the device tree overlay application process, when
324  * you want all the phandles in the overlay to point to the actual
325  * base dt nodes.
326  *
327  * returns:
328  *      0 on success
329  *      Negative error code on failure
330  */
331 static int overlay_fixup_one_phandle(void *fdt, void *fdto,
332                                      int symbols_off,
333                                      const char *path, uint32_t path_len,
334                                      const char *name, uint32_t name_len,
335                                      int poffset, uint32_t phandle)
336 {
337         fdt32_t phandle_prop;
338         int fixup_off;
339 
340         if (symbols_off < 0)
341                 return symbols_off;
342 
343         fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
344         if (fixup_off == -FDT_ERR_NOTFOUND)
345                 return -FDT_ERR_BADOVERLAY;
346         if (fixup_off < 0)
347                 return fixup_off;
348 
349         phandle_prop = cpu_to_fdt32(phandle);
350         return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
351                                                    name, name_len, poffset,
352                                                    &phandle_prop,
353                                                    sizeof(phandle_prop));
354 };
355 
356 /**
357  * overlay_fixup_phandle - Set an overlay phandle to the base one
358  * @fdt: Base Device Tree blob
359  * @fdto: Device tree overlay blob
360  * @symbols_off: Node offset of the symbols node in the base device tree
361  * @property: Property offset in the overlay holding the list of fixups
362  *
363  * overlay_fixup_phandle() resolves all the overlay phandles pointed
364  * to in a __fixups__ property, and updates them to match the phandles
365  * in use in the base device tree.
366  *
367  * This is part of the device tree overlay application process, when
368  * you want all the phandles in the overlay to point to the actual
369  * base dt nodes.
370  *
371  * returns:
372  *      0 on success
373  *      Negative error code on failure
374  */
375 static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
376                                  int property)
377 {
378         const char *value;
379         const char *label;
380         int len;
381         const char *symbol_path;
382         int prop_len;
383         int symbol_off;
384         uint32_t phandle;
385 
386         value = fdt_getprop_by_offset(fdto, property,
387                                       &label, &len);
388         if (!value) {
389                 if (len == -FDT_ERR_NOTFOUND)
390                         return -FDT_ERR_INTERNAL;
391 
392                 return len;
393         }
394 
395         symbol_path = fdt_getprop(fdt, symbols_off, label, &prop_len);
396         if (!symbol_path)
397                 return prop_len;
398         
399         symbol_off = fdt_path_offset(fdt, symbol_path);
400         if (symbol_off < 0)
401                 return symbol_off;
402         
403         phandle = fdt_get_phandle(fdt, symbol_off);
404         if (!phandle)
405                 return -FDT_ERR_NOTFOUND;
406 
407         do {
408                 const char *path, *name, *fixup_end;
409                 const char *fixup_str = value;
410                 uint32_t path_len, name_len;
411                 uint32_t fixup_len;
412                 char *sep, *endptr;
413                 int poffset, ret;
414 
415                 fixup_end = memchr(value, '\0', len);
416                 if (!fixup_end)
417                         return -FDT_ERR_BADOVERLAY;
418                 fixup_len = fixup_end - fixup_str;
419 
420                 len -= fixup_len + 1;
421                 value += fixup_len + 1;
422 
423                 path = fixup_str;
424                 sep = memchr(fixup_str, ':', fixup_len);
425                 if (!sep || *sep != ':')
426                         return -FDT_ERR_BADOVERLAY;
427 
428                 path_len = sep - path;
429                 if (path_len == (fixup_len - 1))
430                         return -FDT_ERR_BADOVERLAY;
431 
432                 fixup_len -= path_len + 1;
433                 name = sep + 1;
434                 sep = memchr(name, ':', fixup_len);
435                 if (!sep || *sep != ':')
436                         return -FDT_ERR_BADOVERLAY;
437 
438                 name_len = sep - name;
439                 if (!name_len)
440                         return -FDT_ERR_BADOVERLAY;
441 
442                 poffset = strtoul(sep + 1, &endptr, 10);
443                 if ((*endptr != '\0') || (endptr <= (sep + 1)))
444                         return -FDT_ERR_BADOVERLAY;
445 
446                 ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
447                                                 path, path_len, name, name_len,
448                                                 poffset, phandle);
449                 if (ret)
450                         return ret;
451         } while (len > 0);
452 
453         return 0;
454 }
455 
456 /**
457  * overlay_fixup_phandles - Resolve the overlay phandles to the base
458  *                          device tree
459  * @fdt: Base Device Tree blob
460  * @fdto: Device tree overlay blob
461  *
462  * overlay_fixup_phandles() resolves all the overlay phandles pointing
463  * to nodes in the base device tree.
464  *
465  * This is one of the steps of the device tree overlay application
466  * process, when you want all the phandles in the overlay to point to
467  * the actual base dt nodes.
468  *
469  * returns:
470  *      0 on success
471  *      Negative error code on failure
472  */
473 static int overlay_fixup_phandles(void *fdt, void *fdto)
474 {
475         int fixups_off, symbols_off;
476         int property;
477 
478         /* We can have overlays without any fixups */
479         fixups_off = fdt_path_offset(fdto, "/__fixups__");
480         if (fixups_off == -FDT_ERR_NOTFOUND)
481                 return 0; /* nothing to do */
482         if (fixups_off < 0)
483                 return fixups_off;
484 
485         /* And base DTs without symbols */
486         symbols_off = fdt_path_offset(fdt, "/__symbols__");
487         if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
488                 return symbols_off;
489 
490         fdt_for_each_property_offset(property, fdto, fixups_off) {
491                 int ret;
492 
493                 ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
494                 if (ret)
495                         return ret;
496         }
497 
498         return 0;
499 }
500 
501 /**
502  * overlay_adjust_local_conflicting_phandle: Changes a phandle value
503  * @fdto: Device tree overlay
504  * @node: The node the phandle is set for
505  * @fdt_phandle: The new value for the phandle
506  *
507  * returns:
508  *      0 on success
509  *      Negative error code on failure
510  */
511 static int overlay_adjust_local_conflicting_phandle(void *fdto, int node,
512                                                     uint32_t fdt_phandle)
513 {
514         const fdt32_t *php;
515         int len, ret;
516 
517         php = fdt_getprop(fdto, node, "phandle", &len);
518         if (php && len == sizeof(*php)) {
519                 ret = fdt_setprop_inplace_u32(fdto, node, "phandle", fdt_phandle);
520                 if (ret)
521                         return ret;
522         }
523 
524         php = fdt_getprop(fdto, node, "linux,phandle", &len);
525         if (php && len == sizeof(*php)) {
526                 ret = fdt_setprop_inplace_u32(fdto, node, "linux,phandle", fdt_phandle);
527                 if (ret)
528                         return ret;
529         }
530 
531         return 0;
532 }
533 
534 /**
535  * overlay_update_node_conflicting_references - Recursively replace phandle values
536  * @fdto: Device tree overlay blob
537  * @tree_node: Node to recurse into
538  * @fixup_node: Node offset of the matching local fixups node
539  * @fdt_phandle: Value to replace phandles with
540  * @fdto_phandle: Value to be replaced
541  *
542  * Replaces all phandles with value @fdto_phandle by @fdt_phandle.
543  *
544  * returns:
545  *      0 on success
546  *      Negative error code on failure
547  */
548 static int overlay_update_node_conflicting_references(void *fdto, int tree_node,
549                                                       int fixup_node,
550                                                       uint32_t fdt_phandle,
551                                                       uint32_t fdto_phandle)
552 {
553         int fixup_prop;
554         int fixup_child;
555         int ret;
556 
557         fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
558                 const fdt32_t *fixup_val;
559                 const char *name;
560                 char *tree_val;
561                 int fixup_len;
562                 int tree_len;
563                 int i;
564 
565                 fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
566                                                   &name, &fixup_len);
567                 if (!fixup_val)
568                         return fixup_len;
569 
570                 if (fixup_len % sizeof(uint32_t))
571                         return -FDT_ERR_BADOVERLAY;
572                 fixup_len /= sizeof(uint32_t);
573 
574                 tree_val = fdt_getprop_w(fdto, tree_node, name, &tree_len);
575                 if (!tree_val) {
576                         if (tree_len == -FDT_ERR_NOTFOUND)
577                                 return -FDT_ERR_BADOVERLAY;
578 
579                         return tree_len;
580                 }
581 
582                 for (i = 0; i < fixup_len; i++) {
583                         fdt32_t *refp;
584                         uint32_t valp;
585 
586                         refp = (fdt32_t *)(tree_val + fdt32_ld_(fixup_val + i));
587                         valp = fdt32_ld(refp);
588 
589                         if (valp == fdto_phandle)
590                                 fdt32_st(refp, fdt_phandle);
591                 }
592         }
593 
594         fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
595                 const char *fixup_child_name = fdt_get_name(fdto, fixup_child, NULL);
596                 int tree_child;
597 
598                 tree_child = fdt_subnode_offset(fdto, tree_node, fixup_child_name);
599 
600                 if (tree_child == -FDT_ERR_NOTFOUND)
601                         return -FDT_ERR_BADOVERLAY;
602                 if (tree_child < 0)
603                         return tree_child;
604 
605                 ret = overlay_update_node_conflicting_references(fdto, tree_child,
606                                                                  fixup_child,
607                                                                  fdt_phandle,
608                                                                  fdto_phandle);
609                 if (ret)
610                         return ret;
611         }
612 
613         return 0;
614 }
615 
616 /**
617  * overlay_update_local_conflicting_references - Recursively replace phandle values
618  * @fdto: Device tree overlay blob
619  * @fdt_phandle: Value to replace phandles with
620  * @fdto_phandle: Value to be replaced
621  *
622  * Replaces all phandles with value @fdto_phandle by @fdt_phandle.
623  *
624  * returns:
625  *      0 on success
626  *      Negative error code on failure
627  */
628 static int overlay_update_local_conflicting_references(void *fdto,
629                                                        uint32_t fdt_phandle,
630                                                        uint32_t fdto_phandle)
631 {
632         int fixups;
633 
634         fixups = fdt_path_offset(fdto, "/__local_fixups__");
635         if (fixups == -FDT_ERR_NOTFOUND)
636                 return 0;
637         if (fixups < 0)
638                 return fixups;
639 
640         return overlay_update_node_conflicting_references(fdto, 0, fixups,
641                                                           fdt_phandle,
642                                                           fdto_phandle);
643 }
644 
645 /**
646  * overlay_prevent_phandle_overwrite_node - Helper function for overlay_prevent_phandle_overwrite
647  * @fdt: Base Device tree blob
648  * @fdtnode: Node in fdt that is checked for an overwrite
649  * @fdto: Device tree overlay blob
650  * @fdtonode: Node in fdto matching @fdtnode
651  *
652  * returns:
653  *      0 on success
654  *      Negative error code on failure
655  */
656 static int overlay_prevent_phandle_overwrite_node(void *fdt, int fdtnode,
657                                                   void *fdto, int fdtonode)
658 {
659         uint32_t fdt_phandle, fdto_phandle;
660         int fdtochild;
661 
662         fdt_phandle = fdt_get_phandle(fdt, fdtnode);
663         fdto_phandle = fdt_get_phandle(fdto, fdtonode);
664 
665         if (fdt_phandle && fdto_phandle) {
666                 int ret;
667 
668                 ret = overlay_adjust_local_conflicting_phandle(fdto, fdtonode,
669                                                                fdt_phandle);
670                 if (ret)
671                         return ret;
672 
673                 ret = overlay_update_local_conflicting_references(fdto,
674                                                                   fdt_phandle,
675                                                                   fdto_phandle);
676                 if (ret)
677                         return ret;
678         }
679 
680         fdt_for_each_subnode(fdtochild, fdto, fdtonode) {
681                 const char *name = fdt_get_name(fdto, fdtochild, NULL);
682                 int fdtchild;
683                 int ret;
684 
685                 fdtchild = fdt_subnode_offset(fdt, fdtnode, name);
686                 if (fdtchild == -FDT_ERR_NOTFOUND)
687                         /*
688                          * no further overwrites possible here as this node is
689                          * new
690                          */
691                         continue;
692 
693                 ret = overlay_prevent_phandle_overwrite_node(fdt, fdtchild,
694                                                              fdto, fdtochild);
695                 if (ret)
696                         return ret;
697         }
698 
699         return 0;
700 }
701 
702 /**
703  * overlay_prevent_phandle_overwrite - Fixes overlay phandles to not overwrite base phandles
704  * @fdt: Base Device Tree blob
705  * @fdto: Device tree overlay blob
706  *
707  * Checks recursively if applying fdto overwrites phandle values in the base
708  * dtb. When such a phandle is found, the fdto is changed to use the fdt's
709  * phandle value to not break references in the base.
710  *
711  * returns:
712  *      0 on success
713  *      Negative error code on failure
714  */
715 static int overlay_prevent_phandle_overwrite(void *fdt, void *fdto)
716 {
717         int fragment;
718 
719         fdt_for_each_subnode(fragment, fdto, 0) {
720                 int overlay;
721                 int target;
722                 int ret;
723 
724                 overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
725                 if (overlay == -FDT_ERR_NOTFOUND)
726                         continue;
727 
728                 if (overlay < 0)
729                         return overlay;
730 
731                 target = fdt_overlay_target_offset(fdt, fdto, fragment, NULL);
732                 if (target == -FDT_ERR_NOTFOUND)
733                         /*
734                          * The subtree doesn't exist in the base, so nothing
735                          * will be overwritten.
736                          */
737                         continue;
738                 else if (target < 0)
739                         return target;
740 
741                 ret = overlay_prevent_phandle_overwrite_node(fdt, target,
742                                                              fdto, overlay);
743                 if (ret)
744                         return ret;
745         }
746 
747         return 0;
748 }
749 
750 /**
751  * overlay_apply_node - Merges a node into the base device tree
752  * @fdt: Base Device Tree blob
753  * @target: Node offset in the base device tree to apply the fragment to
754  * @fdto: Device tree overlay blob
755  * @node: Node offset in the overlay holding the changes to merge
756  *
757  * overlay_apply_node() merges a node into a target base device tree
758  * node pointed.
759  *
760  * This is part of the final step in the device tree overlay
761  * application process, when all the phandles have been adjusted and
762  * resolved and you just have to merge overlay into the base device
763  * tree.
764  *
765  * returns:
766  *      0 on success
767  *      Negative error code on failure
768  */
769 static int overlay_apply_node(void *fdt, int target,
770                               void *fdto, int node)
771 {
772         int property;
773         int subnode;
774 
775         fdt_for_each_property_offset(property, fdto, node) {
776                 const char *name;
777                 const void *prop;
778                 int prop_len;
779                 int ret;
780 
781                 prop = fdt_getprop_by_offset(fdto, property, &name,
782                                              &prop_len);
783                 if (prop_len == -FDT_ERR_NOTFOUND)
784                         return -FDT_ERR_INTERNAL;
785                 if (prop_len < 0)
786                         return prop_len;
787 
788                 ret = fdt_setprop(fdt, target, name, prop, prop_len);
789                 if (ret)
790                         return ret;
791         }
792 
793         fdt_for_each_subnode(subnode, fdto, node) {
794                 const char *name = fdt_get_name(fdto, subnode, NULL);
795                 int nnode;
796                 int ret;
797 
798                 nnode = fdt_add_subnode(fdt, target, name);
799                 if (nnode == -FDT_ERR_EXISTS) {
800                         nnode = fdt_subnode_offset(fdt, target, name);
801                         if (nnode == -FDT_ERR_NOTFOUND)
802                                 return -FDT_ERR_INTERNAL;
803                 }
804 
805                 if (nnode < 0)
806                         return nnode;
807 
808                 ret = overlay_apply_node(fdt, nnode, fdto, subnode);
809                 if (ret)
810                         return ret;
811         }
812 
813         return 0;
814 }
815 
816 /**
817  * overlay_merge - Merge an overlay into its base device tree
818  * @fdt: Base Device Tree blob
819  * @fdto: Device tree overlay blob
820  *
821  * overlay_merge() merges an overlay into its base device tree.
822  *
823  * This is the next to last step in the device tree overlay application
824  * process, when all the phandles have been adjusted and resolved and
825  * you just have to merge overlay into the base device tree.
826  *
827  * returns:
828  *      0 on success
829  *      Negative error code on failure
830  */
831 static int overlay_merge(void *fdt, void *fdto)
832 {
833         int fragment;
834 
835         fdt_for_each_subnode(fragment, fdto, 0) {
836                 int overlay;
837                 int target;
838                 int ret;
839 
840                 /*
841                  * Each fragments will have an __overlay__ node. If
842                  * they don't, it's not supposed to be merged
843                  */
844                 overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
845                 if (overlay == -FDT_ERR_NOTFOUND)
846                         continue;
847 
848                 if (overlay < 0)
849                         return overlay;
850 
851                 target = fdt_overlay_target_offset(fdt, fdto, fragment, NULL);
852                 if (target < 0)
853                         return target;
854 
855                 ret = overlay_apply_node(fdt, target, fdto, overlay);
856                 if (ret)
857                         return ret;
858         }
859 
860         return 0;
861 }
862 
863 static int get_path_len(const void *fdt, int nodeoffset)
864 {
865         int len = 0, namelen;
866         const char *name;
867 
868         FDT_RO_PROBE(fdt);
869 
870         for (;;) {
871                 name = fdt_get_name(fdt, nodeoffset, &namelen);
872                 if (!name)
873                         return namelen;
874 
875                 /* root? we're done */
876                 if (namelen == 0)
877                         break;
878 
879                 nodeoffset = fdt_parent_offset(fdt, nodeoffset);
880                 if (nodeoffset < 0)
881                         return nodeoffset;
882                 len += namelen + 1;
883         }
884 
885         /* in case of root pretend it's "/" */
886         if (len == 0)
887                 len++;
888         return len;
889 }
890 
891 /**
892  * overlay_symbol_update - Update the symbols of base tree after a merge
893  * @fdt: Base Device Tree blob
894  * @fdto: Device tree overlay blob
895  *
896  * overlay_symbol_update() updates the symbols of the base tree with the
897  * symbols of the applied overlay
898  *
899  * This is the last step in the device tree overlay application
900  * process, allowing the reference of overlay symbols by subsequent
901  * overlay operations.
902  *
903  * returns:
904  *      0 on success
905  *      Negative error code on failure
906  */
907 static int overlay_symbol_update(void *fdt, void *fdto)
908 {
909         int root_sym, ov_sym, prop, path_len, fragment, target;
910         int len, frag_name_len, ret, rel_path_len;
911         const char *s, *e;
912         const char *path;
913         const char *name;
914         const char *frag_name;
915         const char *rel_path;
916         const char *target_path;
917         char *buf;
918         void *p;
919 
920         ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__");
921 
922         /* if no overlay symbols exist no problem */
923         if (ov_sym < 0)
924                 return 0;
925 
926         root_sym = fdt_subnode_offset(fdt, 0, "__symbols__");
927 
928         /* it no root symbols exist we should create them */
929         if (root_sym == -FDT_ERR_NOTFOUND)
930                 root_sym = fdt_add_subnode(fdt, 0, "__symbols__");
931 
932         /* any error is fatal now */
933         if (root_sym < 0)
934                 return root_sym;
935 
936         /* iterate over each overlay symbol */
937         fdt_for_each_property_offset(prop, fdto, ov_sym) {
938                 path = fdt_getprop_by_offset(fdto, prop, &name, &path_len);
939                 if (!path)
940                         return path_len;
941 
942                 /* verify it's a string property (terminated by a single \0) */
943                 if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1])
944                         return -FDT_ERR_BADVALUE;
945 
946                 /* keep end marker to avoid strlen() */
947                 e = path + path_len;
948 
949                 if (*path != '/')
950                         return -FDT_ERR_BADVALUE;
951 
952                 /* get fragment name first */
953                 s = strchr(path + 1, '/');
954                 if (!s) {
955                         /* Symbol refers to something that won't end
956                          * up in the target tree */
957                         continue;
958                 }
959 
960                 frag_name = path + 1;
961                 frag_name_len = s - path - 1;
962 
963                 /* verify format; safe since "s" lies in \0 terminated prop */
964                 len = sizeof("/__overlay__/") - 1;
965                 if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) {
966                         /* /<fragment-name>/__overlay__/<relative-subnode-path> */
967                         rel_path = s + len;
968                         rel_path_len = e - rel_path - 1;
969                 } else if ((e - s) == len
970                            && (memcmp(s, "/__overlay__", len - 1) == 0)) {
971                         /* /<fragment-name>/__overlay__ */
972                         rel_path = "";
973                         rel_path_len = 0;
974                 } else {
975                         /* Symbol refers to something that won't end
976                          * up in the target tree */
977                         continue;
978                 }
979 
980                 /* find the fragment index in which the symbol lies */
981                 ret = fdt_subnode_offset_namelen(fdto, 0, frag_name,
982                                                frag_name_len);
983                 /* not found? */
984                 if (ret < 0)
985                         return -FDT_ERR_BADOVERLAY;
986                 fragment = ret;
987 
988                 /* an __overlay__ subnode must exist */
989                 ret = fdt_subnode_offset(fdto, fragment, "__overlay__");
990                 if (ret < 0)
991                         return -FDT_ERR_BADOVERLAY;
992 
993                 /* get the target of the fragment */
994                 ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path);
995                 if (ret < 0)
996                         return ret;
997                 target = ret;
998 
999                 /* if we have a target path use */
1000                 if (!target_path) {
1001                         ret = get_path_len(fdt, target);
1002                         if (ret < 0)
1003                                 return ret;
1004                         len = ret;
1005                 } else {
1006                         len = strlen(target_path);
1007                 }
1008 
1009                 ret = fdt_setprop_placeholder(fdt, root_sym, name,
1010                                 len + (len > 1) + rel_path_len + 1, &p);
1011                 if (ret < 0)
1012                         return ret;
1013 
1014                 if (!target_path) {
1015                         /* again in case setprop_placeholder changed it */
1016                         ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path);
1017                         if (ret < 0)
1018                                 return ret;
1019                         target = ret;
1020                 }
1021 
1022                 buf = p;
1023                 if (len > 1) { /* target is not root */
1024                         if (!target_path) {
1025                                 ret = fdt_get_path(fdt, target, buf, len + 1);
1026                                 if (ret < 0)
1027                                         return ret;
1028                         } else
1029                                 memcpy(buf, target_path, len + 1);
1030 
1031                 } else
1032                         len--;
1033 
1034                 buf[len] = '/';
1035                 memcpy(buf + len + 1, rel_path, rel_path_len);
1036                 buf[len + 1 + rel_path_len] = '\0';
1037         }
1038 
1039         return 0;
1040 }
1041 
1042 int fdt_overlay_apply(void *fdt, void *fdto)
1043 {
1044         uint32_t delta;
1045         int ret;
1046 
1047         FDT_RO_PROBE(fdt);
1048         FDT_RO_PROBE(fdto);
1049 
1050         ret = fdt_find_max_phandle(fdt, &delta);
1051         if (ret)
1052                 goto err;
1053 
1054         /* Increase all phandles in the fdto by delta */
1055         ret = overlay_adjust_local_phandles(fdto, delta);
1056         if (ret)
1057                 goto err;
1058 
1059         /* Adapt the phandle values in fdto to the above increase */
1060         ret = overlay_update_local_references(fdto, delta);
1061         if (ret)
1062                 goto err;
1063 
1064         /* Update fdto's phandles using symbols from fdt */
1065         ret = overlay_fixup_phandles(fdt, fdto);
1066         if (ret)
1067                 goto err;
1068 
1069         /* Don't overwrite phandles in fdt */
1070         ret = overlay_prevent_phandle_overwrite(fdt, fdto);
1071         if (ret)
1072                 goto err;
1073 
1074         ret = overlay_merge(fdt, fdto);
1075         if (ret)
1076                 goto err;
1077 
1078         ret = overlay_symbol_update(fdt, fdto);
1079         if (ret)
1080                 goto err;
1081 
1082         /*
1083          * The overlay has been damaged, erase its magic.
1084          */
1085         fdt_set_magic(fdto, ~0);
1086 
1087         return 0;
1088 
1089 err:
1090         /*
1091          * The overlay might have been damaged, erase its magic.
1092          */
1093         fdt_set_magic(fdto, ~0);
1094 
1095         /*
1096          * The base device tree might have been damaged, erase its
1097          * magic.
1098          */
1099         fdt_set_magic(fdt, ~0);
1100 
1101         return ret;
1102 }
1103 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php