]>
Commit | Line | Data |
---|---|---|
44e6186e AB |
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 | unsigned long strtoul(const char *nptr, char **endptr, int base); | |
454 | ||
455 | /** | |
456 | * overlay_fixup_phandle - Set an overlay phandle to the base one | |
457 | * @fdt: Base Device Tree blob | |
458 | * @fdto: Device tree overlay blob | |
459 | * @symbols_off: Node offset of the symbols node in the base device tree | |
460 | * @property: Property offset in the overlay holding the list of fixups | |
461 | * | |
462 | * overlay_fixup_phandle() resolves all the overlay phandles pointed | |
463 | * to in a __fixups__ property, and updates them to match the phandles | |
464 | * in use in the base device tree. | |
465 | * | |
466 | * This is part of the device tree overlay application process, when | |
467 | * you want all the phandles in the overlay to point to the actual | |
468 | * base dt nodes. | |
469 | * | |
470 | * returns: | |
471 | * 0 on success | |
472 | * Negative error code on failure | |
473 | */ | |
474 | static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, | |
475 | int property) | |
476 | { | |
477 | const char *value; | |
478 | const char *label; | |
479 | int len; | |
480 | ||
481 | value = fdt_getprop_by_offset(fdto, property, | |
482 | &label, &len); | |
483 | if (!value) { | |
484 | if (len == -FDT_ERR_NOTFOUND) | |
485 | return -FDT_ERR_INTERNAL; | |
486 | ||
487 | return len; | |
488 | } | |
489 | ||
490 | do { | |
491 | const char *path, *name, *fixup_end; | |
492 | const char *fixup_str = value; | |
493 | uint32_t path_len, name_len; | |
494 | uint32_t fixup_len; | |
495 | char *sep, *endptr; | |
496 | int poffset, ret; | |
497 | ||
498 | fixup_end = memchr(value, '\0', len); | |
499 | if (!fixup_end) | |
500 | return -FDT_ERR_BADOVERLAY; | |
501 | fixup_len = fixup_end - fixup_str; | |
502 | ||
503 | len -= fixup_len + 1; | |
504 | value += fixup_len + 1; | |
505 | ||
506 | path = fixup_str; | |
507 | sep = memchr(fixup_str, ':', fixup_len); | |
508 | if (!sep || *sep != ':') | |
509 | return -FDT_ERR_BADOVERLAY; | |
510 | ||
511 | path_len = sep - path; | |
512 | if (path_len == (fixup_len - 1)) | |
513 | return -FDT_ERR_BADOVERLAY; | |
514 | ||
515 | fixup_len -= path_len + 1; | |
516 | name = sep + 1; | |
517 | sep = memchr(name, ':', fixup_len); | |
518 | if (!sep || *sep != ':') | |
519 | return -FDT_ERR_BADOVERLAY; | |
520 | ||
521 | name_len = sep - name; | |
522 | if (!name_len) | |
523 | return -FDT_ERR_BADOVERLAY; | |
524 | ||
525 | poffset = strtoul(sep + 1, &endptr, 10); | |
526 | if ((*endptr != '\0') || (endptr <= (sep + 1))) | |
527 | return -FDT_ERR_BADOVERLAY; | |
528 | ||
529 | ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, | |
530 | path, path_len, name, name_len, | |
531 | poffset, label); | |
532 | if (ret) | |
533 | return ret; | |
534 | } while (len > 0); | |
535 | ||
536 | return 0; | |
537 | } | |
538 | ||
539 | /** | |
540 | * overlay_fixup_phandles - Resolve the overlay phandles to the base | |
541 | * device tree | |
542 | * @fdt: Base Device Tree blob | |
543 | * @fdto: Device tree overlay blob | |
544 | * | |
545 | * overlay_fixup_phandles() resolves all the overlay phandles pointing | |
546 | * to nodes in the base device tree. | |
547 | * | |
548 | * This is one of the steps of the device tree overlay application | |
549 | * process, when you want all the phandles in the overlay to point to | |
550 | * the actual base dt nodes. | |
551 | * | |
552 | * returns: | |
553 | * 0 on success | |
554 | * Negative error code on failure | |
555 | */ | |
556 | static int overlay_fixup_phandles(void *fdt, void *fdto) | |
557 | { | |
558 | int fixups_off, symbols_off; | |
559 | int property; | |
560 | ||
561 | /* We can have overlays without any fixups */ | |
562 | fixups_off = fdt_path_offset(fdto, "/__fixups__"); | |
563 | if (fixups_off == -FDT_ERR_NOTFOUND) | |
564 | return 0; /* nothing to do */ | |
565 | if (fixups_off < 0) | |
566 | return fixups_off; | |
567 | ||
568 | /* And base DTs without symbols */ | |
569 | symbols_off = fdt_path_offset(fdt, "/__symbols__"); | |
570 | if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) | |
571 | return symbols_off; | |
572 | ||
573 | fdt_for_each_property_offset(property, fdto, fixups_off) { | |
574 | int ret; | |
575 | ||
576 | ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); | |
577 | if (ret) | |
578 | return ret; | |
579 | } | |
580 | ||
581 | return 0; | |
582 | } | |
583 | ||
584 | /** | |
585 | * overlay_apply_node - Merges a node into the base device tree | |
586 | * @fdt: Base Device Tree blob | |
587 | * @target: Node offset in the base device tree to apply the fragment to | |
588 | * @fdto: Device tree overlay blob | |
589 | * @node: Node offset in the overlay holding the changes to merge | |
590 | * | |
591 | * overlay_apply_node() merges a node into a target base device tree | |
592 | * node pointed. | |
593 | * | |
594 | * This is part of the final step in the device tree overlay | |
595 | * application process, when all the phandles have been adjusted and | |
596 | * resolved and you just have to merge overlay into the base device | |
597 | * tree. | |
598 | * | |
599 | * returns: | |
600 | * 0 on success | |
601 | * Negative error code on failure | |
602 | */ | |
603 | static int overlay_apply_node(void *fdt, int target, | |
604 | void *fdto, int node) | |
605 | { | |
606 | int property; | |
607 | int subnode; | |
608 | ||
609 | fdt_for_each_property_offset(property, fdto, node) { | |
610 | const char *name; | |
611 | const void *prop; | |
612 | int prop_len; | |
613 | int ret; | |
614 | ||
615 | prop = fdt_getprop_by_offset(fdto, property, &name, | |
616 | &prop_len); | |
617 | if (prop_len == -FDT_ERR_NOTFOUND) | |
618 | return -FDT_ERR_INTERNAL; | |
619 | if (prop_len < 0) | |
620 | return prop_len; | |
621 | ||
622 | ret = fdt_setprop(fdt, target, name, prop, prop_len); | |
623 | if (ret) | |
624 | return ret; | |
625 | } | |
626 | ||
627 | fdt_for_each_subnode(subnode, fdto, node) { | |
628 | const char *name = fdt_get_name(fdto, subnode, NULL); | |
629 | int nnode; | |
630 | int ret; | |
631 | ||
632 | nnode = fdt_add_subnode(fdt, target, name); | |
633 | if (nnode == -FDT_ERR_EXISTS) { | |
634 | nnode = fdt_subnode_offset(fdt, target, name); | |
635 | if (nnode == -FDT_ERR_NOTFOUND) | |
636 | return -FDT_ERR_INTERNAL; | |
637 | } | |
638 | ||
639 | if (nnode < 0) | |
640 | return nnode; | |
641 | ||
642 | ret = overlay_apply_node(fdt, nnode, fdto, subnode); | |
643 | if (ret) | |
644 | return ret; | |
645 | } | |
646 | ||
647 | return 0; | |
648 | } | |
649 | ||
650 | /** | |
651 | * overlay_merge - Merge an overlay into its base device tree | |
652 | * @fdt: Base Device Tree blob | |
653 | * @fdto: Device tree overlay blob | |
654 | * | |
655 | * overlay_merge() merges an overlay into its base device tree. | |
656 | * | |
657 | * This is the next to last step in the device tree overlay application | |
658 | * process, when all the phandles have been adjusted and resolved and | |
659 | * you just have to merge overlay into the base device tree. | |
660 | * | |
661 | * returns: | |
662 | * 0 on success | |
663 | * Negative error code on failure | |
664 | */ | |
665 | static int overlay_merge(void *fdt, void *fdto) | |
666 | { | |
667 | int fragment; | |
668 | ||
669 | fdt_for_each_subnode(fragment, fdto, 0) { | |
670 | int overlay; | |
671 | int target; | |
672 | int ret; | |
673 | ||
674 | /* | |
675 | * Each fragments will have an __overlay__ node. If | |
676 | * they don't, it's not supposed to be merged | |
677 | */ | |
678 | overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); | |
679 | if (overlay == -FDT_ERR_NOTFOUND) | |
680 | continue; | |
681 | ||
682 | if (overlay < 0) | |
683 | return overlay; | |
684 | ||
685 | target = overlay_get_target(fdt, fdto, fragment, NULL); | |
686 | if (target < 0) | |
687 | return target; | |
688 | ||
689 | ret = overlay_apply_node(fdt, target, fdto, overlay); | |
690 | if (ret) | |
691 | return ret; | |
692 | } | |
693 | ||
694 | return 0; | |
695 | } | |
696 | ||
697 | static int get_path_len(const void *fdt, int nodeoffset) | |
698 | { | |
699 | int len = 0, namelen; | |
700 | const char *name; | |
701 | ||
702 | FDT_CHECK_HEADER(fdt); | |
703 | ||
704 | for (;;) { | |
705 | name = fdt_get_name(fdt, nodeoffset, &namelen); | |
706 | if (!name) | |
707 | return namelen; | |
708 | ||
709 | /* root? we're done */ | |
710 | if (namelen == 0) | |
711 | break; | |
712 | ||
713 | nodeoffset = fdt_parent_offset(fdt, nodeoffset); | |
714 | if (nodeoffset < 0) | |
715 | return nodeoffset; | |
716 | len += namelen + 1; | |
717 | } | |
718 | ||
719 | /* in case of root pretend it's "/" */ | |
720 | if (len == 0) | |
721 | len++; | |
722 | return len; | |
723 | } | |
724 | ||
725 | /** | |
726 | * overlay_symbol_update - Update the symbols of base tree after a merge | |
727 | * @fdt: Base Device Tree blob | |
728 | * @fdto: Device tree overlay blob | |
729 | * | |
730 | * overlay_symbol_update() updates the symbols of the base tree with the | |
731 | * symbols of the applied overlay | |
732 | * | |
733 | * This is the last step in the device tree overlay application | |
734 | * process, allowing the reference of overlay symbols by subsequent | |
735 | * overlay operations. | |
736 | * | |
737 | * returns: | |
738 | * 0 on success | |
739 | * Negative error code on failure | |
740 | */ | |
741 | static int overlay_symbol_update(void *fdt, void *fdto) | |
742 | { | |
743 | int root_sym, ov_sym, prop, path_len, fragment, target; | |
744 | int len, frag_name_len, ret, rel_path_len; | |
745 | const char *s, *e; | |
746 | const char *path; | |
747 | const char *name; | |
748 | const char *frag_name; | |
749 | const char *rel_path; | |
750 | const char *target_path; | |
751 | char *buf; | |
752 | void *p; | |
753 | ||
754 | ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); | |
755 | ||
756 | /* if no overlay symbols exist no problem */ | |
757 | if (ov_sym < 0) | |
758 | return 0; | |
759 | ||
760 | root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); | |
761 | ||
762 | /* it no root symbols exist we should create them */ | |
763 | if (root_sym == -FDT_ERR_NOTFOUND) | |
764 | root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); | |
765 | ||
766 | /* any error is fatal now */ | |
767 | if (root_sym < 0) | |
768 | return root_sym; | |
769 | ||
770 | /* iterate over each overlay symbol */ | |
771 | fdt_for_each_property_offset(prop, fdto, ov_sym) { | |
772 | path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); | |
773 | if (!path) | |
774 | return path_len; | |
775 | ||
776 | /* verify it's a string property (terminated by a single \0) */ | |
777 | if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) | |
778 | return -FDT_ERR_BADVALUE; | |
779 | ||
780 | /* keep end marker to avoid strlen() */ | |
781 | e = path + path_len; | |
782 | ||
783 | /* format: /<fragment-name>/__overlay__/<relative-subnode-path> */ | |
784 | ||
785 | if (*path != '/') | |
786 | return -FDT_ERR_BADVALUE; | |
787 | ||
788 | /* get fragment name first */ | |
789 | s = strchr(path + 1, '/'); | |
790 | if (!s) | |
791 | return -FDT_ERR_BADOVERLAY; | |
792 | ||
793 | frag_name = path + 1; | |
794 | frag_name_len = s - path - 1; | |
795 | ||
796 | /* verify format; safe since "s" lies in \0 terminated prop */ | |
797 | len = sizeof("/__overlay__/") - 1; | |
798 | if ((e - s) < len || memcmp(s, "/__overlay__/", len)) | |
799 | return -FDT_ERR_BADOVERLAY; | |
800 | ||
801 | rel_path = s + len; | |
802 | rel_path_len = e - rel_path; | |
803 | ||
804 | /* find the fragment index in which the symbol lies */ | |
805 | ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, | |
806 | frag_name_len); | |
807 | /* not found? */ | |
808 | if (ret < 0) | |
809 | return -FDT_ERR_BADOVERLAY; | |
810 | fragment = ret; | |
811 | ||
812 | /* an __overlay__ subnode must exist */ | |
813 | ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); | |
814 | if (ret < 0) | |
815 | return -FDT_ERR_BADOVERLAY; | |
816 | ||
817 | /* get the target of the fragment */ | |
818 | ret = overlay_get_target(fdt, fdto, fragment, &target_path); | |
819 | if (ret < 0) | |
820 | return ret; | |
821 | target = ret; | |
822 | ||
823 | /* if we have a target path use */ | |
824 | if (!target_path) { | |
825 | ret = get_path_len(fdt, target); | |
826 | if (ret < 0) | |
827 | return ret; | |
828 | len = ret; | |
829 | } else { | |
830 | len = strlen(target_path); | |
831 | } | |
832 | ||
833 | ret = fdt_setprop_placeholder(fdt, root_sym, name, | |
834 | len + (len > 1) + rel_path_len + 1, &p); | |
835 | if (ret < 0) | |
836 | return ret; | |
837 | ||
838 | if (!target_path) { | |
839 | /* again in case setprop_placeholder changed it */ | |
840 | ret = overlay_get_target(fdt, fdto, fragment, &target_path); | |
841 | if (ret < 0) | |
842 | return ret; | |
843 | target = ret; | |
844 | } | |
845 | ||
846 | buf = p; | |
847 | if (len > 1) { /* target is not root */ | |
848 | if (!target_path) { | |
849 | ret = fdt_get_path(fdt, target, buf, len + 1); | |
850 | if (ret < 0) | |
851 | return ret; | |
852 | } else | |
853 | memcpy(buf, target_path, len + 1); | |
854 | ||
855 | } else | |
856 | len--; | |
857 | ||
858 | buf[len] = '/'; | |
859 | memcpy(buf + len + 1, rel_path, rel_path_len); | |
860 | buf[len + 1 + rel_path_len] = '\0'; | |
861 | } | |
862 | ||
863 | return 0; | |
864 | } | |
865 | ||
866 | int fdt_overlay_apply(void *fdt, void *fdto) | |
867 | { | |
868 | uint32_t delta = fdt_get_max_phandle(fdt); | |
869 | int ret; | |
870 | ||
871 | FDT_CHECK_HEADER(fdt); | |
872 | FDT_CHECK_HEADER(fdto); | |
873 | ||
874 | ret = overlay_adjust_local_phandles(fdto, delta); | |
875 | if (ret) | |
876 | goto err; | |
877 | ||
878 | ret = overlay_update_local_references(fdto, delta); | |
879 | if (ret) | |
880 | goto err; | |
881 | ||
882 | ret = overlay_fixup_phandles(fdt, fdto); | |
883 | if (ret) | |
884 | goto err; | |
885 | ||
886 | ret = overlay_merge(fdt, fdto); | |
887 | if (ret) | |
888 | goto err; | |
889 | ||
890 | ret = overlay_symbol_update(fdt, fdto); | |
891 | if (ret) | |
892 | goto err; | |
893 | ||
894 | /* | |
895 | * The overlay has been damaged, erase its magic. | |
896 | */ | |
897 | fdt_set_magic(fdto, ~0); | |
898 | ||
899 | return 0; | |
900 | ||
901 | err: | |
902 | /* | |
903 | * The overlay might have been damaged, erase its magic. | |
904 | */ | |
905 | fdt_set_magic(fdto, ~0); | |
906 | ||
907 | /* | |
908 | * The base device tree might have been damaged, erase its | |
909 | * magic. | |
910 | */ | |
911 | fdt_set_magic(fdt, ~0); | |
912 | ||
913 | return ret; | |
914 | } |