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(fdt: fdto, nodeoffset: fragment, name: "target" , lenp: &len); |
34 | if (!val) |
35 | return 0; |
36 | |
37 | if ((len != sizeof(*val)) || (fdt32_to_cpu(x: *val) == (uint32_t)-1)) |
38 | return (uint32_t)-1; |
39 | |
40 | return fdt32_to_cpu(x: *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: 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(fdt: fdto, nodeoffset: fragment_offset, name: "target-path" , lenp: &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 | const fdt32_t *val; |
105 | uint32_t adj_val; |
106 | int len; |
107 | |
108 | val = fdt_getprop(fdt, nodeoffset: node, name, lenp: &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(x: *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, nodeoffset: node, name, val: 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(fdt: fdto, node, name: "phandle" , delta); |
148 | if (ret && ret != -FDT_ERR_NOTFOUND) |
149 | return ret; |
150 | |
151 | ret = overlay_phandle_add_offset(fdt: fdto, node, name: "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, node: 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, node: 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(fdt: fdto, offset: fixup_prop, |
223 | namep: &name, lenp: &fixup_len); |
224 | if (!fixup_val) |
225 | return fixup_len; |
226 | |
227 | if (fixup_len % sizeof(uint32_t)) |
228 | return -FDT_ERR_BADOVERLAY; |
229 | fixup_len /= sizeof(uint32_t); |
230 | |
231 | tree_val = fdt_getprop(fdt: fdto, nodeoffset: tree_node, name, lenp: &tree_len); |
232 | if (!tree_val) { |
233 | if (tree_len == -FDT_ERR_NOTFOUND) |
234 | return -FDT_ERR_BADOVERLAY; |
235 | |
236 | return tree_len; |
237 | } |
238 | |
239 | for (i = 0; i < fixup_len; i++) { |
240 | fdt32_t adj_val; |
241 | uint32_t poffset; |
242 | |
243 | poffset = fdt32_to_cpu(x: fixup_val[i]); |
244 | |
245 | /* |
246 | * phandles to fixup can be unaligned. |
247 | * |
248 | * Use a memcpy for the architectures that do |
249 | * not support unaligned accesses. |
250 | */ |
251 | memcpy(dest: &adj_val, src: tree_val + poffset, n: sizeof(adj_val)); |
252 | |
253 | adj_val = cpu_to_fdt32(x: fdt32_to_cpu(x: adj_val) + delta); |
254 | |
255 | ret = fdt_setprop_inplace_namelen_partial(fdt: fdto, |
256 | nodeoffset: tree_node, |
257 | name, |
258 | namelen: strlen(s: name), |
259 | idx: poffset, |
260 | val: &adj_val, |
261 | len: sizeof(adj_val)); |
262 | if (ret == -FDT_ERR_NOSPACE) |
263 | return -FDT_ERR_BADOVERLAY; |
264 | |
265 | if (ret) |
266 | return ret; |
267 | } |
268 | } |
269 | |
270 | fdt_for_each_subnode(fixup_child, fdto, fixup_node) { |
271 | const char *fixup_child_name = fdt_get_name(fdt: fdto, nodeoffset: fixup_child, |
272 | NULL); |
273 | int tree_child; |
274 | |
275 | tree_child = fdt_subnode_offset(fdt: fdto, parentoffset: tree_node, |
276 | name: fixup_child_name); |
277 | if (tree_child == -FDT_ERR_NOTFOUND) |
278 | return -FDT_ERR_BADOVERLAY; |
279 | if (tree_child < 0) |
280 | return tree_child; |
281 | |
282 | ret = overlay_update_local_node_references(fdto, |
283 | tree_node: tree_child, |
284 | fixup_node: fixup_child, |
285 | delta); |
286 | if (ret) |
287 | return ret; |
288 | } |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | /** |
294 | * overlay_update_local_references - Adjust the overlay references |
295 | * @fdto: Device tree overlay blob |
296 | * @delta: Offset to shift the phandles of |
297 | * |
298 | * overlay_update_local_references() update all the phandles pointing |
299 | * to a node within the device tree overlay by adding a constant |
300 | * delta to not conflict with the base overlay. |
301 | * |
302 | * This is mainly used as part of a device tree application process, |
303 | * where you want the device tree overlays phandles to not conflict |
304 | * with the ones from the base device tree before merging them. |
305 | * |
306 | * returns: |
307 | * 0 on success |
308 | * Negative error code on failure |
309 | */ |
310 | static int overlay_update_local_references(void *fdto, uint32_t delta) |
311 | { |
312 | int fixups; |
313 | |
314 | fixups = fdt_path_offset(fdt: fdto, path: "/__local_fixups__" ); |
315 | if (fixups < 0) { |
316 | /* There's no local phandles to adjust, bail out */ |
317 | if (fixups == -FDT_ERR_NOTFOUND) |
318 | return 0; |
319 | |
320 | return fixups; |
321 | } |
322 | |
323 | /* |
324 | * Update our local references from the root of the tree |
325 | */ |
326 | return overlay_update_local_node_references(fdto, tree_node: 0, fixup_node: fixups, |
327 | delta); |
328 | } |
329 | |
330 | /** |
331 | * overlay_fixup_one_phandle - Set an overlay phandle to the base one |
332 | * @fdt: Base Device Tree blob |
333 | * @fdto: Device tree overlay blob |
334 | * @symbols_off: Node offset of the symbols node in the base device tree |
335 | * @path: Path to a node holding a phandle in the overlay |
336 | * @path_len: number of path characters to consider |
337 | * @name: Name of the property holding the phandle reference in the overlay |
338 | * @name_len: number of name characters to consider |
339 | * @poffset: Offset within the overlay property where the phandle is stored |
340 | * @label: Label of the node referenced by the phandle |
341 | * |
342 | * overlay_fixup_one_phandle() resolves an overlay phandle pointing to |
343 | * a node in the base device tree. |
344 | * |
345 | * This is part of the device tree overlay application process, when |
346 | * you want all the phandles in the overlay to point to the actual |
347 | * base dt nodes. |
348 | * |
349 | * returns: |
350 | * 0 on success |
351 | * Negative error code on failure |
352 | */ |
353 | static int overlay_fixup_one_phandle(void *fdt, void *fdto, |
354 | int symbols_off, |
355 | const char *path, uint32_t path_len, |
356 | const char *name, uint32_t name_len, |
357 | int poffset, const char *label) |
358 | { |
359 | const char *symbol_path; |
360 | uint32_t phandle; |
361 | fdt32_t phandle_prop; |
362 | int symbol_off, fixup_off; |
363 | int prop_len; |
364 | |
365 | if (symbols_off < 0) |
366 | return symbols_off; |
367 | |
368 | symbol_path = fdt_getprop(fdt, nodeoffset: symbols_off, name: label, |
369 | lenp: &prop_len); |
370 | if (!symbol_path) |
371 | return prop_len; |
372 | |
373 | symbol_off = fdt_path_offset(fdt, path: symbol_path); |
374 | if (symbol_off < 0) |
375 | return symbol_off; |
376 | |
377 | phandle = fdt_get_phandle(fdt, nodeoffset: symbol_off); |
378 | if (!phandle) |
379 | return -FDT_ERR_NOTFOUND; |
380 | |
381 | fixup_off = fdt_path_offset_namelen(fdt: fdto, path, namelen: path_len); |
382 | if (fixup_off == -FDT_ERR_NOTFOUND) |
383 | return -FDT_ERR_BADOVERLAY; |
384 | if (fixup_off < 0) |
385 | return fixup_off; |
386 | |
387 | phandle_prop = cpu_to_fdt32(x: phandle); |
388 | return fdt_setprop_inplace_namelen_partial(fdt: fdto, nodeoffset: fixup_off, |
389 | name, namelen: name_len, idx: poffset, |
390 | val: &phandle_prop, |
391 | len: sizeof(phandle_prop)); |
392 | }; |
393 | |
394 | /** |
395 | * overlay_fixup_phandle - Set an overlay phandle to the base one |
396 | * @fdt: Base Device Tree blob |
397 | * @fdto: Device tree overlay blob |
398 | * @symbols_off: Node offset of the symbols node in the base device tree |
399 | * @property: Property offset in the overlay holding the list of fixups |
400 | * |
401 | * overlay_fixup_phandle() resolves all the overlay phandles pointed |
402 | * to in a __fixups__ property, and updates them to match the phandles |
403 | * in use in the base device tree. |
404 | * |
405 | * This is part of the device tree overlay application process, when |
406 | * you want all the phandles in the overlay to point to the actual |
407 | * base dt nodes. |
408 | * |
409 | * returns: |
410 | * 0 on success |
411 | * Negative error code on failure |
412 | */ |
413 | static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, |
414 | int property) |
415 | { |
416 | const char *value; |
417 | const char *label; |
418 | int len; |
419 | |
420 | value = fdt_getprop_by_offset(fdt: fdto, offset: property, |
421 | namep: &label, lenp: &len); |
422 | if (!value) { |
423 | if (len == -FDT_ERR_NOTFOUND) |
424 | return -FDT_ERR_INTERNAL; |
425 | |
426 | return len; |
427 | } |
428 | |
429 | do { |
430 | const char *path, *name, *fixup_end; |
431 | const char *fixup_str = value; |
432 | uint32_t path_len, name_len; |
433 | uint32_t fixup_len; |
434 | char *sep, *endptr; |
435 | int poffset, ret; |
436 | |
437 | fixup_end = memchr(s: value, c: '\0', n: len); |
438 | if (!fixup_end) |
439 | return -FDT_ERR_BADOVERLAY; |
440 | fixup_len = fixup_end - fixup_str; |
441 | |
442 | len -= fixup_len + 1; |
443 | value += fixup_len + 1; |
444 | |
445 | path = fixup_str; |
446 | sep = memchr(s: fixup_str, c: ':', n: fixup_len); |
447 | if (!sep || *sep != ':') |
448 | return -FDT_ERR_BADOVERLAY; |
449 | |
450 | path_len = sep - path; |
451 | if (path_len == (fixup_len - 1)) |
452 | return -FDT_ERR_BADOVERLAY; |
453 | |
454 | fixup_len -= path_len + 1; |
455 | name = sep + 1; |
456 | sep = memchr(s: name, c: ':', n: fixup_len); |
457 | if (!sep || *sep != ':') |
458 | return -FDT_ERR_BADOVERLAY; |
459 | |
460 | name_len = sep - name; |
461 | if (!name_len) |
462 | return -FDT_ERR_BADOVERLAY; |
463 | |
464 | poffset = strtoul(nptr: sep + 1, endptr: &endptr, base: 10); |
465 | if ((*endptr != '\0') || (endptr <= (sep + 1))) |
466 | return -FDT_ERR_BADOVERLAY; |
467 | |
468 | ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, |
469 | path, path_len, name, name_len, |
470 | poffset, label); |
471 | if (ret) |
472 | return ret; |
473 | } while (len > 0); |
474 | |
475 | return 0; |
476 | } |
477 | |
478 | /** |
479 | * overlay_fixup_phandles - Resolve the overlay phandles to the base |
480 | * device tree |
481 | * @fdt: Base Device Tree blob |
482 | * @fdto: Device tree overlay blob |
483 | * |
484 | * overlay_fixup_phandles() resolves all the overlay phandles pointing |
485 | * to nodes in the base device tree. |
486 | * |
487 | * This is one of the steps of the device tree overlay application |
488 | * process, when you want all the phandles in the overlay to point to |
489 | * the actual base dt nodes. |
490 | * |
491 | * returns: |
492 | * 0 on success |
493 | * Negative error code on failure |
494 | */ |
495 | static int overlay_fixup_phandles(void *fdt, void *fdto) |
496 | { |
497 | int fixups_off, symbols_off; |
498 | int property; |
499 | |
500 | /* We can have overlays without any fixups */ |
501 | fixups_off = fdt_path_offset(fdt: fdto, path: "/__fixups__" ); |
502 | if (fixups_off == -FDT_ERR_NOTFOUND) |
503 | return 0; /* nothing to do */ |
504 | if (fixups_off < 0) |
505 | return fixups_off; |
506 | |
507 | /* And base DTs without symbols */ |
508 | symbols_off = fdt_path_offset(fdt, path: "/__symbols__" ); |
509 | if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) |
510 | return symbols_off; |
511 | |
512 | fdt_for_each_property_offset(property, fdto, fixups_off) { |
513 | int ret; |
514 | |
515 | ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); |
516 | if (ret) |
517 | return ret; |
518 | } |
519 | |
520 | return 0; |
521 | } |
522 | |
523 | /** |
524 | * overlay_apply_node - Merges a node into the base device tree |
525 | * @fdt: Base Device Tree blob |
526 | * @target: Node offset in the base device tree to apply the fragment to |
527 | * @fdto: Device tree overlay blob |
528 | * @node: Node offset in the overlay holding the changes to merge |
529 | * |
530 | * overlay_apply_node() merges a node into a target base device tree |
531 | * node pointed. |
532 | * |
533 | * This is part of the final step in the device tree overlay |
534 | * application process, when all the phandles have been adjusted and |
535 | * resolved and you just have to merge overlay into the base device |
536 | * tree. |
537 | * |
538 | * returns: |
539 | * 0 on success |
540 | * Negative error code on failure |
541 | */ |
542 | static int overlay_apply_node(void *fdt, int target, |
543 | void *fdto, int node) |
544 | { |
545 | int property; |
546 | int subnode; |
547 | |
548 | fdt_for_each_property_offset(property, fdto, node) { |
549 | const char *name; |
550 | const void *prop; |
551 | int prop_len; |
552 | int ret; |
553 | |
554 | prop = fdt_getprop_by_offset(fdt: fdto, offset: property, namep: &name, |
555 | lenp: &prop_len); |
556 | if (prop_len == -FDT_ERR_NOTFOUND) |
557 | return -FDT_ERR_INTERNAL; |
558 | if (prop_len < 0) |
559 | return prop_len; |
560 | |
561 | ret = fdt_setprop(fdt, nodeoffset: target, name, val: prop, len: prop_len); |
562 | if (ret) |
563 | return ret; |
564 | } |
565 | |
566 | fdt_for_each_subnode(subnode, fdto, node) { |
567 | const char *name = fdt_get_name(fdt: fdto, nodeoffset: subnode, NULL); |
568 | int nnode; |
569 | int ret; |
570 | |
571 | nnode = fdt_add_subnode(fdt, parentoffset: target, name); |
572 | if (nnode == -FDT_ERR_EXISTS) { |
573 | nnode = fdt_subnode_offset(fdt, parentoffset: target, name); |
574 | if (nnode == -FDT_ERR_NOTFOUND) |
575 | return -FDT_ERR_INTERNAL; |
576 | } |
577 | |
578 | if (nnode < 0) |
579 | return nnode; |
580 | |
581 | ret = overlay_apply_node(fdt, target: nnode, fdto, node: subnode); |
582 | if (ret) |
583 | return ret; |
584 | } |
585 | |
586 | return 0; |
587 | } |
588 | |
589 | /** |
590 | * overlay_merge - Merge an overlay into its base device tree |
591 | * @fdt: Base Device Tree blob |
592 | * @fdto: Device tree overlay blob |
593 | * |
594 | * overlay_merge() merges an overlay into its base device tree. |
595 | * |
596 | * This is the next to last step in the device tree overlay application |
597 | * process, when all the phandles have been adjusted and resolved and |
598 | * you just have to merge overlay into the base device tree. |
599 | * |
600 | * returns: |
601 | * 0 on success |
602 | * Negative error code on failure |
603 | */ |
604 | static int overlay_merge(void *fdt, void *fdto) |
605 | { |
606 | int fragment; |
607 | |
608 | fdt_for_each_subnode(fragment, fdto, 0) { |
609 | int overlay; |
610 | int target; |
611 | int ret; |
612 | |
613 | /* |
614 | * Each fragments will have an __overlay__ node. If |
615 | * they don't, it's not supposed to be merged |
616 | */ |
617 | overlay = fdt_subnode_offset(fdt: fdto, parentoffset: fragment, name: "__overlay__" ); |
618 | if (overlay == -FDT_ERR_NOTFOUND) |
619 | continue; |
620 | |
621 | if (overlay < 0) |
622 | return overlay; |
623 | |
624 | target = fdt_overlay_target_offset(fdt, fdto, fragment_offset: fragment, NULL); |
625 | if (target < 0) |
626 | return target; |
627 | |
628 | ret = overlay_apply_node(fdt, target, fdto, node: overlay); |
629 | if (ret) |
630 | return ret; |
631 | } |
632 | |
633 | return 0; |
634 | } |
635 | |
636 | static int get_path_len(const void *fdt, int nodeoffset) |
637 | { |
638 | int len = 0, namelen; |
639 | const char *name; |
640 | |
641 | FDT_RO_PROBE(fdt); |
642 | |
643 | for (;;) { |
644 | name = fdt_get_name(fdt, nodeoffset, lenp: &namelen); |
645 | if (!name) |
646 | return namelen; |
647 | |
648 | /* root? we're done */ |
649 | if (namelen == 0) |
650 | break; |
651 | |
652 | nodeoffset = fdt_parent_offset(fdt, nodeoffset); |
653 | if (nodeoffset < 0) |
654 | return nodeoffset; |
655 | len += namelen + 1; |
656 | } |
657 | |
658 | /* in case of root pretend it's "/" */ |
659 | if (len == 0) |
660 | len++; |
661 | return len; |
662 | } |
663 | |
664 | /** |
665 | * overlay_symbol_update - Update the symbols of base tree after a merge |
666 | * @fdt: Base Device Tree blob |
667 | * @fdto: Device tree overlay blob |
668 | * |
669 | * overlay_symbol_update() updates the symbols of the base tree with the |
670 | * symbols of the applied overlay |
671 | * |
672 | * This is the last step in the device tree overlay application |
673 | * process, allowing the reference of overlay symbols by subsequent |
674 | * overlay operations. |
675 | * |
676 | * returns: |
677 | * 0 on success |
678 | * Negative error code on failure |
679 | */ |
680 | static int overlay_symbol_update(void *fdt, void *fdto) |
681 | { |
682 | int root_sym, ov_sym, prop, path_len, fragment, target; |
683 | int len, frag_name_len, ret, rel_path_len; |
684 | const char *s, *e; |
685 | const char *path; |
686 | const char *name; |
687 | const char *frag_name; |
688 | const char *rel_path; |
689 | const char *target_path; |
690 | char *buf; |
691 | void *p; |
692 | |
693 | ov_sym = fdt_subnode_offset(fdt: fdto, parentoffset: 0, name: "__symbols__" ); |
694 | |
695 | /* if no overlay symbols exist no problem */ |
696 | if (ov_sym < 0) |
697 | return 0; |
698 | |
699 | root_sym = fdt_subnode_offset(fdt, parentoffset: 0, name: "__symbols__" ); |
700 | |
701 | /* it no root symbols exist we should create them */ |
702 | if (root_sym == -FDT_ERR_NOTFOUND) |
703 | root_sym = fdt_add_subnode(fdt, parentoffset: 0, name: "__symbols__" ); |
704 | |
705 | /* any error is fatal now */ |
706 | if (root_sym < 0) |
707 | return root_sym; |
708 | |
709 | /* iterate over each overlay symbol */ |
710 | fdt_for_each_property_offset(prop, fdto, ov_sym) { |
711 | path = fdt_getprop_by_offset(fdt: fdto, offset: prop, namep: &name, lenp: &path_len); |
712 | if (!path) |
713 | return path_len; |
714 | |
715 | /* verify it's a string property (terminated by a single \0) */ |
716 | if (path_len < 1 || memchr(s: path, c: '\0', n: path_len) != &path[path_len - 1]) |
717 | return -FDT_ERR_BADVALUE; |
718 | |
719 | /* keep end marker to avoid strlen() */ |
720 | e = path + path_len; |
721 | |
722 | if (*path != '/') |
723 | return -FDT_ERR_BADVALUE; |
724 | |
725 | /* get fragment name first */ |
726 | s = strchr(s: path + 1, c: '/'); |
727 | if (!s) { |
728 | /* Symbol refers to something that won't end |
729 | * up in the target tree */ |
730 | continue; |
731 | } |
732 | |
733 | frag_name = path + 1; |
734 | frag_name_len = s - path - 1; |
735 | |
736 | /* verify format; safe since "s" lies in \0 terminated prop */ |
737 | len = sizeof("/__overlay__/" ) - 1; |
738 | if ((e - s) > len && (memcmp(s1: s, s2: "/__overlay__/" , n: len) == 0)) { |
739 | /* /<fragment-name>/__overlay__/<relative-subnode-path> */ |
740 | rel_path = s + len; |
741 | rel_path_len = e - rel_path - 1; |
742 | } else if ((e - s) == len |
743 | && (memcmp(s1: s, s2: "/__overlay__" , n: len - 1) == 0)) { |
744 | /* /<fragment-name>/__overlay__ */ |
745 | rel_path = "" ; |
746 | rel_path_len = 0; |
747 | } else { |
748 | /* Symbol refers to something that won't end |
749 | * up in the target tree */ |
750 | continue; |
751 | } |
752 | |
753 | /* find the fragment index in which the symbol lies */ |
754 | ret = fdt_subnode_offset_namelen(fdt: fdto, parentoffset: 0, name: frag_name, |
755 | namelen: frag_name_len); |
756 | /* not found? */ |
757 | if (ret < 0) |
758 | return -FDT_ERR_BADOVERLAY; |
759 | fragment = ret; |
760 | |
761 | /* an __overlay__ subnode must exist */ |
762 | ret = fdt_subnode_offset(fdt: fdto, parentoffset: fragment, name: "__overlay__" ); |
763 | if (ret < 0) |
764 | return -FDT_ERR_BADOVERLAY; |
765 | |
766 | /* get the target of the fragment */ |
767 | ret = fdt_overlay_target_offset(fdt, fdto, fragment_offset: fragment, pathp: &target_path); |
768 | if (ret < 0) |
769 | return ret; |
770 | target = ret; |
771 | |
772 | /* if we have a target path use */ |
773 | if (!target_path) { |
774 | ret = get_path_len(fdt, nodeoffset: target); |
775 | if (ret < 0) |
776 | return ret; |
777 | len = ret; |
778 | } else { |
779 | len = strlen(s: target_path); |
780 | } |
781 | |
782 | ret = fdt_setprop_placeholder(fdt, nodeoffset: root_sym, name, |
783 | len: len + (len > 1) + rel_path_len + 1, prop_data: &p); |
784 | if (ret < 0) |
785 | return ret; |
786 | |
787 | if (!target_path) { |
788 | /* again in case setprop_placeholder changed it */ |
789 | ret = fdt_overlay_target_offset(fdt, fdto, fragment_offset: fragment, pathp: &target_path); |
790 | if (ret < 0) |
791 | return ret; |
792 | target = ret; |
793 | } |
794 | |
795 | buf = p; |
796 | if (len > 1) { /* target is not root */ |
797 | if (!target_path) { |
798 | ret = fdt_get_path(fdt, nodeoffset: target, buf, buflen: len + 1); |
799 | if (ret < 0) |
800 | return ret; |
801 | } else |
802 | memcpy(dest: buf, src: target_path, n: len + 1); |
803 | |
804 | } else |
805 | len--; |
806 | |
807 | buf[len] = '/'; |
808 | memcpy(dest: buf + len + 1, src: rel_path, n: rel_path_len); |
809 | buf[len + 1 + rel_path_len] = '\0'; |
810 | } |
811 | |
812 | return 0; |
813 | } |
814 | |
815 | int fdt_overlay_apply(void *fdt, void *fdto) |
816 | { |
817 | uint32_t delta; |
818 | int ret; |
819 | |
820 | FDT_RO_PROBE(fdt); |
821 | FDT_RO_PROBE(fdto); |
822 | |
823 | ret = fdt_find_max_phandle(fdt, phandle: &delta); |
824 | if (ret) |
825 | goto err; |
826 | |
827 | ret = overlay_adjust_local_phandles(fdto, delta); |
828 | if (ret) |
829 | goto err; |
830 | |
831 | ret = overlay_update_local_references(fdto, delta); |
832 | if (ret) |
833 | goto err; |
834 | |
835 | ret = overlay_fixup_phandles(fdt, fdto); |
836 | if (ret) |
837 | goto err; |
838 | |
839 | ret = overlay_merge(fdt, fdto); |
840 | if (ret) |
841 | goto err; |
842 | |
843 | ret = overlay_symbol_update(fdt, fdto); |
844 | if (ret) |
845 | goto err; |
846 | |
847 | /* |
848 | * The overlay has been damaged, erase its magic. |
849 | */ |
850 | fdt_set_magic(fdt: fdto, val: ~0); |
851 | |
852 | return 0; |
853 | |
854 | err: |
855 | /* |
856 | * The overlay might have been damaged, erase its magic. |
857 | */ |
858 | fdt_set_magic(fdt: fdto, val: ~0); |
859 | |
860 | /* |
861 | * The base device tree might have been damaged, erase its |
862 | * magic. |
863 | */ |
864 | fdt_set_magic(fdt, val: ~0); |
865 | |
866 | return ret; |
867 | } |
868 | |