• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <string>
18 #include <unordered_map>
19 
20 extern "C" {
21 
22 #include "libufdt.h"
23 #include "ufdt_node_pool.h"
24 #include "ufdt_overlay.h"
25 #include "ufdt_overlay_internal.h"
26 
27 }
28 
29 #include "ufdt_test_overlay.h"
30 
31 static bool ufdt_node_compare(struct ufdt_node *node_a, struct ufdt_node *node_b,
32                               struct ufdt* tree_a, struct ufdt* tree_b);
33 
34 /*
35  * Helper method to check if the tree rooted at node_b is a subset of the tree rooted
36  * at node_a.
37  */
compare_child_nodes(struct ufdt_node * node_a,struct ufdt_node * node_b,struct ufdt * tree_a,struct ufdt * tree_b)38 static bool compare_child_nodes(struct ufdt_node *node_a, struct ufdt_node *node_b,
39                                 struct ufdt * tree_a, struct ufdt * tree_b) {
40     bool result = true;
41     struct ufdt_node *it;
42 
43     for (it = ((struct ufdt_node_fdt_node *)node_b)->child; it; it = it->sibling) {
44         struct ufdt_node *cur_node = it;
45         struct ufdt_node *target_node = NULL;
46 
47         if (ufdt_node_tag(cur_node) == FDT_BEGIN_NODE) {
48             target_node =
49                     ufdt_node_get_subnode_by_name(node_a, ufdt_node_name(cur_node));
50         } else {
51             target_node =
52                     ufdt_node_get_property_by_name(node_a, ufdt_node_name(cur_node));
53         }
54 
55         if (target_node == NULL) {
56             result = false;
57         } else {
58             result = ufdt_node_compare(target_node, cur_node, tree_a, tree_b);
59         }
60 
61         if (!result) {
62             break;
63         }
64     }
65 
66     return result;
67 }
68 
69 /*
70  * Method to compare two nodes with tag FDT_PROP. Also accounts for the cases where
71  * the property type is phandle.
72  */
ufdt_compare_property(struct ufdt_node * node_final,struct ufdt_node * node_overlay,struct ufdt * tree_final,struct ufdt * tree_overlay)73 static bool ufdt_compare_property(struct ufdt_node* node_final, struct ufdt_node* node_overlay,
74                                   struct ufdt* tree_final, struct ufdt* tree_overlay) {
75     if (ufdt_node_tag(node_final) == FDT_PROP) {
76         /* Return -1 if property names are differemt */
77         if (strcmp(ufdt_node_name(node_final), ufdt_node_name(node_overlay)) != 0)
78             return false;
79 
80         int length_data_final = 0, length_data_overlay = 0;
81         char *prop_data_final = ufdt_node_get_fdt_prop_data(node_final, &length_data_final);
82         char *prop_data_overlay = ufdt_node_get_fdt_prop_data(node_overlay,
83                                                               &length_data_overlay);
84 
85         /* Confirm length for the property values are the same */
86         if (length_data_final != length_data_overlay) {
87             return false;
88         }
89 
90         if (((length_data_final == 0) && (length_data_overlay ==0)) ||
91             (memcmp(prop_data_final, prop_data_overlay, length_data_final) == 0)) {
92             // Return if the properties have same value.
93             return true;
94         } else {
95             /* check for the presence of phandles */
96             for (int i = 0; i < length_data_final; i += sizeof(fdt32_t)) {
97                 int offset_data_a = fdt32_to_cpu(
98                         *reinterpret_cast<fdt32_t *>(prop_data_final + i));
99                 int offset_data_b = fdt32_to_cpu(
100                         *reinterpret_cast<fdt32_t *>(prop_data_overlay + i));
101                 if (offset_data_a == offset_data_b) continue;
102                 /* If the offsets have phandles, they would have valid target nodes */
103                 struct ufdt_node * target_node_a = ufdt_get_node_by_phandle(tree_final,
104                                                                             offset_data_a);
105                 struct ufdt_node * target_node_b = ufdt_get_node_by_phandle(tree_overlay,
106                                                                             offset_data_b);
107 
108                 /*
109                  * verify that the target nodes are valid and point to the same node.
110                  */
111                 if ((target_node_a == NULL) || (target_node_b == NULL) ||
112                     strcmp(ufdt_node_name(target_node_a),
113                            ufdt_node_name(target_node_b)) != 0) {
114                     return false;
115                 }
116             }
117         }
118     }
119 
120     return true;
121 }
122 
123 /*
124  * Checks if the ufdt tree rooted at node_b is a subtree of the tree rooted at
125  * node_a.
126  */
ufdt_node_compare(struct ufdt_node * node_final,struct ufdt_node * node_overlay,struct ufdt * tree_final,struct ufdt * tree_overlay)127 static bool ufdt_node_compare(struct ufdt_node *node_final, struct ufdt_node *node_overlay,
128                               struct ufdt * tree_final, struct ufdt * tree_overlay) {
129     if (ufdt_node_tag(node_final) == FDT_PROP) {
130         return ufdt_compare_property(node_final, node_overlay, tree_final, tree_overlay);
131     }
132 
133     return compare_child_nodes(node_final, node_overlay, tree_final, tree_overlay);
134 }
135 
136 
137 /*
138  * Multiple fragments may fixup to the same node on the base device tree.
139  * Combine these fragments for easier verification.
140  */
ufdt_combine_fixup(struct ufdt * tree,const char * fixup,struct ufdt_node ** prev_node,struct ufdt_node_pool * node_pool)141 void ufdt_combine_fixup(struct ufdt *tree, const char *fixup,
142                         struct ufdt_node **prev_node, struct ufdt_node_pool *node_pool) {
143     char *path, *prop_ptr, *offset_ptr;
144     char path_buf[1024];
145     char *path_mem = NULL;
146     int result = 0;
147 
148     size_t fixup_len = strlen(fixup) + 1;
149     if (fixup_len > sizeof(path_buf)) {
150         path_mem = static_cast<char *>(dto_malloc(fixup_len));
151         path = path_mem;
152     } else {
153         path = path_buf;
154     }
155     dto_memcpy(path, fixup, fixup_len);
156 
157     prop_ptr = dto_strchr(path, ':');
158     if (prop_ptr == NULL) {
159         dto_error("Missing property part in '%s'\n", path);
160         goto fail;
161     }
162 
163     *prop_ptr = '\0';
164     prop_ptr++;
165 
166     offset_ptr = dto_strchr(prop_ptr, ':');
167     if (offset_ptr == NULL) {
168         dto_error("Missing offset part in '%s'\n", path);
169         goto fail;
170     }
171 
172     *offset_ptr = '\0';
173     offset_ptr++;
174 
175     result = dto_strcmp(prop_ptr, "target");
176     /* If the property being fixed up is not target, ignore and return */
177     if (result == 0) {
178         struct ufdt_node *target_node;
179         target_node = ufdt_get_node_by_path(tree, path);
180         if (target_node == NULL) {
181             dto_error("Path '%s' not found\n", path);
182         } else {
183             /* The goal is to combine fragments that have a common target */
184             if (*prev_node != NULL) {
185                 ufdt_node_merge_into(*prev_node, target_node, node_pool);
186             } else {
187                 *prev_node = target_node;
188             }
189         }
190     }
191 
192 fail:
193     if (path_mem) {
194         dto_free(path_mem);
195     }
196 
197     return;
198 }
199 
200 /*
201  * Creates a table of node paths to their corresponding phandles by walking
202  * through the 'symbols' node of the main device tree. The table would be
203  * used in combining overlay nodes that map to the same nodes in the
204  * main device tree.
205  */
create_path_phandle_map(std::unordered_map<uint32_t,std::string> * phandle_path_map,struct ufdt * main_tree)206 void create_path_phandle_map(std::unordered_map<uint32_t, std::string>* phandle_path_map,
207                              struct ufdt* main_tree) {
208     int len = 0;
209     struct ufdt_node *main_symbols_node =
210             ufdt_get_node_by_path(main_tree, "/__symbols__");
211     if (!main_symbols_node) {
212         dto_error("No node __symbols__ in main dtb.\n");
213         return;
214     }
215 
216     struct ufdt_node **it = nullptr;
217     for_each_prop(it, main_symbols_node) {
218         const char* symbol_path = ufdt_node_get_fdt_prop_data(*it, &len);
219         struct ufdt_node* symbol_node = ufdt_get_node_by_path(main_tree, symbol_path);
220         uint32_t phandle = ufdt_node_get_phandle(symbol_node);
221         (*phandle_path_map)[phandle] = std::string(symbol_path);
222     }
223 }
224 
225 /*
226  * Recursively checks whether a node from another overlay fragment had overlaid the
227  * target node and if so merges the node into the previous node.
228  */
combine_overlay_node(std::unordered_map<std::string,struct ufdt_node * > * path_node_map,std::string path,struct ufdt_node * node,struct ufdt_node_pool * pool)229 static void combine_overlay_node(std::unordered_map<std::string,
230                                  struct ufdt_node*>* path_node_map,
231                                  std::string path,
232                                  struct ufdt_node* node,
233                                  struct ufdt_node_pool* pool) {
234     struct ufdt_node **it = nullptr;
235     for_each_node(it, node) {
236         //skips properties
237         if (ufdt_node_tag(*it) == FDT_BEGIN_NODE) {
238             combine_overlay_node(path_node_map, path + "/" + ufdt_node_name(*it), *it, pool);
239         }
240     }
241 
242     if (path_node_map->find(path) != path_node_map->end()) {
243         ufdt_node_merge_into((*path_node_map)[path], node, pool);
244     } else {
245         //This is the first node overlaying the target node, add the same to the
246         //table.
247         (*path_node_map)[path] = node;
248     }
249 }
250 
251 /* END of doing fixup in the overlay ufdt. */
252 
ufdt_verify_overlay_node(struct ufdt_node * target_node,struct ufdt_node * overlay_node,struct ufdt * target_tree,struct ufdt * overlay_tree)253 static bool ufdt_verify_overlay_node(struct ufdt_node *target_node,
254                                      struct ufdt_node *overlay_node,
255                                      struct ufdt * target_tree,
256                                      struct ufdt * overlay_tree) {
257     return ufdt_node_compare(target_node, overlay_node, target_tree, overlay_tree);
258 }
259 
260 /*
261  * verify one overlay fragment (subtree).
262  */
ufdt_verify_fragment(struct ufdt * tree,struct ufdt * overlay_tree,struct ufdt_node * frag_node)263 static int ufdt_verify_fragment(struct ufdt *tree,
264                                 struct ufdt *overlay_tree,
265                                 struct ufdt_node *frag_node) {
266     struct ufdt_node *target_node = NULL;
267     struct ufdt_node *overlay_node = NULL;
268     enum overlay_result target_search_result = ufdt_overlay_get_target(tree, frag_node,
269                                                                        &target_node);
270     if (target_node == NULL) {
271         return target_search_result;
272     }
273 
274     overlay_node = ufdt_node_get_node_by_path(frag_node, "__overlay__");
275     if (overlay_node == NULL) {
276         dto_error("missing __overlay__ sub-node\n");
277         return OVERLAY_RESULT_MISSING_OVERLAY;
278     }
279 
280     bool result = ufdt_verify_overlay_node(target_node, overlay_node, tree, overlay_tree);
281 
282     if (!result) {
283         dto_error("failed to verify overlay node %s to target %s\n",
284                   ufdt_node_name(overlay_node), ufdt_node_name(target_node));
285         return OVERLAY_RESULT_VERIFY_FAIL;
286     }
287 
288     return OVERLAY_RESULT_OK;
289 }
290 
291 /*
292  * verify each fragment in overlay.
293  */
ufdt_overlay_verify_fragments(struct ufdt * final_tree,struct ufdt * overlay_tree)294 static int ufdt_overlay_verify_fragments(struct ufdt *final_tree,
295                                          struct ufdt *overlay_tree) {
296     enum overlay_result err;
297     struct ufdt_node **it;
298     for_each_node(it, overlay_tree->root) {
299         err = static_cast<enum overlay_result>(ufdt_verify_fragment(final_tree, overlay_tree,
300                                                                     *it));
301         if (err == OVERLAY_RESULT_VERIFY_FAIL) {
302             return -1;
303         }
304     }
305     return 0;
306 }
307 
308 /*
309  * Examine target nodes for fragments in all overlays and combine ones with the
310  * same target.
311  */
ufdt_overlay_combine_common_nodes(struct ufdt ** overlay_trees,size_t overlay_count,struct ufdt * final_tree,struct ufdt_node_pool * pool)312 static void ufdt_overlay_combine_common_nodes(struct ufdt** overlay_trees,
313                                               size_t overlay_count,
314                                               struct ufdt* final_tree,
315                                               struct ufdt_node_pool* pool
316                                              ) {
317     std::unordered_map<std::string, struct ufdt_node*> path_node_map;
318     std::unordered_map<uint32_t, std::string> phandle_path_map;
319 
320     create_path_phandle_map(&phandle_path_map, final_tree);
321 
322     struct ufdt_node **it = nullptr;
323     for (size_t i = 0; i < overlay_count; i++) {
324         for_each_node(it, overlay_trees[i]->root) {
325             uint32_t target = 0;
326             const void* val = ufdt_node_get_fdt_prop_data_by_name(*it, "target", NULL);
327             if (val) {
328                 dto_memcpy(&target, val, sizeof(target));
329                 target = fdt32_to_cpu(target);
330                 std::string path = phandle_path_map[target];
331                 struct ufdt_node* overlay_node = ufdt_node_get_node_by_path(*it, "__overlay__");
332                 if (overlay_node != nullptr) {
333                     combine_overlay_node(&path_node_map, path, overlay_node, pool);
334                 }
335             }
336         }
337     }
338 }
339 
340 /*
341  * Makes sure that all phandles in the overlays are unique since they will be
342  * combined before verification.
343  */
ufdt_resolve_duplicate_phandles(ufdt ** overlay_tree,size_t overlay_count)344 int ufdt_resolve_duplicate_phandles(ufdt** overlay_tree, size_t overlay_count) {
345   size_t phandle_offset = 0;
346   for (size_t i = 0; i < overlay_count; i++) {
347         ufdt_try_increase_phandle(overlay_tree[i], phandle_offset);
348         if (ufdt_overlay_do_local_fixups(overlay_tree[i], phandle_offset) < 0) {
349             return -1;
350         }
351         phandle_offset = ufdt_get_max_phandle(overlay_tree[i]);
352   }
353 
354   return 0;
355 }
356 
357 /*
358  * Combines all overlays into a single tree at overlay_trees[0]
359  */
ufdt_combine_all_overlays(struct ufdt ** overlay_trees,size_t overlay_count,struct ufdt * final_tree,struct ufdt_node_pool * pool)360 int ufdt_combine_all_overlays(struct ufdt** overlay_trees, size_t overlay_count,
361                               struct ufdt* final_tree, struct ufdt_node_pool* pool) {
362     struct ufdt* combined_overlay_tree = nullptr;
363 
364     if (!overlay_trees || !overlay_count || !final_tree || !pool) {
365         return -1;
366     }
367 
368     /*
369      * If there are duplicate phandles amongst the overlays, replace them with
370      * unique ones.
371      */
372     if (ufdt_resolve_duplicate_phandles(overlay_trees, overlay_count) < 0) {
373         return -1;
374     }
375 
376     /*
377      * For each overlay, perform fixup for each fragment.
378      */
379     for (size_t i = 0; i < overlay_count; i++) {
380         if (ufdt_overlay_do_fixups(final_tree, overlay_trees[i]) < 0) {
381             dto_error("failed to perform fixups in overlay\n");
382             return -1;
383         }
384     }
385 
386     /*
387      * Iterate through each overlay and combine all nodes with the same target
388      * node.
389      */
390     ufdt_overlay_combine_common_nodes(overlay_trees, overlay_count, final_tree, pool);
391 
392     /*
393      * Combine all overlays into the tree at overlay_trees[0] for easy
394      * verification.
395      */
396     combined_overlay_tree = overlay_trees[0];
397     struct ufdt_node* combined_root_node = combined_overlay_tree->root;
398 
399     for (size_t i = 1; i < overlay_count; i++) {
400         struct ufdt_node** it = nullptr;
401         struct ufdt_node* root_node = overlay_trees[i]->root;
402         for_each_node(it, root_node) {
403             ufdt_node_add_child(combined_root_node, *it);
404         }
405         ((struct ufdt_node_fdt_node *)root_node)->child = nullptr;
406     }
407 
408     /*
409      * Rebuild the phandle_table for the combined tree.
410      */
411     combined_overlay_tree->phandle_table = build_phandle_table(combined_overlay_tree);
412 
413     return 0;
414 }
415 
ufdt_verify_dtbo(struct fdt_header * final_fdt_header,size_t final_fdt_size,void ** overlay_arr,size_t overlay_count)416 int ufdt_verify_dtbo(struct fdt_header* final_fdt_header,
417                      size_t final_fdt_size, void** overlay_arr,
418                      size_t overlay_count) {
419     const size_t min_fdt_size = 8;
420     struct ufdt_node_pool pool;
421     struct ufdt* final_tree = nullptr;
422     struct ufdt** overlay_trees = nullptr;
423     int result = 1;
424 
425     if (final_fdt_header == NULL) {
426         goto fail;
427     }
428 
429     if (final_fdt_size < min_fdt_size || final_fdt_size != fdt_totalsize(final_fdt_header)) {
430         dto_error("Bad fdt size!\n");
431         goto fail;
432     }
433 
434     for (size_t i = 0; i < overlay_count; i++) {
435         if ((fdt_magic(overlay_arr[i]) != FDT_MAGIC) || !fdt_totalsize(overlay_arr[i])) {
436             dto_error("Corrupted or empty overlay\n");
437             goto fail;
438         }
439     }
440     ufdt_node_pool_construct(&pool);
441     final_tree = ufdt_from_fdt(final_fdt_header, final_fdt_size, &pool);
442 
443     overlay_trees = new ufdt*[overlay_count];
444     for (size_t i = 0; i < overlay_count; i++) {
445         size_t fdt_size = fdt_totalsize(overlay_arr[i]);
446         overlay_trees[i] = ufdt_from_fdt(overlay_arr[i], fdt_size, &pool);
447     }
448 
449     if (ufdt_combine_all_overlays(overlay_trees, overlay_count, final_tree, &pool) < 0) {
450         dto_error("Unable to combine overlays\n");
451         goto fail;
452     }
453     if (ufdt_overlay_verify_fragments(final_tree, overlay_trees[0]) < 0) {
454         dto_error("Failed to verify overlay application\n");
455         goto fail;
456     } else {
457         result = 0;
458     }
459 
460 fail:
461     if (overlay_trees) {
462         for (size_t i = 0; i < overlay_count; i++) {
463             ufdt_destruct(overlay_trees[i], &pool);
464         }
465         delete[] overlay_trees;
466     }
467 
468     ufdt_destruct(final_tree, &pool);
469     ufdt_node_pool_destruct(&pool);
470     return result;
471 }
472