1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2013 David Chisnall
5 * All rights reserved.
6 *
7 * This software was developed by SRI International and the University of
8 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
9 * ("CTSRD"), as part of the DARPA CRASH research programme.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include "checking.hh"
34#include <stdio.h>
35
36using std::string;
37
38namespace dtc
39{
40namespace fdt
41{
42namespace checking
43{
44
45namespace
46{
47	struct deleted_node_checker : public checker
48	{
49		deleted_node_checker(const char *name) : checker(name) {}
50		virtual bool check_node(device_tree *, const node_ptr &n)
51		{
52			auto &deleted = n->deleted_child_nodes();
53			if (deleted.empty())
54			{
55				return true;
56			}
57			bool plural = deleted.size() > 1;
58			string errmsg("Attempts to delete ");
59			errmsg += plural ? "nodes" : "node";
60			errmsg += " that ";
61			errmsg += plural ? "were" : "was";
62			errmsg += " not added in merge: ";
63			for (auto &d : deleted)
64			{
65				errmsg += d;
66			}
67			report_error(errmsg.c_str());
68			return false;
69		}
70	};
71	/**
72	 * Checker that verifies that every node that has children has
73	 * #address-cells and #size-cells properties.
74	 */
75	struct address_cells_checker : public checker
76	{
77		address_cells_checker(const char *name) : checker(name) {}
78		virtual bool check_node(device_tree *, const node_ptr &n)
79		{
80			// If this has no children, it trivially meets the
81			// conditions.
82			if (n->child_begin() == n->child_end())
83			{
84				return true;
85			}
86			bool found_address = false;
87			bool found_size = false;
88			for (auto i=n->property_begin(), e=n->property_end() ; i!=e ; ++i)
89			{
90				if (!found_address)
91				{
92					found_address = ((*i)->get_key() == "#address-cells");
93				}
94				if (!found_size)
95				{
96					found_size = ((*i)->get_key() == "#size-cells");
97				}
98				if (found_size && found_address)
99				{
100						break;
101				}
102			}
103			if (!found_address)
104			{
105					report_error("Missing #address-cells property");
106			}
107			if (!found_size)
108			{
109					report_error("Missing #size-cells property");
110			}
111			return found_address && found_size;
112		}
113	};
114} // anonymous namespace
115
116bool
117checker::visit_node(device_tree *tree, const node_ptr &n)
118{
119	path.push_back(std::make_pair(n->name, n->unit_address));
120	// Check this node
121	if (!check_node(tree, n))
122	{
123		return false;
124	}
125	// Now check its properties
126	for (auto i=n->property_begin(), e=n->property_end() ; i!=e ; ++i)
127	{
128		if (!check_property(tree, n, *i))
129		{
130			return false;
131		}
132	}
133	// And then recursively check the children
134	for (node::child_iterator i=n->child_begin(), e=n->child_end() ; i!=e ;
135	     ++i)
136	{
137		if (!visit_node(tree, *i))
138		{
139			return false;
140		}
141	}
142	path.pop_back();
143	return true;
144}
145
146void
147checker::report_error(const char *errmsg)
148{
149	fprintf(stderr, "Error: %s, while checking node: ", errmsg);
150	for (auto &p : path)
151	{
152		putc('/', stderr);
153		puts(p.first.c_str());
154		if (!(p.second.empty()))
155		{
156			putc('@', stderr);
157			puts(p.second.c_str());
158		}
159	}
160	fprintf(stderr, " [-W%s]\n", checker_name);
161}
162
163bool
164property_checker::check_property(device_tree *tree, const node_ptr &n, property_ptr p)
165{
166	if (p->get_key() == key)
167	{
168		if (!check(tree, n, p))
169		{
170			report_error("property check failed");
171			return false;
172		}
173	}
174	return true;
175}
176
177bool
178property_size_checker::check(device_tree *, const node_ptr &, property_ptr p)
179{
180	uint32_t psize = 0;
181	for (property::value_iterator i=p->begin(),e=p->end() ; i!=e ; ++i)
182	{
183		if (!i->is_binary())
184		{
185			return false;
186		}
187		psize += i->byte_data.size();
188	}
189	return psize == size;
190}
191
192template<property_value::value_type T>
193void
194check_manager::add_property_type_checker(const char *name, const string &prop)
195{
196	checkers.insert(std::make_pair(string(name),
197		new property_type_checker<T>(name, prop)));
198}
199
200void
201check_manager::add_property_size_checker(const char *name,
202                                         const string &prop,
203                                         uint32_t size)
204{
205	checkers.insert(std::make_pair(string(name),
206		new property_size_checker(name, prop, size)));
207}
208
209check_manager::~check_manager()
210{
211	while (checkers.begin() != checkers.end())
212	{
213		delete checkers.begin()->second;
214		checkers.erase(checkers.begin());
215	}
216	while (disabled_checkers.begin() != disabled_checkers.end())
217	{
218		delete disabled_checkers.begin()->second;
219		disabled_checkers.erase(disabled_checkers.begin());
220	}
221}
222
223check_manager::check_manager()
224{
225	// NOTE: All checks listed here MUST have a corresponding line
226	// in the man page!
227	add_property_type_checker<property_value::STRING_LIST>(
228			"type-compatible", string("compatible"));
229	add_property_type_checker<property_value::STRING>(
230			"type-model", string("model"));
231	add_property_size_checker("type-phandle", string("phandle"), 4);
232	disabled_checkers.insert(std::make_pair(string("cells-attributes"),
233		new address_cells_checker("cells-attributes")));
234	checkers.insert(std::make_pair(string("deleted-nodes"),
235		new deleted_node_checker("deleted-nodes")));
236}
237
238bool
239check_manager::run_checks(device_tree *tree, bool keep_going)
240{
241	bool success = true;
242	for (auto &i : checkers)
243	{
244		success &= i.second->check_tree(tree);
245		if (!(success || keep_going))
246		{
247			break;
248		}
249	}
250	return success;
251}
252
253bool
254check_manager::disable_checker(const string &name)
255{
256	auto checker = checkers.find(name);
257	if (checker != checkers.end())
258	{
259		disabled_checkers.insert(std::make_pair(name,
260		                                        checker->second));
261		checkers.erase(checker);
262		return true;
263	}
264	return false;
265}
266
267bool
268check_manager::enable_checker(const string &name)
269{
270	auto checker = disabled_checkers.find(name);
271	if (checker != disabled_checkers.end())
272	{
273		checkers.insert(std::make_pair(name, checker->second));
274		disabled_checkers.erase(checker);
275		return true;
276	}
277	return false;
278}
279
280} // namespace checking
281
282} // namespace fdt
283
284} // namespace dtc
285
286