472 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			472 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
/*
 | 
						|
 * 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 fragment node %s to target %s\n",
 | 
						|
                  ufdt_node_name(frag_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;
 | 
						|
    }
 | 
						|
 | 
						|
    for (size_t i = 0; i < overlay_count; i++) {
 | 
						|
        if ((fdt_magic(overlay_arr[i]) != FDT_MAGIC) || !fdt_totalsize(overlay_arr[i])) {
 | 
						|
            dto_error("Corrupted or empty overlay\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;
 | 
						|
}
 |