| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <string> |
| #include <unordered_map> |
| |
| extern "C" { |
| |
| #include "libufdt.h" |
| #include "ufdt_node_pool.h" |
| #include "ufdt_overlay.h" |
| #include "ufdt_overlay_internal.h" |
| |
| } |
| |
| #include "ufdt_test_overlay.h" |
| |
| static bool ufdt_node_compare(struct ufdt_node *node_a, struct ufdt_node *node_b, |
| struct ufdt* tree_a, struct ufdt* tree_b); |
| |
| /* |
| * Helper method to check if the tree rooted at node_b is a subset of the tree rooted |
| * at node_a. |
| */ |
| static bool compare_child_nodes(struct ufdt_node *node_a, struct ufdt_node *node_b, |
| struct ufdt * tree_a, struct ufdt * tree_b) { |
| bool result = true; |
| struct ufdt_node *it; |
| |
| for (it = ((struct ufdt_node_fdt_node *)node_b)->child; it; it = it->sibling) { |
| struct ufdt_node *cur_node = it; |
| struct ufdt_node *target_node = NULL; |
| |
| if (ufdt_node_tag(cur_node) == FDT_BEGIN_NODE) { |
| target_node = |
| ufdt_node_get_subnode_by_name(node_a, ufdt_node_name(cur_node)); |
| } else { |
| target_node = |
| ufdt_node_get_property_by_name(node_a, ufdt_node_name(cur_node)); |
| } |
| |
| if (target_node == NULL) { |
| result = false; |
| } else { |
| result = ufdt_node_compare(target_node, cur_node, tree_a, tree_b); |
| } |
| |
| if (!result) { |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| /* |
| * Method to compare two nodes with tag FDT_PROP. Also accounts for the cases where |
| * the property type is phandle. |
| */ |
| static bool ufdt_compare_property(struct ufdt_node* node_final, struct ufdt_node* node_overlay, |
| struct ufdt* tree_final, struct ufdt* tree_overlay) { |
| if (ufdt_node_tag(node_final) == FDT_PROP) { |
| /* Return -1 if property names are differemt */ |
| if (strcmp(ufdt_node_name(node_final), ufdt_node_name(node_overlay)) != 0) |
| return false; |
| |
| int length_data_final = 0, length_data_overlay = 0; |
| char *prop_data_final = ufdt_node_get_fdt_prop_data(node_final, &length_data_final); |
| char *prop_data_overlay = ufdt_node_get_fdt_prop_data(node_overlay, |
| &length_data_overlay); |
| |
| /* Confirm length for the property values are the same */ |
| if (length_data_final != length_data_overlay) { |
| return false; |
| } |
| |
| if (((length_data_final == 0) && (length_data_overlay ==0)) || |
| (memcmp(prop_data_final, prop_data_overlay, length_data_final) == 0)) { |
| // Return if the properties have same value. |
| return true; |
| } else { |
| /* check for the presence of phandles */ |
| for (int i = 0; i < length_data_final; i += sizeof(fdt32_t)) { |
| int offset_data_a = fdt32_to_cpu( |
| *reinterpret_cast<fdt32_t *>(prop_data_final + i)); |
| int offset_data_b = fdt32_to_cpu( |
| *reinterpret_cast<fdt32_t *>(prop_data_overlay + i)); |
| if (offset_data_a == offset_data_b) continue; |
| /* If the offsets have phandles, they would have valid target nodes */ |
| struct ufdt_node * target_node_a = ufdt_get_node_by_phandle(tree_final, |
| offset_data_a); |
| struct ufdt_node * target_node_b = ufdt_get_node_by_phandle(tree_overlay, |
| offset_data_b); |
| |
| /* |
| * verify that the target nodes are valid and point to the same node. |
| */ |
| if ((target_node_a == NULL) || (target_node_b == NULL) || |
| strcmp(ufdt_node_name(target_node_a), |
| ufdt_node_name(target_node_b)) != 0) { |
| return false; |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| /* |
| * Checks if the ufdt tree rooted at node_b is a subtree of the tree rooted at |
| * node_a. |
| */ |
| static bool ufdt_node_compare(struct ufdt_node *node_final, struct ufdt_node *node_overlay, |
| struct ufdt * tree_final, struct ufdt * tree_overlay) { |
| if (ufdt_node_tag(node_final) == FDT_PROP) { |
| return ufdt_compare_property(node_final, node_overlay, tree_final, tree_overlay); |
| } |
| |
| return compare_child_nodes(node_final, node_overlay, tree_final, tree_overlay); |
| } |
| |
| |
| /* |
| * Multiple fragments may fixup to the same node on the base device tree. |
| * Combine these fragments for easier verification. |
| */ |
| void ufdt_combine_fixup(struct ufdt *tree, const char *fixup, |
| struct ufdt_node **prev_node, struct ufdt_node_pool *node_pool) { |
| char *path, *prop_ptr, *offset_ptr; |
| char path_buf[1024]; |
| char *path_mem = NULL; |
| int result = 0; |
| |
| size_t fixup_len = strlen(fixup) + 1; |
| if (fixup_len > sizeof(path_buf)) { |
| path_mem = static_cast<char *>(dto_malloc(fixup_len)); |
| path = path_mem; |
| } else { |
| path = path_buf; |
| } |
| dto_memcpy(path, fixup, fixup_len); |
| |
| prop_ptr = dto_strchr(path, ':'); |
| if (prop_ptr == NULL) { |
| dto_error("Missing property part in '%s'\n", path); |
| goto fail; |
| } |
| |
| *prop_ptr = '\0'; |
| prop_ptr++; |
| |
| offset_ptr = dto_strchr(prop_ptr, ':'); |
| if (offset_ptr == NULL) { |
| dto_error("Missing offset part in '%s'\n", path); |
| goto fail; |
| } |
| |
| *offset_ptr = '\0'; |
| offset_ptr++; |
| |
| result = dto_strcmp(prop_ptr, "target"); |
| /* If the property being fixed up is not target, ignore and return */ |
| if (result == 0) { |
| struct ufdt_node *target_node; |
| target_node = ufdt_get_node_by_path(tree, path); |
| if (target_node == NULL) { |
| dto_error("Path '%s' not found\n", path); |
| } else { |
| /* The goal is to combine fragments that have a common target */ |
| if (*prev_node != NULL) { |
| ufdt_node_merge_into(*prev_node, target_node, node_pool); |
| } else { |
| *prev_node = target_node; |
| } |
| } |
| } |
| |
| fail: |
| if (path_mem) { |
| dto_free(path_mem); |
| } |
| |
| return; |
| } |
| |
| /* |
| * Creates a table of node paths to their corresponding phandles by walking |
| * through the 'symbols' node of the main device tree. The table would be |
| * used in combining overlay nodes that map to the same nodes in the |
| * main device tree. |
| */ |
| void create_path_phandle_map(std::unordered_map<uint32_t, std::string>* phandle_path_map, |
| struct ufdt* main_tree) { |
| int len = 0; |
| struct ufdt_node *main_symbols_node = |
| ufdt_get_node_by_path(main_tree, "/__symbols__"); |
| if (!main_symbols_node) { |
| dto_error("No node __symbols__ in main dtb.\n"); |
| return; |
| } |
| |
| struct ufdt_node **it = nullptr; |
| for_each_prop(it, main_symbols_node) { |
| const char* symbol_path = ufdt_node_get_fdt_prop_data(*it, &len); |
| struct ufdt_node* symbol_node = ufdt_get_node_by_path(main_tree, symbol_path); |
| uint32_t phandle = ufdt_node_get_phandle(symbol_node); |
| (*phandle_path_map)[phandle] = std::string(symbol_path); |
| } |
| } |
| |
| /* |
| * Recursively checks whether a node from another overlay fragment had overlaid the |
| * target node and if so merges the node into the previous node. |
| */ |
| static void 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) { |
| struct ufdt_node **it = nullptr; |
| for_each_node(it, node) { |
| //skips properties |
| if (ufdt_node_tag(*it) == FDT_BEGIN_NODE) { |
| combine_overlay_node(path_node_map, path + "/" + ufdt_node_name(*it), *it, pool); |
| } |
| } |
| |
| if (path_node_map->find(path) != path_node_map->end()) { |
| ufdt_node_merge_into((*path_node_map)[path], node, pool); |
| } else { |
| //This is the first node overlaying the target node, add the same to the |
| //table. |
| (*path_node_map)[path] = node; |
| } |
| } |
| |
| /* END of doing fixup in the overlay ufdt. */ |
| |
| static bool ufdt_verify_overlay_node(struct ufdt_node *target_node, |
| struct ufdt_node *overlay_node, |
| struct ufdt * target_tree, |
| struct ufdt * overlay_tree) { |
| return ufdt_node_compare(target_node, overlay_node, target_tree, overlay_tree); |
| } |
| |
| /* |
| * verify one overlay fragment (subtree). |
| */ |
| static int ufdt_verify_fragment(struct ufdt *tree, |
| struct ufdt *overlay_tree, |
| struct ufdt_node *frag_node) { |
| struct ufdt_node *target_node = NULL; |
| struct ufdt_node *overlay_node = NULL; |
| enum overlay_result target_search_result = ufdt_overlay_get_target(tree, frag_node, |
| &target_node); |
| if (target_node == NULL) { |
| return target_search_result; |
| } |
| |
| overlay_node = ufdt_node_get_node_by_path(frag_node, "__overlay__"); |
| if (overlay_node == NULL) { |
| dto_error("missing __overlay__ sub-node\n"); |
| return OVERLAY_RESULT_MISSING_OVERLAY; |
| } |
| |
| bool result = ufdt_verify_overlay_node(target_node, overlay_node, tree, overlay_tree); |
| |
| if (!result) { |
| dto_error("failed to verify overlay node %s to target %s\n", |
| ufdt_node_name(overlay_node), ufdt_node_name(target_node)); |
| return OVERLAY_RESULT_VERIFY_FAIL; |
| } |
| |
| return OVERLAY_RESULT_OK; |
| } |
| |
| /* |
| * verify each fragment in overlay. |
| */ |
| static int ufdt_overlay_verify_fragments(struct ufdt *final_tree, |
| struct ufdt *overlay_tree) { |
| enum overlay_result err; |
| struct ufdt_node **it; |
| for_each_node(it, overlay_tree->root) { |
| err = static_cast<enum overlay_result>(ufdt_verify_fragment(final_tree, overlay_tree, |
| *it)); |
| if (err == OVERLAY_RESULT_VERIFY_FAIL) { |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /* |
| * Examine target nodes for fragments in all overlays and combine ones with the |
| * same target. |
| */ |
| static void ufdt_overlay_combine_common_nodes(struct ufdt** overlay_trees, |
| size_t overlay_count, |
| struct ufdt* final_tree, |
| struct ufdt_node_pool* pool |
| ) { |
| std::unordered_map<std::string, struct ufdt_node*> path_node_map; |
| std::unordered_map<uint32_t, std::string> phandle_path_map; |
| |
| create_path_phandle_map(&phandle_path_map, final_tree); |
| |
| struct ufdt_node **it = nullptr; |
| for (size_t i = 0; i < overlay_count; i++) { |
| for_each_node(it, overlay_trees[i]->root) { |
| uint32_t target = 0; |
| const void* val = ufdt_node_get_fdt_prop_data_by_name(*it, "target", NULL); |
| if (val) { |
| dto_memcpy(&target, val, sizeof(target)); |
| target = fdt32_to_cpu(target); |
| std::string path = phandle_path_map[target]; |
| struct ufdt_node* overlay_node = ufdt_node_get_node_by_path(*it, "__overlay__"); |
| if (overlay_node != nullptr) { |
| combine_overlay_node(&path_node_map, path, overlay_node, pool); |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * Makes sure that all phandles in the overlays are unique since they will be |
| * combined before verification. |
| */ |
| int ufdt_resolve_duplicate_phandles(ufdt** overlay_tree, size_t overlay_count) { |
| size_t phandle_offset = 0; |
| for (size_t i = 0; i < overlay_count; i++) { |
| ufdt_try_increase_phandle(overlay_tree[i], phandle_offset); |
| if (ufdt_overlay_do_local_fixups(overlay_tree[i], phandle_offset) < 0) { |
| return -1; |
| } |
| phandle_offset = ufdt_get_max_phandle(overlay_tree[i]); |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Combines all overlays into a single tree at overlay_trees[0] |
| */ |
| int ufdt_combine_all_overlays(struct ufdt** overlay_trees, size_t overlay_count, |
| struct ufdt* final_tree, struct ufdt_node_pool* pool) { |
| struct ufdt* combined_overlay_tree = nullptr; |
| |
| if (!overlay_trees || !overlay_count || !final_tree || !pool) { |
| return -1; |
| } |
| |
| /* |
| * If there are duplicate phandles amongst the overlays, replace them with |
| * unique ones. |
| */ |
| if (ufdt_resolve_duplicate_phandles(overlay_trees, overlay_count) < 0) { |
| return -1; |
| } |
| |
| /* |
| * For each overlay, perform fixup for each fragment. |
| */ |
| for (size_t i = 0; i < overlay_count; i++) { |
| if (ufdt_overlay_do_fixups(final_tree, overlay_trees[i]) < 0) { |
| dto_error("failed to perform fixups in overlay\n"); |
| return -1; |
| } |
| } |
| |
| /* |
| * Iterate through each overlay and combine all nodes with the same target |
| * node. |
| */ |
| ufdt_overlay_combine_common_nodes(overlay_trees, overlay_count, final_tree, pool); |
| |
| /* |
| * Combine all overlays into the tree at overlay_trees[0] for easy |
| * verification. |
| */ |
| combined_overlay_tree = overlay_trees[0]; |
| struct ufdt_node* combined_root_node = combined_overlay_tree->root; |
| |
| for (size_t i = 1; i < overlay_count; i++) { |
| struct ufdt_node** it = nullptr; |
| struct ufdt_node* root_node = overlay_trees[i]->root; |
| for_each_node(it, root_node) { |
| ufdt_node_add_child(combined_root_node, *it); |
| } |
| ((struct ufdt_node_fdt_node *)root_node)->child = nullptr; |
| } |
| |
| /* |
| * Rebuild the phandle_table for the combined tree. |
| */ |
| combined_overlay_tree->phandle_table = build_phandle_table(combined_overlay_tree); |
| |
| return 0; |
| } |
| |
| int ufdt_verify_dtbo(struct fdt_header* final_fdt_header, |
| size_t final_fdt_size, void** overlay_arr, |
| size_t overlay_count) { |
| const size_t min_fdt_size = 8; |
| struct ufdt_node_pool pool; |
| struct ufdt* final_tree = nullptr; |
| struct ufdt** overlay_trees = nullptr; |
| int result = 1; |
| |
| if (final_fdt_header == NULL) { |
| goto fail; |
| } |
| |
| if (final_fdt_size < min_fdt_size || final_fdt_size != fdt_totalsize(final_fdt_header)) { |
| dto_error("Bad fdt size!\n"); |
| goto fail; |
| } |
| |
| ufdt_node_pool_construct(&pool); |
| final_tree = ufdt_from_fdt(final_fdt_header, final_fdt_size, &pool); |
| |
| overlay_trees = new ufdt*[overlay_count]; |
| for (size_t i = 0; i < overlay_count; i++) { |
| size_t fdt_size = fdt_totalsize(overlay_arr[i]); |
| overlay_trees[i] = ufdt_from_fdt(overlay_arr[i], fdt_size, &pool); |
| } |
| |
| if (ufdt_combine_all_overlays(overlay_trees, overlay_count, final_tree, &pool) < 0) { |
| dto_error("Unable to combine overlays\n"); |
| goto fail; |
| } |
| if (ufdt_overlay_verify_fragments(final_tree, overlay_trees[0]) < 0) { |
| dto_error("Failed to verify overlay application\n"); |
| goto fail; |
| } else { |
| result = 0; |
| } |
| |
| fail: |
| if (overlay_trees) { |
| for (size_t i = 0; i < overlay_count; i++) { |
| ufdt_destruct(overlay_trees[i], &pool); |
| } |
| delete[] overlay_trees; |
| } |
| |
| ufdt_destruct(final_tree, &pool); |
| ufdt_node_pool_destruct(&pool); |
| return result; |
| } |