root/usr.bin/dtc/checking.hh
/*-
 * 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.
 */

#ifndef _CHECKING_HH_
#define _CHECKING_HH_
#include <string>
#include "fdt.hh"

namespace dtc
{
namespace fdt
{
namespace checking
{
/**
 * Base class for all checkers.  This will visit the entire tree and perform
 * semantic checks defined in subclasses.  Note that device trees are generally
 * small (a few dozen nodes at most) and so we optimise for flexibility and
 * extensibility here, not for performance.  Each checker will visit the entire
 * tree.
 */
class checker
{
        /**
         * The path to the current node being checked.  This is used for
         * printing error messages.
         */
        device_tree::node_path path;
        /**
         * The name of the checker.  This is used for printing error messages
         * and for enabling / disabling specific checkers from the command
         * line.
         */
        const char *checker_name;
        /**
         * Visits each node, calling the checker functions on properties and
         * nodes.
         */
        bool visit_node(device_tree *tree, const node_ptr &n);
        protected:
        /**
         * Prints the error message, along with the path to the node that
         * caused the error and the name of the checker.
         */
        void report_error(const char *errmsg);
        public:
        /**
         * Constructor.  Takes the name of this checker, which is which is used
         * when reporting errors.
         */
        checker(const char *name) : checker_name(name) {}
        /**
         * Virtual destructor in case any subclasses need to do cleanup.
         */
        virtual ~checker() {}
        /**
         * Method for checking that a node is valid.  The root class version
         * does nothing, subclasses should override this.
         */
        virtual bool check_node(device_tree *, const node_ptr &)
        {
                return true;
        }
        /**
         * Method for checking that a property is valid.  The root class
         * version does nothing, subclasses should override this.
         */
        virtual bool check_property(device_tree *, const node_ptr &, property_ptr )
        {
                return true;
        }
        /**
         * Runs the checker on the specified device tree.
         */
        bool check_tree(fdt::device_tree *tree)
        {
                return visit_node(tree, tree->get_root());
        }
};

/**
 * Abstract base class for simple property checks.  This class defines a check
 * method for subclasses, which is invoked only when it finds a property with
 * the matching name.  To define simple property checkers, just subclass this
 * and override the check() method.
 */
class property_checker : public checker
{
        /**
         * The name of the property that this checker is looking for.
         */
        std::string key;
        public:
        /**
         * Implementation of the generic property-checking method that checks
         * for a property with the name specified in the constructor.
         */
        virtual bool check_property(device_tree *tree, const node_ptr &n, property_ptr p);
        /**
         * Constructor.  Takes the name of the checker and the name of the
         * property to check.
         */
        property_checker(const char* name, const std::string &property_name)
                : checker(name), key(property_name) {}
        /**
         * The check method, which subclasses should implement.
         */
        virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p) = 0;
};

/**
 * Property type checker.
 */
template<property_value::value_type T>
struct property_type_checker : public property_checker
{
        /**
         * Constructor, takes the name of the checker and the name of the
         * property to check as arguments.
         */
        property_type_checker(const char* name, const std::string &property_name) :
                property_checker(name, property_name) {}
        virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p) = 0;
};

/**
 * Empty property checker.  This checks that the property has no value.
 */
template<>
struct property_type_checker <property_value::EMPTY> : public property_checker
{
        property_type_checker(const char* name, const std::string &property_name) :
                property_checker(name, property_name) {}
        virtual bool check(device_tree *, const node_ptr &, property_ptr p)
        {
                return p->begin() == p->end();
        }
};

/**
 * String property checker.  This checks that the property has exactly one
 * value, which is a string.
 */
template<>
struct property_type_checker <property_value::STRING> : public property_checker
{
        property_type_checker(const char* name, const std::string &property_name) :
                property_checker(name, property_name) {}
        virtual bool check(device_tree *, const node_ptr &, property_ptr p)
        {
                return (p->begin() + 1 == p->end()) && p->begin()->is_string();
        }
};
/**
 * String list property checker.  This checks that the property has at least
 * one value, all of which are strings.
 */
template<>
struct property_type_checker <property_value::STRING_LIST> :
        public property_checker
{
        property_type_checker(const char* name, const std::string &property_name) :
                property_checker(name, property_name) {}
        virtual bool check(device_tree *, const node_ptr &, property_ptr p)
        {
                for (property::value_iterator i=p->begin(),e=p->end() ; i!=e ;
                     ++i)
                {
                        if (!(i->is_string() || i->is_string_list()))
                        {
                                return false;
                        }
                }
                return p->begin() != p->end();
        }
};

/**
 * Phandle property checker.  This checks that the property has exactly one
 * value, which is a valid phandle.
 */
template<>
struct property_type_checker <property_value::PHANDLE> : public property_checker
{
        property_type_checker(const char* name, const std::string &property_name) :
                property_checker(name, property_name) {}
        virtual bool check(device_tree *tree, const node_ptr &, property_ptr p)
        {
                return (p->begin() + 1 == p->end()) &&
                        (tree->referenced_node(*p->begin()) != 0);
        }
};

/**
 * Check that a property has the correct size.
 */
struct property_size_checker : public property_checker
{
        /**
         * The expected size of the property.
         */
        uint32_t size;
        public:
        /**
         * Constructor, takes the name of the checker, the name of the property
         * to check, and its expected size as arguments.
         */
        property_size_checker(const char* name,
                              const std::string &property_name,
                              uint32_t bytes)
                : property_checker(name, property_name), size(bytes) {}
        /**
         * Check, validates that the property has the correct size.
         */
        virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p);
};


/**
 * The check manager is the interface to running the checks.  This allows
 * default checks to be enabled, non-default checks to be enabled, and so on.
 */
class check_manager
{
        /**
         * The enabled checkers, indexed by their names.  The name is used when
         * disabling checkers from the command line.  When this manager runs,
         * it will only run the checkers from this map.
         */
        std::unordered_map<std::string, checker*> checkers;
        /**
         * The disabled checkers.  Moving checkers to this list disables them,
         * but allows them to be easily moved back.
         */
        std::unordered_map<std::string, checker*> disabled_checkers;
        /**
         * Helper function for adding a property value checker.
         */
        template<property_value::value_type T>
        void add_property_type_checker(const char *name, const std::string &prop);
        /**
         * Helper function for adding a simple type checker.
         */
        void add_property_type_checker(const char *name, const std::string &prop);
        /**
         * Helper function for adding a property value checker.
         */
        void add_property_size_checker(const char *name,
                                       const std::string &prop,
                                       uint32_t size);
        public:
        /**
         * Delete all of the checkers that are part of this checker manager.
         */
        ~check_manager();
        /**
         * Default constructor, creates check manager containing all of the
         * default checks.
         */
        check_manager();
        /**
         * Run all of the checks on the specified tree.
         */
        bool run_checks(device_tree *tree, bool keep_going);
        /**
         * Disables the named checker.
         */
        bool disable_checker(const std::string &name);
        /**
         * Enables the named checker.
         */
        bool enable_checker(const std::string &name);
};

} // namespace checking

} // namespace fdt

} // namespace dtc

#endif // !_CHECKING_HH_