/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "checking.hh" #include using std::string; namespace dtc { namespace fdt { namespace checking { namespace { struct deleted_node_checker : public checker { deleted_node_checker(const char *name) : checker(name) {} virtual bool check_node(device_tree *, const node_ptr &n) { auto &deleted = n->deleted_child_nodes(); if (deleted.empty()) { return true; } bool plural = deleted.size() > 1; string errmsg("Attempts to delete "); errmsg += plural ? "nodes" : "node"; errmsg += " that "; errmsg += plural ? "were" : "was"; errmsg += " not added in merge: "; for (auto &d : deleted) { errmsg += d; } report_error(errmsg.c_str()); return false; } }; /** * Checker that verifies that every node that has children has * #address-cells and #size-cells properties. */ struct address_cells_checker : public checker { address_cells_checker(const char *name) : checker(name) {} virtual bool check_node(device_tree *, const node_ptr &n) { // If this has no children, it trivially meets the // conditions. if (n->child_begin() == n->child_end()) { return true; } bool found_address = false; bool found_size = false; for (auto i=n->property_begin(), e=n->property_end() ; i!=e ; ++i) { if (!found_address) { found_address = ((*i)->get_key() == "#address-cells"); } if (!found_size) { found_size = ((*i)->get_key() == "#size-cells"); } if (found_size && found_address) { break; } } if (!found_address) { report_error("Missing #address-cells property"); } if (!found_size) { report_error("Missing #size-cells property"); } return found_address && found_size; } }; } // anonymous namespace bool checker::visit_node(device_tree *tree, const node_ptr &n) { path.push_back(std::make_pair(n->name, n->unit_address)); // Check this node if (!check_node(tree, n)) { return false; } // Now check its properties for (auto i=n->property_begin(), e=n->property_end() ; i!=e ; ++i) { if (!check_property(tree, n, *i)) { return false; } } // And then recursively check the children for (node::child_iterator i=n->child_begin(), e=n->child_end() ; i!=e ; ++i) { if (!visit_node(tree, *i)) { return false; } } path.pop_back(); return true; } void checker::report_error(const char *errmsg) { fprintf(stderr, "Error: %s, while checking node: ", errmsg); for (auto &p : path) { putc('/', stderr); puts(p.first.c_str()); if (!(p.second.empty())) { putc('@', stderr); puts(p.second.c_str()); } } fprintf(stderr, " [-W%s]\n", checker_name); } bool property_checker::check_property(device_tree *tree, const node_ptr &n, property_ptr p) { if (p->get_key() == key) { if (!check(tree, n, p)) { report_error("property check failed"); return false; } } return true; } bool property_size_checker::check(device_tree *, const node_ptr &, property_ptr p) { uint32_t psize = 0; for (property::value_iterator i=p->begin(),e=p->end() ; i!=e ; ++i) { if (!i->is_binary()) { return false; } psize += i->byte_data.size(); } return psize == size; } template void check_manager::add_property_type_checker(const char *name, const string &prop) { checkers.insert(std::make_pair(string(name), new property_type_checker(name, prop))); } void check_manager::add_property_size_checker(const char *name, const string &prop, uint32_t size) { checkers.insert(std::make_pair(string(name), new property_size_checker(name, prop, size))); } check_manager::~check_manager() { while (checkers.begin() != checkers.end()) { delete checkers.begin()->second; checkers.erase(checkers.begin()); } while (disabled_checkers.begin() != disabled_checkers.end()) { delete disabled_checkers.begin()->second; disabled_checkers.erase(disabled_checkers.begin()); } } check_manager::check_manager() { // NOTE: All checks listed here MUST have a corresponding line // in the man page! add_property_type_checker( "type-compatible", string("compatible")); add_property_type_checker( "type-model", string("model")); add_property_size_checker("type-phandle", string("phandle"), 4); disabled_checkers.insert(std::make_pair(string("cells-attributes"), new address_cells_checker("cells-attributes"))); checkers.insert(std::make_pair(string("deleted-nodes"), new deleted_node_checker("deleted-nodes"))); } bool check_manager::run_checks(device_tree *tree, bool keep_going) { bool success = true; for (auto &i : checkers) { success &= i.second->check_tree(tree); if (!(success || keep_going)) { break; } } return success; } bool check_manager::disable_checker(const string &name) { auto checker = checkers.find(name); if (checker != checkers.end()) { disabled_checkers.insert(std::make_pair(name, checker->second)); checkers.erase(checker); return true; } return false; } bool check_manager::enable_checker(const string &name) { auto checker = disabled_checkers.find(name); if (checker != disabled_checkers.end()) { checkers.insert(std::make_pair(name, checker->second)); disabled_checkers.erase(checker); return true; } return false; } } // namespace checking } // namespace fdt } // namespace dtc