checks.c revision 204431
1/*
2 * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2007.
3 *
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program; if not, write to the Free Software
17 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
18 *                                                                   USA
19 */
20
21#include "dtc.h"
22
23#ifdef TRACE_CHECKS
24#define TRACE(c, ...) \
25	do { \
26		fprintf(stderr, "=== %s: ", (c)->name); \
27		fprintf(stderr, __VA_ARGS__); \
28		fprintf(stderr, "\n"); \
29	} while (0)
30#else
31#define TRACE(c, fmt, ...)	do { } while (0)
32#endif
33
34enum checklevel {
35	IGNORE = 0,
36	WARN = 1,
37	ERROR = 2,
38};
39
40enum checkstatus {
41	UNCHECKED = 0,
42	PREREQ,
43	PASSED,
44	FAILED,
45};
46
47struct check;
48
49typedef void (*tree_check_fn)(struct check *c, struct node *dt);
50typedef void (*node_check_fn)(struct check *c, struct node *dt, struct node *node);
51typedef void (*prop_check_fn)(struct check *c, struct node *dt,
52			      struct node *node, struct property *prop);
53
54struct check {
55	const char *name;
56	tree_check_fn tree_fn;
57	node_check_fn node_fn;
58	prop_check_fn prop_fn;
59	void *data;
60	enum checklevel level;
61	enum checkstatus status;
62	int inprogress;
63	int num_prereqs;
64	struct check **prereq;
65};
66
67#define CHECK(nm, tfn, nfn, pfn, d, lvl, ...) \
68	static struct check *nm##_prereqs[] = { __VA_ARGS__ }; \
69	static struct check nm = { \
70		.name = #nm, \
71		.tree_fn = (tfn), \
72		.node_fn = (nfn), \
73		.prop_fn = (pfn), \
74		.data = (d), \
75		.level = (lvl), \
76		.status = UNCHECKED, \
77		.num_prereqs = ARRAY_SIZE(nm##_prereqs), \
78		.prereq = nm##_prereqs, \
79	};
80
81#define TREE_CHECK(nm, d, lvl, ...) \
82	CHECK(nm, check_##nm, NULL, NULL, d, lvl, __VA_ARGS__)
83#define NODE_CHECK(nm, d, lvl, ...) \
84	CHECK(nm, NULL, check_##nm, NULL, d, lvl, __VA_ARGS__)
85#define PROP_CHECK(nm, d, lvl, ...) \
86	CHECK(nm, NULL, NULL, check_##nm, d, lvl, __VA_ARGS__)
87#define BATCH_CHECK(nm, lvl, ...) \
88	CHECK(nm, NULL, NULL, NULL, NULL, lvl, __VA_ARGS__)
89
90#ifdef __GNUC__
91static inline void check_msg(struct check *c, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
92#endif
93static inline void check_msg(struct check *c, const char *fmt, ...)
94{
95	va_list ap;
96	va_start(ap, fmt);
97
98	if ((c->level < WARN) || (c->level <= quiet))
99		return; /* Suppress message */
100
101	fprintf(stderr, "%s (%s): ",
102		(c->level == ERROR) ? "ERROR" : "Warning", c->name);
103	vfprintf(stderr, fmt, ap);
104	fprintf(stderr, "\n");
105}
106
107#define FAIL(c, ...) \
108	do { \
109		TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__); \
110		(c)->status = FAILED; \
111		check_msg((c), __VA_ARGS__); \
112	} while (0)
113
114static void check_nodes_props(struct check *c, struct node *dt, struct node *node)
115{
116	struct node *child;
117	struct property *prop;
118
119	TRACE(c, "%s", node->fullpath);
120	if (c->node_fn)
121		c->node_fn(c, dt, node);
122
123	if (c->prop_fn)
124		for_each_property(node, prop) {
125			TRACE(c, "%s\t'%s'", node->fullpath, prop->name);
126			c->prop_fn(c, dt, node, prop);
127		}
128
129	for_each_child(node, child)
130		check_nodes_props(c, dt, child);
131}
132
133static int run_check(struct check *c, struct node *dt)
134{
135	int error = 0;
136	int i;
137
138	assert(!c->inprogress);
139
140	if (c->status != UNCHECKED)
141		goto out;
142
143	c->inprogress = 1;
144
145	for (i = 0; i < c->num_prereqs; i++) {
146		struct check *prq = c->prereq[i];
147		error |= run_check(prq, dt);
148		if (prq->status != PASSED) {
149			c->status = PREREQ;
150			check_msg(c, "Failed prerequisite '%s'",
151				  c->prereq[i]->name);
152		}
153	}
154
155	if (c->status != UNCHECKED)
156		goto out;
157
158	if (c->node_fn || c->prop_fn)
159		check_nodes_props(c, dt, dt);
160
161	if (c->tree_fn)
162		c->tree_fn(c, dt);
163	if (c->status == UNCHECKED)
164		c->status = PASSED;
165
166	TRACE(c, "\tCompleted, status %d", c->status);
167
168out:
169	c->inprogress = 0;
170	if ((c->status != PASSED) && (c->level == ERROR))
171		error = 1;
172	return error;
173}
174
175/*
176 * Utility check functions
177 */
178
179static void check_is_string(struct check *c, struct node *root,
180			    struct node *node)
181{
182	struct property *prop;
183	char *propname = c->data;
184
185	prop = get_property(node, propname);
186	if (!prop)
187		return; /* Not present, assumed ok */
188
189	if (!data_is_one_string(prop->val))
190		FAIL(c, "\"%s\" property in %s is not a string",
191		     propname, node->fullpath);
192}
193#define CHECK_IS_STRING(nm, propname, lvl) \
194	CHECK(nm, NULL, check_is_string, NULL, (propname), (lvl))
195
196static void check_is_cell(struct check *c, struct node *root,
197			  struct node *node)
198{
199	struct property *prop;
200	char *propname = c->data;
201
202	prop = get_property(node, propname);
203	if (!prop)
204		return; /* Not present, assumed ok */
205
206	if (prop->val.len != sizeof(cell_t))
207		FAIL(c, "\"%s\" property in %s is not a single cell",
208		     propname, node->fullpath);
209}
210#define CHECK_IS_CELL(nm, propname, lvl) \
211	CHECK(nm, NULL, check_is_cell, NULL, (propname), (lvl))
212
213/*
214 * Structural check functions
215 */
216
217static void check_duplicate_node_names(struct check *c, struct node *dt,
218				       struct node *node)
219{
220	struct node *child, *child2;
221
222	for_each_child(node, child)
223		for (child2 = child->next_sibling;
224		     child2;
225		     child2 = child2->next_sibling)
226			if (streq(child->name, child2->name))
227				FAIL(c, "Duplicate node name %s",
228				     child->fullpath);
229}
230NODE_CHECK(duplicate_node_names, NULL, ERROR);
231
232static void check_duplicate_property_names(struct check *c, struct node *dt,
233					   struct node *node)
234{
235	struct property *prop, *prop2;
236
237	for_each_property(node, prop)
238		for (prop2 = prop->next; prop2; prop2 = prop2->next)
239			if (streq(prop->name, prop2->name))
240				FAIL(c, "Duplicate property name %s in %s",
241				     prop->name, node->fullpath);
242}
243NODE_CHECK(duplicate_property_names, NULL, ERROR);
244
245#define LOWERCASE	"abcdefghijklmnopqrstuvwxyz"
246#define UPPERCASE	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
247#define DIGITS		"0123456789"
248#define PROPNODECHARS	LOWERCASE UPPERCASE DIGITS ",._+*#?-"
249
250static void check_node_name_chars(struct check *c, struct node *dt,
251				  struct node *node)
252{
253	int n = strspn(node->name, c->data);
254
255	if (n < strlen(node->name))
256		FAIL(c, "Bad character '%c' in node %s",
257		     node->name[n], node->fullpath);
258}
259NODE_CHECK(node_name_chars, PROPNODECHARS "@", ERROR);
260
261static void check_node_name_format(struct check *c, struct node *dt,
262				   struct node *node)
263{
264	if (strchr(get_unitname(node), '@'))
265		FAIL(c, "Node %s has multiple '@' characters in name",
266		     node->fullpath);
267}
268NODE_CHECK(node_name_format, NULL, ERROR, &node_name_chars);
269
270static void check_property_name_chars(struct check *c, struct node *dt,
271				      struct node *node, struct property *prop)
272{
273	int n = strspn(prop->name, c->data);
274
275	if (n < strlen(prop->name))
276		FAIL(c, "Bad character '%c' in property name \"%s\", node %s",
277		     prop->name[n], prop->name, node->fullpath);
278}
279PROP_CHECK(property_name_chars, PROPNODECHARS, ERROR);
280
281static void check_explicit_phandles(struct check *c, struct node *root,
282					  struct node *node)
283{
284	struct property *prop;
285	struct node *other;
286	cell_t phandle;
287
288	prop = get_property(node, "linux,phandle");
289	if (! prop)
290		return; /* No phandle, that's fine */
291
292	if (prop->val.len != sizeof(cell_t)) {
293		FAIL(c, "%s has bad length (%d) linux,phandle property",
294		     node->fullpath, prop->val.len);
295		return;
296	}
297
298	phandle = propval_cell(prop);
299	if ((phandle == 0) || (phandle == -1)) {
300		FAIL(c, "%s has invalid linux,phandle value 0x%x",
301		     node->fullpath, phandle);
302		return;
303	}
304
305	other = get_node_by_phandle(root, phandle);
306	if (other) {
307		FAIL(c, "%s has duplicated phandle 0x%x (seen before at %s)",
308		     node->fullpath, phandle, other->fullpath);
309		return;
310	}
311
312	node->phandle = phandle;
313}
314NODE_CHECK(explicit_phandles, NULL, ERROR);
315
316static void check_name_properties(struct check *c, struct node *root,
317				  struct node *node)
318{
319	struct property **pp, *prop = NULL;
320
321	for (pp = &node->proplist; *pp; pp = &((*pp)->next))
322		if (streq((*pp)->name, "name")) {
323			prop = *pp;
324			break;
325		}
326
327	if (!prop)
328		return; /* No name property, that's fine */
329
330	if ((prop->val.len != node->basenamelen+1)
331	    || (memcmp(prop->val.val, node->name, node->basenamelen) != 0)) {
332		FAIL(c, "\"name\" property in %s is incorrect (\"%s\" instead"
333		     " of base node name)", node->fullpath, prop->val.val);
334	} else {
335		/* The name property is correct, and therefore redundant.
336		 * Delete it */
337		*pp = prop->next;
338		free(prop->name);
339		data_free(prop->val);
340		free(prop);
341	}
342}
343CHECK_IS_STRING(name_is_string, "name", ERROR);
344NODE_CHECK(name_properties, NULL, ERROR, &name_is_string);
345
346/*
347 * Reference fixup functions
348 */
349
350static void fixup_phandle_references(struct check *c, struct node *dt,
351				     struct node *node, struct property *prop)
352{
353	struct marker *m = prop->val.markers;
354	struct node *refnode;
355	cell_t phandle;
356
357	for_each_marker_of_type(m, REF_PHANDLE) {
358		assert(m->offset + sizeof(cell_t) <= prop->val.len);
359
360		refnode = get_node_by_ref(dt, m->ref);
361		if (! refnode) {
362			FAIL(c, "Reference to non-existent node or label \"%s\"\n",
363			     m->ref);
364			continue;
365		}
366
367		phandle = get_node_phandle(dt, refnode);
368		*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
369	}
370}
371CHECK(phandle_references, NULL, NULL, fixup_phandle_references, NULL, ERROR,
372      &duplicate_node_names, &explicit_phandles);
373
374static void fixup_path_references(struct check *c, struct node *dt,
375				  struct node *node, struct property *prop)
376{
377	struct marker *m = prop->val.markers;
378	struct node *refnode;
379	char *path;
380
381	for_each_marker_of_type(m, REF_PATH) {
382		assert(m->offset <= prop->val.len);
383
384		refnode = get_node_by_ref(dt, m->ref);
385		if (!refnode) {
386			FAIL(c, "Reference to non-existent node or label \"%s\"\n",
387			     m->ref);
388			continue;
389		}
390
391		path = refnode->fullpath;
392		prop->val = data_insert_at_marker(prop->val, m, path,
393						  strlen(path) + 1);
394	}
395}
396CHECK(path_references, NULL, NULL, fixup_path_references, NULL, ERROR,
397      &duplicate_node_names);
398
399/*
400 * Semantic checks
401 */
402CHECK_IS_CELL(address_cells_is_cell, "#address-cells", WARN);
403CHECK_IS_CELL(size_cells_is_cell, "#size-cells", WARN);
404CHECK_IS_CELL(interrupt_cells_is_cell, "#interrupt-cells", WARN);
405
406CHECK_IS_STRING(device_type_is_string, "device_type", WARN);
407CHECK_IS_STRING(model_is_string, "model", WARN);
408CHECK_IS_STRING(status_is_string, "status", WARN);
409
410static void fixup_addr_size_cells(struct check *c, struct node *dt,
411				  struct node *node)
412{
413	struct property *prop;
414
415	node->addr_cells = -1;
416	node->size_cells = -1;
417
418	prop = get_property(node, "#address-cells");
419	if (prop)
420		node->addr_cells = propval_cell(prop);
421
422	prop = get_property(node, "#size-cells");
423	if (prop)
424		node->size_cells = propval_cell(prop);
425}
426CHECK(addr_size_cells, NULL, fixup_addr_size_cells, NULL, NULL, WARN,
427      &address_cells_is_cell, &size_cells_is_cell);
428
429#define node_addr_cells(n) \
430	(((n)->addr_cells == -1) ? 2 : (n)->addr_cells)
431#define node_size_cells(n) \
432	(((n)->size_cells == -1) ? 1 : (n)->size_cells)
433
434static void check_reg_format(struct check *c, struct node *dt,
435			     struct node *node)
436{
437	struct property *prop;
438	int addr_cells, size_cells, entrylen;
439
440	prop = get_property(node, "reg");
441	if (!prop)
442		return; /* No "reg", that's fine */
443
444	if (!node->parent) {
445		FAIL(c, "Root node has a \"reg\" property");
446		return;
447	}
448
449	if (prop->val.len == 0)
450		FAIL(c, "\"reg\" property in %s is empty", node->fullpath);
451
452	addr_cells = node_addr_cells(node->parent);
453	size_cells = node_size_cells(node->parent);
454	entrylen = (addr_cells + size_cells) * sizeof(cell_t);
455
456	if ((prop->val.len % entrylen) != 0)
457		FAIL(c, "\"reg\" property in %s has invalid length (%d bytes) "
458		     "(#address-cells == %d, #size-cells == %d)",
459		     node->fullpath, prop->val.len, addr_cells, size_cells);
460}
461NODE_CHECK(reg_format, NULL, WARN, &addr_size_cells);
462
463static void check_ranges_format(struct check *c, struct node *dt,
464				struct node *node)
465{
466	struct property *prop;
467	int c_addr_cells, p_addr_cells, c_size_cells, p_size_cells, entrylen;
468
469	prop = get_property(node, "ranges");
470	if (!prop)
471		return;
472
473	if (!node->parent) {
474		FAIL(c, "Root node has a \"ranges\" property");
475		return;
476	}
477
478	p_addr_cells = node_addr_cells(node->parent);
479	p_size_cells = node_size_cells(node->parent);
480	c_addr_cells = node_addr_cells(node);
481	c_size_cells = node_size_cells(node);
482	entrylen = (p_addr_cells + c_addr_cells + c_size_cells) * sizeof(cell_t);
483
484	if (prop->val.len == 0) {
485		if (p_addr_cells != c_addr_cells)
486			FAIL(c, "%s has empty \"ranges\" property but its "
487			     "#address-cells (%d) differs from %s (%d)",
488			     node->fullpath, c_addr_cells, node->parent->fullpath,
489			     p_addr_cells);
490		if (p_size_cells != c_size_cells)
491			FAIL(c, "%s has empty \"ranges\" property but its "
492			     "#size-cells (%d) differs from %s (%d)",
493			     node->fullpath, c_size_cells, node->parent->fullpath,
494			     p_size_cells);
495	} else if ((prop->val.len % entrylen) != 0) {
496		FAIL(c, "\"ranges\" property in %s has invalid length (%d bytes) "
497		     "(parent #address-cells == %d, child #address-cells == %d, "
498		     "#size-cells == %d)", node->fullpath, prop->val.len,
499		     p_addr_cells, c_addr_cells, c_size_cells);
500	}
501}
502NODE_CHECK(ranges_format, NULL, WARN, &addr_size_cells);
503
504/*
505 * Style checks
506 */
507static void check_avoid_default_addr_size(struct check *c, struct node *dt,
508					  struct node *node)
509{
510	struct property *reg, *ranges;
511
512	if (!node->parent)
513		return; /* Ignore root node */
514
515	reg = get_property(node, "reg");
516	ranges = get_property(node, "ranges");
517
518	if (!reg && !ranges)
519		return;
520
521	if ((node->parent->addr_cells == -1))
522		FAIL(c, "Relying on default #address-cells value for %s",
523		     node->fullpath);
524
525	if ((node->parent->size_cells == -1))
526		FAIL(c, "Relying on default #size-cells value for %s",
527		     node->fullpath);
528}
529NODE_CHECK(avoid_default_addr_size, NULL, WARN, &addr_size_cells);
530
531static void check_obsolete_chosen_interrupt_controller(struct check *c,
532						       struct node *dt)
533{
534	struct node *chosen;
535	struct property *prop;
536
537	chosen = get_node_by_path(dt, "/chosen");
538	if (!chosen)
539		return;
540
541	prop = get_property(chosen, "interrupt-controller");
542	if (prop)
543		FAIL(c, "/chosen has obsolete \"interrupt-controller\" "
544		     "property");
545}
546TREE_CHECK(obsolete_chosen_interrupt_controller, NULL, WARN);
547
548static struct check *check_table[] = {
549	&duplicate_node_names, &duplicate_property_names,
550	&node_name_chars, &node_name_format, &property_name_chars,
551	&name_is_string, &name_properties,
552	&explicit_phandles,
553	&phandle_references, &path_references,
554
555	&address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell,
556	&device_type_is_string, &model_is_string, &status_is_string,
557
558	&addr_size_cells, &reg_format, &ranges_format,
559
560	&avoid_default_addr_size,
561	&obsolete_chosen_interrupt_controller,
562};
563
564void process_checks(int force, struct boot_info *bi)
565{
566	struct node *dt = bi->dt;
567	int i;
568	int error = 0;
569
570	for (i = 0; i < ARRAY_SIZE(check_table); i++) {
571		struct check *c = check_table[i];
572
573		if (c->level != IGNORE)
574			error = error || run_check(c, dt);
575	}
576
577	if (error) {
578		if (!force) {
579			fprintf(stderr, "ERROR: Input tree has errors, aborting "
580				"(use -f to force output)\n");
581			exit(2);
582		} else if (quiet < 3) {
583			fprintf(stderr, "Warning: Input tree has errors, "
584				"output forced\n");
585		}
586	}
587}
588