1 | /* |
2 | * libfdt - Flat Device Tree manipulation |
3 | * Copyright (C) 2016 Free Electrons |
4 | * Copyright (C) 2016 NextThing Co. |
5 | * |
6 | * libfdt is dual licensed: you can use it either under the terms of |
7 | * the GPL, or the BSD license, at your option. |
8 | * |
9 | * a) This library is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU General Public License as |
11 | * published by the Free Software Foundation; either version 2 of the |
12 | * License, or (at your option) any later version. |
13 | * |
14 | * This library is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public |
20 | * License along with this library; if not, write to the Free |
21 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
22 | * MA 02110-1301 USA |
23 | * |
24 | * Alternatively, |
25 | * |
26 | * b) Redistribution and use in source and binary forms, with or |
27 | * without modification, are permitted provided that the following |
28 | * conditions are met: |
29 | * |
30 | * 1. Redistributions of source code must retain the above |
31 | * copyright notice, this list of conditions and the following |
32 | * disclaimer. |
33 | * 2. Redistributions in binary form must reproduce the above |
34 | * copyright notice, this list of conditions and the following |
35 | * disclaimer in the documentation and/or other materials |
36 | * provided with the distribution. |
37 | * |
38 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
39 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
40 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
41 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
42 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
43 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
44 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
45 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
46 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
47 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
48 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
49 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
50 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
51 | */ |
52 | #include "libfdt_env.h" |
53 | |
54 | #include <fdt.h> |
55 | #include <libfdt.h> |
56 | |
57 | #include "libfdt_internal.h" |
58 | |
59 | /** |
60 | * overlay_get_target_phandle - retrieves the target phandle of a fragment |
61 | * @fdto: pointer to the device tree overlay blob |
62 | * @fragment: node offset of the fragment in the overlay |
63 | * |
64 | * overlay_get_target_phandle() retrieves the target phandle of an |
65 | * overlay fragment when that fragment uses a phandle (target |
66 | * property) instead of a path (target-path property). |
67 | * |
68 | * returns: |
69 | * the phandle pointed by the target property |
70 | * 0, if the phandle was not found |
71 | * -1, if the phandle was malformed |
72 | */ |
73 | static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) |
74 | { |
75 | const fdt32_t *val; |
76 | int len; |
77 | |
78 | val = fdt_getprop(fdto, fragment, "target" , &len); |
79 | if (!val) |
80 | return 0; |
81 | |
82 | if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) |
83 | return (uint32_t)-1; |
84 | |
85 | return fdt32_to_cpu(*val); |
86 | } |
87 | |
88 | /** |
89 | * overlay_get_target - retrieves the offset of a fragment's target |
90 | * @fdt: Base device tree blob |
91 | * @fdto: Device tree overlay blob |
92 | * @fragment: node offset of the fragment in the overlay |
93 | * @pathp: pointer which receives the path of the target (or NULL) |
94 | * |
95 | * overlay_get_target() retrieves the target offset in the base |
96 | * device tree of a fragment, no matter how the actual targetting is |
97 | * done (through a phandle or a path) |
98 | * |
99 | * returns: |
100 | * the targetted node offset in the base device tree |
101 | * Negative error code on error |
102 | */ |
103 | static int overlay_get_target(const void *fdt, const void *fdto, |
104 | int fragment, char const **pathp) |
105 | { |
106 | uint32_t phandle; |
107 | const char *path = NULL; |
108 | int path_len = 0, ret; |
109 | |
110 | /* Try first to do a phandle based lookup */ |
111 | phandle = overlay_get_target_phandle(fdto, fragment); |
112 | if (phandle == (uint32_t)-1) |
113 | return -FDT_ERR_BADPHANDLE; |
114 | |
115 | /* no phandle, try path */ |
116 | if (!phandle) { |
117 | /* And then a path based lookup */ |
118 | path = fdt_getprop(fdto, fragment, "target-path" , &path_len); |
119 | if (path) |
120 | ret = fdt_path_offset(fdt, path); |
121 | else |
122 | ret = path_len; |
123 | } else |
124 | ret = fdt_node_offset_by_phandle(fdt, phandle); |
125 | |
126 | /* |
127 | * If we haven't found either a target or a |
128 | * target-path property in a node that contains a |
129 | * __overlay__ subnode (we wouldn't be called |
130 | * otherwise), consider it a improperly written |
131 | * overlay |
132 | */ |
133 | if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) |
134 | ret = -FDT_ERR_BADOVERLAY; |
135 | |
136 | /* return on error */ |
137 | if (ret < 0) |
138 | return ret; |
139 | |
140 | /* return pointer to path (if available) */ |
141 | if (pathp) |
142 | *pathp = path ? path : NULL; |
143 | |
144 | return ret; |
145 | } |
146 | |
147 | /** |
148 | * overlay_phandle_add_offset - Increases a phandle by an offset |
149 | * @fdt: Base device tree blob |
150 | * @node: Device tree overlay blob |
151 | * @name: Name of the property to modify (phandle or linux,phandle) |
152 | * @delta: offset to apply |
153 | * |
154 | * overlay_phandle_add_offset() increments a node phandle by a given |
155 | * offset. |
156 | * |
157 | * returns: |
158 | * 0 on success. |
159 | * Negative error code on error |
160 | */ |
161 | static int overlay_phandle_add_offset(void *fdt, int node, |
162 | const char *name, uint32_t delta) |
163 | { |
164 | const fdt32_t *val; |
165 | uint32_t adj_val; |
166 | int len; |
167 | |
168 | val = fdt_getprop(fdt, node, name, &len); |
169 | if (!val) |
170 | return len; |
171 | |
172 | if (len != sizeof(*val)) |
173 | return -FDT_ERR_BADPHANDLE; |
174 | |
175 | adj_val = fdt32_to_cpu(*val); |
176 | if ((adj_val + delta) < adj_val) |
177 | return -FDT_ERR_NOPHANDLES; |
178 | |
179 | adj_val += delta; |
180 | if (adj_val == (uint32_t)-1) |
181 | return -FDT_ERR_NOPHANDLES; |
182 | |
183 | return fdt_setprop_inplace_u32(fdt, node, name, adj_val); |
184 | } |
185 | |
186 | /** |
187 | * overlay_adjust_node_phandles - Offsets the phandles of a node |
188 | * @fdto: Device tree overlay blob |
189 | * @node: Offset of the node we want to adjust |
190 | * @delta: Offset to shift the phandles of |
191 | * |
192 | * overlay_adjust_node_phandles() adds a constant to all the phandles |
193 | * of a given node. This is mainly use as part of the overlay |
194 | * application process, when we want to update all the overlay |
195 | * phandles to not conflict with the overlays of the base device tree. |
196 | * |
197 | * returns: |
198 | * 0 on success |
199 | * Negative error code on failure |
200 | */ |
201 | static int overlay_adjust_node_phandles(void *fdto, int node, |
202 | uint32_t delta) |
203 | { |
204 | int child; |
205 | int ret; |
206 | |
207 | ret = overlay_phandle_add_offset(fdto, node, "phandle" , delta); |
208 | if (ret && ret != -FDT_ERR_NOTFOUND) |
209 | return ret; |
210 | |
211 | ret = overlay_phandle_add_offset(fdto, node, "linux,phandle" , delta); |
212 | if (ret && ret != -FDT_ERR_NOTFOUND) |
213 | return ret; |
214 | |
215 | fdt_for_each_subnode(child, fdto, node) { |
216 | ret = overlay_adjust_node_phandles(fdto, child, delta); |
217 | if (ret) |
218 | return ret; |
219 | } |
220 | |
221 | return 0; |
222 | } |
223 | |
224 | /** |
225 | * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay |
226 | * @fdto: Device tree overlay blob |
227 | * @delta: Offset to shift the phandles of |
228 | * |
229 | * overlay_adjust_local_phandles() adds a constant to all the |
230 | * phandles of an overlay. This is mainly use as part of the overlay |
231 | * application process, when we want to update all the overlay |
232 | * phandles to not conflict with the overlays of the base device tree. |
233 | * |
234 | * returns: |
235 | * 0 on success |
236 | * Negative error code on failure |
237 | */ |
238 | static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) |
239 | { |
240 | /* |
241 | * Start adjusting the phandles from the overlay root |
242 | */ |
243 | return overlay_adjust_node_phandles(fdto, 0, delta); |
244 | } |
245 | |
246 | /** |
247 | * overlay_update_local_node_references - Adjust the overlay references |
248 | * @fdto: Device tree overlay blob |
249 | * @tree_node: Node offset of the node to operate on |
250 | * @fixup_node: Node offset of the matching local fixups node |
251 | * @delta: Offset to shift the phandles of |
252 | * |
253 | * overlay_update_local_nodes_references() update the phandles |
254 | * pointing to a node within the device tree overlay by adding a |
255 | * constant delta. |
256 | * |
257 | * This is mainly used as part of a device tree application process, |
258 | * where you want the device tree overlays phandles to not conflict |
259 | * with the ones from the base device tree before merging them. |
260 | * |
261 | * returns: |
262 | * 0 on success |
263 | * Negative error code on failure |
264 | */ |
265 | static int overlay_update_local_node_references(void *fdto, |
266 | int tree_node, |
267 | int fixup_node, |
268 | uint32_t delta) |
269 | { |
270 | int fixup_prop; |
271 | int fixup_child; |
272 | int ret; |
273 | |
274 | fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { |
275 | const fdt32_t *fixup_val; |
276 | const char *tree_val; |
277 | const char *name; |
278 | int fixup_len; |
279 | int tree_len; |
280 | int i; |
281 | |
282 | fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, |
283 | &name, &fixup_len); |
284 | if (!fixup_val) |
285 | return fixup_len; |
286 | |
287 | if (fixup_len % sizeof(uint32_t)) |
288 | return -FDT_ERR_BADOVERLAY; |
289 | |
290 | tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); |
291 | if (!tree_val) { |
292 | if (tree_len == -FDT_ERR_NOTFOUND) |
293 | return -FDT_ERR_BADOVERLAY; |
294 | |
295 | return tree_len; |
296 | } |
297 | |
298 | for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) { |
299 | fdt32_t adj_val; |
300 | uint32_t poffset; |
301 | |
302 | poffset = fdt32_to_cpu(fixup_val[i]); |
303 | |
304 | /* |
305 | * phandles to fixup can be unaligned. |
306 | * |
307 | * Use a memcpy for the architectures that do |
308 | * not support unaligned accesses. |
309 | */ |
310 | memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); |
311 | |
312 | adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); |
313 | |
314 | ret = fdt_setprop_inplace_namelen_partial(fdto, |
315 | tree_node, |
316 | name, |
317 | strlen(name), |
318 | poffset, |
319 | &adj_val, |
320 | sizeof(adj_val)); |
321 | if (ret == -FDT_ERR_NOSPACE) |
322 | return -FDT_ERR_BADOVERLAY; |
323 | |
324 | if (ret) |
325 | return ret; |
326 | } |
327 | } |
328 | |
329 | fdt_for_each_subnode(fixup_child, fdto, fixup_node) { |
330 | const char *fixup_child_name = fdt_get_name(fdto, fixup_child, |
331 | NULL); |
332 | int tree_child; |
333 | |
334 | tree_child = fdt_subnode_offset(fdto, tree_node, |
335 | fixup_child_name); |
336 | if (tree_child == -FDT_ERR_NOTFOUND) |
337 | return -FDT_ERR_BADOVERLAY; |
338 | if (tree_child < 0) |
339 | return tree_child; |
340 | |
341 | ret = overlay_update_local_node_references(fdto, |
342 | tree_child, |
343 | fixup_child, |
344 | delta); |
345 | if (ret) |
346 | return ret; |
347 | } |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | /** |
353 | * overlay_update_local_references - Adjust the overlay references |
354 | * @fdto: Device tree overlay blob |
355 | * @delta: Offset to shift the phandles of |
356 | * |
357 | * overlay_update_local_references() update all the phandles pointing |
358 | * to a node within the device tree overlay by adding a constant |
359 | * delta to not conflict with the base overlay. |
360 | * |
361 | * This is mainly used as part of a device tree application process, |
362 | * where you want the device tree overlays phandles to not conflict |
363 | * with the ones from the base device tree before merging them. |
364 | * |
365 | * returns: |
366 | * 0 on success |
367 | * Negative error code on failure |
368 | */ |
369 | static int overlay_update_local_references(void *fdto, uint32_t delta) |
370 | { |
371 | int fixups; |
372 | |
373 | fixups = fdt_path_offset(fdto, "/__local_fixups__" ); |
374 | if (fixups < 0) { |
375 | /* There's no local phandles to adjust, bail out */ |
376 | if (fixups == -FDT_ERR_NOTFOUND) |
377 | return 0; |
378 | |
379 | return fixups; |
380 | } |
381 | |
382 | /* |
383 | * Update our local references from the root of the tree |
384 | */ |
385 | return overlay_update_local_node_references(fdto, 0, fixups, |
386 | delta); |
387 | } |
388 | |
389 | /** |
390 | * overlay_fixup_one_phandle - Set an overlay phandle to the base one |
391 | * @fdt: Base Device Tree blob |
392 | * @fdto: Device tree overlay blob |
393 | * @symbols_off: Node offset of the symbols node in the base device tree |
394 | * @path: Path to a node holding a phandle in the overlay |
395 | * @path_len: number of path characters to consider |
396 | * @name: Name of the property holding the phandle reference in the overlay |
397 | * @name_len: number of name characters to consider |
398 | * @poffset: Offset within the overlay property where the phandle is stored |
399 | * @label: Label of the node referenced by the phandle |
400 | * |
401 | * overlay_fixup_one_phandle() resolves an overlay phandle pointing to |
402 | * a node 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_one_phandle(void *fdt, void *fdto, |
413 | int symbols_off, |
414 | const char *path, uint32_t path_len, |
415 | const char *name, uint32_t name_len, |
416 | int poffset, const char *label) |
417 | { |
418 | const char *symbol_path; |
419 | uint32_t phandle; |
420 | fdt32_t phandle_prop; |
421 | int symbol_off, fixup_off; |
422 | int prop_len; |
423 | |
424 | if (symbols_off < 0) |
425 | return symbols_off; |
426 | |
427 | symbol_path = fdt_getprop(fdt, symbols_off, label, |
428 | &prop_len); |
429 | if (!symbol_path) |
430 | return prop_len; |
431 | |
432 | symbol_off = fdt_path_offset(fdt, symbol_path); |
433 | if (symbol_off < 0) |
434 | return symbol_off; |
435 | |
436 | phandle = fdt_get_phandle(fdt, symbol_off); |
437 | if (!phandle) |
438 | return -FDT_ERR_NOTFOUND; |
439 | |
440 | fixup_off = fdt_path_offset_namelen(fdto, path, path_len); |
441 | if (fixup_off == -FDT_ERR_NOTFOUND) |
442 | return -FDT_ERR_BADOVERLAY; |
443 | if (fixup_off < 0) |
444 | return fixup_off; |
445 | |
446 | phandle_prop = cpu_to_fdt32(phandle); |
447 | return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, |
448 | name, name_len, poffset, |
449 | &phandle_prop, |
450 | sizeof(phandle_prop)); |
451 | }; |
452 | |
453 | /** |
454 | * overlay_fixup_phandle - Set an overlay phandle to the base one |
455 | * @fdt: Base Device Tree blob |
456 | * @fdto: Device tree overlay blob |
457 | * @symbols_off: Node offset of the symbols node in the base device tree |
458 | * @property: Property offset in the overlay holding the list of fixups |
459 | * |
460 | * overlay_fixup_phandle() resolves all the overlay phandles pointed |
461 | * to in a __fixups__ property, and updates them to match the phandles |
462 | * in use in the base device tree. |
463 | * |
464 | * This is part of the device tree overlay application process, when |
465 | * you want all the phandles in the overlay to point to the actual |
466 | * base dt nodes. |
467 | * |
468 | * returns: |
469 | * 0 on success |
470 | * Negative error code on failure |
471 | */ |
472 | static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, |
473 | int property) |
474 | { |
475 | const char *value; |
476 | const char *label; |
477 | int len; |
478 | |
479 | value = fdt_getprop_by_offset(fdto, property, |
480 | &label, &len); |
481 | if (!value) { |
482 | if (len == -FDT_ERR_NOTFOUND) |
483 | return -FDT_ERR_INTERNAL; |
484 | |
485 | return len; |
486 | } |
487 | |
488 | do { |
489 | const char *path, *name, *fixup_end; |
490 | const char *fixup_str = value; |
491 | uint32_t path_len, name_len; |
492 | uint32_t fixup_len; |
493 | char *sep, *endptr; |
494 | int poffset, ret; |
495 | |
496 | fixup_end = memchr(value, '\0', len); |
497 | if (!fixup_end) |
498 | return -FDT_ERR_BADOVERLAY; |
499 | fixup_len = fixup_end - fixup_str; |
500 | |
501 | len -= fixup_len + 1; |
502 | value += fixup_len + 1; |
503 | |
504 | path = fixup_str; |
505 | sep = memchr(fixup_str, ':', fixup_len); |
506 | if (!sep || *sep != ':') |
507 | return -FDT_ERR_BADOVERLAY; |
508 | |
509 | path_len = sep - path; |
510 | if (path_len == (fixup_len - 1)) |
511 | return -FDT_ERR_BADOVERLAY; |
512 | |
513 | fixup_len -= path_len + 1; |
514 | name = sep + 1; |
515 | sep = memchr(name, ':', fixup_len); |
516 | if (!sep || *sep != ':') |
517 | return -FDT_ERR_BADOVERLAY; |
518 | |
519 | name_len = sep - name; |
520 | if (!name_len) |
521 | return -FDT_ERR_BADOVERLAY; |
522 | |
523 | poffset = strtoul(sep + 1, &endptr, 10); |
524 | if ((*endptr != '\0') || (endptr <= (sep + 1))) |
525 | return -FDT_ERR_BADOVERLAY; |
526 | |
527 | ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, |
528 | path, path_len, name, name_len, |
529 | poffset, label); |
530 | if (ret) |
531 | return ret; |
532 | } while (len > 0); |
533 | |
534 | return 0; |
535 | } |
536 | |
537 | /** |
538 | * overlay_fixup_phandles - Resolve the overlay phandles to the base |
539 | * device tree |
540 | * @fdt: Base Device Tree blob |
541 | * @fdto: Device tree overlay blob |
542 | * |
543 | * overlay_fixup_phandles() resolves all the overlay phandles pointing |
544 | * to nodes in the base device tree. |
545 | * |
546 | * This is one of the steps of the device tree overlay application |
547 | * process, when you want all the phandles in the overlay to point to |
548 | * the actual base dt nodes. |
549 | * |
550 | * returns: |
551 | * 0 on success |
552 | * Negative error code on failure |
553 | */ |
554 | static int overlay_fixup_phandles(void *fdt, void *fdto) |
555 | { |
556 | int fixups_off, symbols_off; |
557 | int property; |
558 | |
559 | /* We can have overlays without any fixups */ |
560 | fixups_off = fdt_path_offset(fdto, "/__fixups__" ); |
561 | if (fixups_off == -FDT_ERR_NOTFOUND) |
562 | return 0; /* nothing to do */ |
563 | if (fixups_off < 0) |
564 | return fixups_off; |
565 | |
566 | /* And base DTs without symbols */ |
567 | symbols_off = fdt_path_offset(fdt, "/__symbols__" ); |
568 | if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) |
569 | return symbols_off; |
570 | |
571 | fdt_for_each_property_offset(property, fdto, fixups_off) { |
572 | int ret; |
573 | |
574 | ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); |
575 | if (ret) |
576 | return ret; |
577 | } |
578 | |
579 | return 0; |
580 | } |
581 | |
582 | /** |
583 | * overlay_apply_node - Merges a node into the base device tree |
584 | * @fdt: Base Device Tree blob |
585 | * @target: Node offset in the base device tree to apply the fragment to |
586 | * @fdto: Device tree overlay blob |
587 | * @node: Node offset in the overlay holding the changes to merge |
588 | * |
589 | * overlay_apply_node() merges a node into a target base device tree |
590 | * node pointed. |
591 | * |
592 | * This is part of the final step in the device tree overlay |
593 | * application process, when all the phandles have been adjusted and |
594 | * resolved and you just have to merge overlay into the base device |
595 | * tree. |
596 | * |
597 | * returns: |
598 | * 0 on success |
599 | * Negative error code on failure |
600 | */ |
601 | static int overlay_apply_node(void *fdt, int target, |
602 | void *fdto, int node) |
603 | { |
604 | int property; |
605 | int subnode; |
606 | |
607 | fdt_for_each_property_offset(property, fdto, node) { |
608 | const char *name; |
609 | const void *prop; |
610 | int prop_len; |
611 | int ret; |
612 | |
613 | prop = fdt_getprop_by_offset(fdto, property, &name, |
614 | &prop_len); |
615 | if (prop_len == -FDT_ERR_NOTFOUND) |
616 | return -FDT_ERR_INTERNAL; |
617 | if (prop_len < 0) |
618 | return prop_len; |
619 | |
620 | ret = fdt_setprop(fdt, target, name, prop, prop_len); |
621 | if (ret) |
622 | return ret; |
623 | } |
624 | |
625 | fdt_for_each_subnode(subnode, fdto, node) { |
626 | const char *name = fdt_get_name(fdto, subnode, NULL); |
627 | int nnode; |
628 | int ret; |
629 | |
630 | nnode = fdt_add_subnode(fdt, target, name); |
631 | if (nnode == -FDT_ERR_EXISTS) { |
632 | nnode = fdt_subnode_offset(fdt, target, name); |
633 | if (nnode == -FDT_ERR_NOTFOUND) |
634 | return -FDT_ERR_INTERNAL; |
635 | } |
636 | |
637 | if (nnode < 0) |
638 | return nnode; |
639 | |
640 | ret = overlay_apply_node(fdt, nnode, fdto, subnode); |
641 | if (ret) |
642 | return ret; |
643 | } |
644 | |
645 | return 0; |
646 | } |
647 | |
648 | /** |
649 | * overlay_merge - Merge an overlay into its base device tree |
650 | * @fdt: Base Device Tree blob |
651 | * @fdto: Device tree overlay blob |
652 | * |
653 | * overlay_merge() merges an overlay into its base device tree. |
654 | * |
655 | * This is the next to last step in the device tree overlay application |
656 | * process, when all the phandles have been adjusted and resolved and |
657 | * you just have to merge overlay into the base device tree. |
658 | * |
659 | * returns: |
660 | * 0 on success |
661 | * Negative error code on failure |
662 | */ |
663 | static int overlay_merge(void *fdt, void *fdto) |
664 | { |
665 | int fragment; |
666 | |
667 | fdt_for_each_subnode(fragment, fdto, 0) { |
668 | int overlay; |
669 | int target; |
670 | int ret; |
671 | |
672 | /* |
673 | * Each fragments will have an __overlay__ node. If |
674 | * they don't, it's not supposed to be merged |
675 | */ |
676 | overlay = fdt_subnode_offset(fdto, fragment, "__overlay__" ); |
677 | if (overlay == -FDT_ERR_NOTFOUND) |
678 | continue; |
679 | |
680 | if (overlay < 0) |
681 | return overlay; |
682 | |
683 | target = overlay_get_target(fdt, fdto, fragment, NULL); |
684 | if (target < 0) |
685 | return target; |
686 | |
687 | ret = overlay_apply_node(fdt, target, fdto, overlay); |
688 | if (ret) |
689 | return ret; |
690 | } |
691 | |
692 | return 0; |
693 | } |
694 | |
695 | static int get_path_len(const void *fdt, int nodeoffset) |
696 | { |
697 | int len = 0, namelen; |
698 | const char *name; |
699 | |
700 | FDT_RO_PROBE(fdt); |
701 | |
702 | for (;;) { |
703 | name = fdt_get_name(fdt, nodeoffset, &namelen); |
704 | if (!name) |
705 | return namelen; |
706 | |
707 | /* root? we're done */ |
708 | if (namelen == 0) |
709 | break; |
710 | |
711 | nodeoffset = fdt_parent_offset(fdt, nodeoffset); |
712 | if (nodeoffset < 0) |
713 | return nodeoffset; |
714 | len += namelen + 1; |
715 | } |
716 | |
717 | /* in case of root pretend it's "/" */ |
718 | if (len == 0) |
719 | len++; |
720 | return len; |
721 | } |
722 | |
723 | /** |
724 | * overlay_symbol_update - Update the symbols of base tree after a merge |
725 | * @fdt: Base Device Tree blob |
726 | * @fdto: Device tree overlay blob |
727 | * |
728 | * overlay_symbol_update() updates the symbols of the base tree with the |
729 | * symbols of the applied overlay |
730 | * |
731 | * This is the last step in the device tree overlay application |
732 | * process, allowing the reference of overlay symbols by subsequent |
733 | * overlay operations. |
734 | * |
735 | * returns: |
736 | * 0 on success |
737 | * Negative error code on failure |
738 | */ |
739 | static int overlay_symbol_update(void *fdt, void *fdto) |
740 | { |
741 | int root_sym, ov_sym, prop, path_len, fragment, target; |
742 | int len, frag_name_len, ret, rel_path_len; |
743 | const char *s, *e; |
744 | const char *path; |
745 | const char *name; |
746 | const char *frag_name; |
747 | const char *rel_path; |
748 | const char *target_path; |
749 | char *buf; |
750 | void *p; |
751 | |
752 | ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__" ); |
753 | |
754 | /* if no overlay symbols exist no problem */ |
755 | if (ov_sym < 0) |
756 | return 0; |
757 | |
758 | root_sym = fdt_subnode_offset(fdt, 0, "__symbols__" ); |
759 | |
760 | /* it no root symbols exist we should create them */ |
761 | if (root_sym == -FDT_ERR_NOTFOUND) |
762 | root_sym = fdt_add_subnode(fdt, 0, "__symbols__" ); |
763 | |
764 | /* any error is fatal now */ |
765 | if (root_sym < 0) |
766 | return root_sym; |
767 | |
768 | /* iterate over each overlay symbol */ |
769 | fdt_for_each_property_offset(prop, fdto, ov_sym) { |
770 | path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); |
771 | if (!path) |
772 | return path_len; |
773 | |
774 | /* verify it's a string property (terminated by a single \0) */ |
775 | if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) |
776 | return -FDT_ERR_BADVALUE; |
777 | |
778 | /* keep end marker to avoid strlen() */ |
779 | e = path + path_len; |
780 | |
781 | /* format: /<fragment-name>/__overlay__/<relative-subnode-path> */ |
782 | |
783 | if (*path != '/') |
784 | return -FDT_ERR_BADVALUE; |
785 | |
786 | /* get fragment name first */ |
787 | s = strchr(path + 1, '/'); |
788 | if (!s) |
789 | return -FDT_ERR_BADOVERLAY; |
790 | |
791 | frag_name = path + 1; |
792 | frag_name_len = s - path - 1; |
793 | |
794 | /* verify format; safe since "s" lies in \0 terminated prop */ |
795 | len = sizeof("/__overlay__/" ) - 1; |
796 | if ((e - s) < len || memcmp(s, "/__overlay__/" , len)) |
797 | return -FDT_ERR_BADOVERLAY; |
798 | |
799 | rel_path = s + len; |
800 | rel_path_len = e - rel_path; |
801 | |
802 | /* find the fragment index in which the symbol lies */ |
803 | ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, |
804 | frag_name_len); |
805 | /* not found? */ |
806 | if (ret < 0) |
807 | return -FDT_ERR_BADOVERLAY; |
808 | fragment = ret; |
809 | |
810 | /* an __overlay__ subnode must exist */ |
811 | ret = fdt_subnode_offset(fdto, fragment, "__overlay__" ); |
812 | if (ret < 0) |
813 | return -FDT_ERR_BADOVERLAY; |
814 | |
815 | /* get the target of the fragment */ |
816 | ret = overlay_get_target(fdt, fdto, fragment, &target_path); |
817 | if (ret < 0) |
818 | return ret; |
819 | target = ret; |
820 | |
821 | /* if we have a target path use */ |
822 | if (!target_path) { |
823 | ret = get_path_len(fdt, target); |
824 | if (ret < 0) |
825 | return ret; |
826 | len = ret; |
827 | } else { |
828 | len = strlen(target_path); |
829 | } |
830 | |
831 | ret = fdt_setprop_placeholder(fdt, root_sym, name, |
832 | len + (len > 1) + rel_path_len + 1, &p); |
833 | if (ret < 0) |
834 | return ret; |
835 | |
836 | if (!target_path) { |
837 | /* again in case setprop_placeholder changed it */ |
838 | ret = overlay_get_target(fdt, fdto, fragment, &target_path); |
839 | if (ret < 0) |
840 | return ret; |
841 | target = ret; |
842 | } |
843 | |
844 | buf = p; |
845 | if (len > 1) { /* target is not root */ |
846 | if (!target_path) { |
847 | ret = fdt_get_path(fdt, target, buf, len + 1); |
848 | if (ret < 0) |
849 | return ret; |
850 | } else |
851 | memcpy(buf, target_path, len + 1); |
852 | |
853 | } else |
854 | len--; |
855 | |
856 | buf[len] = '/'; |
857 | memcpy(buf + len + 1, rel_path, rel_path_len); |
858 | buf[len + 1 + rel_path_len] = '\0'; |
859 | } |
860 | |
861 | return 0; |
862 | } |
863 | |
864 | int fdt_overlay_apply(void *fdt, void *fdto) |
865 | { |
866 | uint32_t delta = fdt_get_max_phandle(fdt); |
867 | int ret; |
868 | |
869 | FDT_RO_PROBE(fdt); |
870 | FDT_RO_PROBE(fdto); |
871 | |
872 | ret = overlay_adjust_local_phandles(fdto, delta); |
873 | if (ret) |
874 | goto err; |
875 | |
876 | ret = overlay_update_local_references(fdto, delta); |
877 | if (ret) |
878 | goto err; |
879 | |
880 | ret = overlay_fixup_phandles(fdt, fdto); |
881 | if (ret) |
882 | goto err; |
883 | |
884 | ret = overlay_merge(fdt, fdto); |
885 | if (ret) |
886 | goto err; |
887 | |
888 | ret = overlay_symbol_update(fdt, fdto); |
889 | if (ret) |
890 | goto err; |
891 | |
892 | /* |
893 | * The overlay has been damaged, erase its magic. |
894 | */ |
895 | fdt_set_magic(fdto, ~0); |
896 | |
897 | return 0; |
898 | |
899 | err: |
900 | /* |
901 | * The overlay might have been damaged, erase its magic. |
902 | */ |
903 | fdt_set_magic(fdto, ~0); |
904 | |
905 | /* |
906 | * The base device tree might have been damaged, erase its |
907 | * magic. |
908 | */ |
909 | fdt_set_magic(fdt, ~0); |
910 | |
911 | return ret; |
912 | } |
913 | |