b9891b96b0
- Report missing includes at the correct location. - Add initial support for the -@ option emitting a symbol table. - Add support for running tests with and without -@ - Add support for generating __fixups__ and __local_fixups__ - Attach the to-string transform to the node path.
286 lines
6.9 KiB
C++
286 lines
6.9 KiB
C++
/*-
|
|
* 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 <stdio.h>
|
|
|
|
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<property_value::value_type T>
|
|
void
|
|
check_manager::add_property_type_checker(const char *name, const string &prop)
|
|
{
|
|
checkers.insert(std::make_pair(string(name),
|
|
new property_type_checker<T>(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<property_value::STRING_LIST>(
|
|
"type-compatible", string("compatible"));
|
|
add_property_type_checker<property_value::STRING>(
|
|
"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
|
|
|