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