/*-
 * 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_
