2089 lines · 54.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2007. */ #include "dtc.h"#include "srcpos.h" #ifdef TRACE_CHECKS#define TRACE(c, ...) \	do { \		fprintf(stderr, "=== %s: ", (c)->name); \		fprintf(stderr, __VA_ARGS__); \		fprintf(stderr, "\n"); \	} while (0)#else#define TRACE(c, fmt, ...)	do { } while (0)#endif enum checkstatus {	UNCHECKED = 0,	PREREQ,	PASSED,	FAILED,}; struct check; typedef void (*check_fn)(struct check *c, struct dt_info *dti, struct node *node); struct check {	const char *name;	check_fn fn;	const void *data;	bool warn, error;	enum checkstatus status;	bool inprogress;	int num_prereqs;	struct check **prereq;}; #define CHECK_ENTRY(nm_, fn_, d_, w_, e_, ...)	       \	static struct check *nm_##_prereqs[] = { __VA_ARGS__ }; \	static struct check nm_ = { \		.name = #nm_, \		.fn = (fn_), \		.data = (d_), \		.warn = (w_), \		.error = (e_), \		.status = UNCHECKED, \		.num_prereqs = ARRAY_SIZE(nm_##_prereqs), \		.prereq = nm_##_prereqs, \	};#define WARNING(nm_, fn_, d_, ...) \	CHECK_ENTRY(nm_, fn_, d_, true, false, __VA_ARGS__)#define ERROR(nm_, fn_, d_, ...) \	CHECK_ENTRY(nm_, fn_, d_, false, true, __VA_ARGS__)#define CHECK(nm_, fn_, d_, ...) \	CHECK_ENTRY(nm_, fn_, d_, false, false, __VA_ARGS__) static inline void  PRINTF(5, 6) check_msg(struct check *c, struct dt_info *dti,					   struct node *node,					   struct property *prop,					   const char *fmt, ...){	va_list ap;	char *str = NULL;	struct srcpos *pos = NULL;	char *file_str; 	if (!(c->warn && (quiet < 1)) && !(c->error && (quiet < 2)))		return; 	if (prop && prop->srcpos)		pos = prop->srcpos;	else if (node && node->srcpos)		pos = node->srcpos; 	if (pos) {		file_str = srcpos_string(pos);		xasprintf(&str, "%s", file_str);		free(file_str);	} else if (streq(dti->outname, "-")) {		xasprintf(&str, "<stdout>");	} else {		xasprintf(&str, "%s", dti->outname);	} 	xasprintf_append(&str, ": %s (%s): ",			(c->error) ? "ERROR" : "Warning", c->name); 	if (node) {		if (prop)			xasprintf_append(&str, "%s:%s: ", node->fullpath, prop->name);		else			xasprintf_append(&str, "%s: ", node->fullpath);	} 	va_start(ap, fmt);	xavsprintf_append(&str, fmt, ap);	va_end(ap); 	xasprintf_append(&str, "\n"); 	if (!prop && pos) {		pos = node->srcpos;		while (pos->next) {			pos = pos->next; 			file_str = srcpos_string(pos);			xasprintf_append(&str, "  also defined at %s\n", file_str);			free(file_str);		}	} 	fputs(str, stderr);	free(str);} #define FAIL(c, dti, node, ...)						\	do {								\		TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__);	\		(c)->status = FAILED;					\		check_msg((c), dti, node, NULL, __VA_ARGS__);		\	} while (0) #define FAIL_PROP(c, dti, node, prop, ...)				\	do {								\		TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__);	\		(c)->status = FAILED;					\		check_msg((c), dti, node, prop, __VA_ARGS__);		\	} while (0)  static void check_nodes_props(struct check *c, struct dt_info *dti, struct node *node){	struct node *child; 	TRACE(c, "%s", node->fullpath);	if (c->fn)		c->fn(c, dti, node); 	for_each_child(node, child)		check_nodes_props(c, dti, child);} static bool is_multiple_of(int multiple, int divisor){	if (divisor == 0)		return multiple == 0;	else		return (multiple % divisor) == 0;} static bool run_check(struct check *c, struct dt_info *dti){	struct node *dt = dti->dt;	bool error = false;	int i; 	assert(!c->inprogress); 	if (c->status != UNCHECKED)		goto out; 	c->inprogress = true; 	for (i = 0; i < c->num_prereqs; i++) {		struct check *prq = c->prereq[i];		error = error || run_check(prq, dti);		if (prq->status != PASSED) {			c->status = PREREQ;			check_msg(c, dti, NULL, NULL, "Failed prerequisite '%s'",				  c->prereq[i]->name);		}	} 	if (c->status != UNCHECKED)		goto out; 	check_nodes_props(c, dti, dt); 	if (c->status == UNCHECKED)		c->status = PASSED; 	TRACE(c, "\tCompleted, status %d", c->status); out:	c->inprogress = false;	if ((c->status != PASSED) && (c->error))		error = true;	return error;} /* * Utility check functions */ /* A check which always fails, for testing purposes only */static inline void check_always_fail(struct check *c, struct dt_info *dti,				     struct node *node){	FAIL(c, dti, node, "always_fail check");}CHECK(always_fail, check_always_fail, NULL); static void check_is_string(struct check *c, struct dt_info *dti,			    struct node *node){	struct property *prop;	const char *propname = c->data; 	prop = get_property(node, propname);	if (!prop)		return; /* Not present, assumed ok */ 	if (!data_is_one_string(prop->val))		FAIL_PROP(c, dti, node, prop, "property is not a string");}#define WARNING_IF_NOT_STRING(nm, propname) \	WARNING(nm, check_is_string, (propname))#define ERROR_IF_NOT_STRING(nm, propname) \	ERROR(nm, check_is_string, (propname)) static void check_is_string_list(struct check *c, struct dt_info *dti,				 struct node *node){	int rem, l;	struct property *prop;	const char *propname = c->data;	char *str; 	prop = get_property(node, propname);	if (!prop)		return; /* Not present, assumed ok */ 	str = prop->val.val;	rem = prop->val.len;	while (rem > 0) {		l = strnlen(str, rem);		if (l == rem) {			FAIL_PROP(c, dti, node, prop, "property is not a string list");			break;		}		rem -= l + 1;		str += l + 1;	}}#define WARNING_IF_NOT_STRING_LIST(nm, propname) \	WARNING(nm, check_is_string_list, (propname))#define ERROR_IF_NOT_STRING_LIST(nm, propname) \	ERROR(nm, check_is_string_list, (propname)) static void check_is_cell(struct check *c, struct dt_info *dti,			  struct node *node){	struct property *prop;	const char *propname = c->data; 	prop = get_property(node, propname);	if (!prop)		return; /* Not present, assumed ok */ 	if (prop->val.len != sizeof(cell_t))		FAIL_PROP(c, dti, node, prop, "property is not a single cell");}#define WARNING_IF_NOT_CELL(nm, propname) \	WARNING(nm, check_is_cell, (propname))#define ERROR_IF_NOT_CELL(nm, propname) \	ERROR(nm, check_is_cell, (propname)) /* * Structural check functions */ static void check_duplicate_node_names(struct check *c, struct dt_info *dti,				       struct node *node){	struct node *child, *child2; 	for_each_child(node, child)		for (child2 = child->next_sibling;		     child2;		     child2 = child2->next_sibling)			if (streq(child->name, child2->name))				FAIL(c, dti, child2, "Duplicate node name");}ERROR(duplicate_node_names, check_duplicate_node_names, NULL); static void check_duplicate_property_names(struct check *c, struct dt_info *dti,					   struct node *node){	struct property *prop, *prop2; 	for_each_property(node, prop) {		for (prop2 = prop->next; prop2; prop2 = prop2->next) {			if (prop2->deleted)				continue;			if (streq(prop->name, prop2->name))				FAIL_PROP(c, dti, node, prop, "Duplicate property name");		}	}}ERROR(duplicate_property_names, check_duplicate_property_names, NULL); #define LOWERCASE	"abcdefghijklmnopqrstuvwxyz"#define UPPERCASE	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"#define DIGITS		"0123456789"#define NODECHARS	LOWERCASE UPPERCASE DIGITS ",._+-@"#define PROPCHARS	LOWERCASE UPPERCASE DIGITS ",._+*#?-"#define PROPNODECHARSSTRICT	LOWERCASE UPPERCASE DIGITS ",-" static void check_node_name_chars(struct check *c, struct dt_info *dti,				  struct node *node){	size_t n = strspn(node->name, c->data); 	if (n < strlen(node->name))		FAIL(c, dti, node, "Bad character '%c' in node name",		     node->name[n]);}ERROR(node_name_chars, check_node_name_chars, NODECHARS); static void check_node_name_chars_strict(struct check *c, struct dt_info *dti,					 struct node *node){	int n = strspn(node->name, c->data); 	if (n < node->basenamelen)		FAIL(c, dti, node, "Character '%c' not recommended in node name",		     node->name[n]);}CHECK(node_name_chars_strict, check_node_name_chars_strict, PROPNODECHARSSTRICT); static void check_node_name_format(struct check *c, struct dt_info *dti,				   struct node *node){	if (strchr(get_unitname(node), '@'))		FAIL(c, dti, node, "multiple '@' characters in node name");}ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars); static void check_node_name_vs_property_name(struct check *c,					     struct dt_info *dti,					     struct node *node){	if (!node->parent)		return; 	if (get_property(node->parent, node->name)) {		FAIL(c, dti, node, "node name and property name conflict");	}}WARNING(node_name_vs_property_name, check_node_name_vs_property_name,	NULL, &node_name_chars); static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti,				      struct node *node){	const char *unitname = get_unitname(node);	struct property *prop = get_property(node, "reg"); 	if (get_subnode(node, "__overlay__")) {		/* HACK: Overlay fragments are a special case */		return;	} 	if (!prop) {		prop = get_property(node, "ranges");		if (prop && !prop->val.len)			prop = NULL;	} 	if (prop) {		if (!unitname[0])			FAIL(c, dti, node, "node has a reg or ranges property, but no unit name");	} else {		if (unitname[0])			FAIL(c, dti, node, "node has a unit name, but no reg or ranges property");	}}WARNING(unit_address_vs_reg, check_unit_address_vs_reg, NULL); static void check_property_name_chars(struct check *c, struct dt_info *dti,				      struct node *node){	struct property *prop; 	for_each_property(node, prop) {		size_t n = strspn(prop->name, c->data); 		if (n < strlen(prop->name))			FAIL_PROP(c, dti, node, prop, "Bad character '%c' in property name",				  prop->name[n]);	}}ERROR(property_name_chars, check_property_name_chars, PROPCHARS); static void check_property_name_chars_strict(struct check *c,					     struct dt_info *dti,					     struct node *node){	struct property *prop; 	for_each_property(node, prop) {		const char *name = prop->name;		size_t n = strspn(name, c->data); 		if (n == strlen(prop->name))			continue; 		/* Certain names are whitelisted */		if (streq(name, "device_type"))			continue; 		/*		 * # is only allowed at the beginning of property names not counting		 * the vendor prefix.		 */		if (name[n] == '#' && ((n == 0) || (name[n-1] == ','))) {			name += n + 1;			n = strspn(name, c->data);		}		if (n < strlen(name))			FAIL_PROP(c, dti, node, prop, "Character '%c' not recommended in property name",				  name[n]);	}}CHECK(property_name_chars_strict, check_property_name_chars_strict, PROPNODECHARSSTRICT); #define DESCLABEL_FMT	"%s%s%s%s%s"#define DESCLABEL_ARGS(node,prop,mark)		\	((mark) ? "value of " : ""),		\	((prop) ? "'" : ""), \	((prop) ? (prop)->name : ""), \	((prop) ? "' in " : ""), (node)->fullpath static void check_duplicate_label(struct check *c, struct dt_info *dti,				  const char *label, struct node *node,				  struct property *prop, struct marker *mark){	struct node *dt = dti->dt;	struct node *othernode = NULL;	struct property *otherprop = NULL;	struct marker *othermark = NULL; 	othernode = get_node_by_label(dt, label); 	if (!othernode)		otherprop = get_property_by_label(dt, label, &othernode);	if (!othernode)		othermark = get_marker_label(dt, label, &othernode,					       &otherprop); 	if (!othernode)		return; 	if ((othernode != node) || (otherprop != prop) || (othermark != mark))		FAIL(c, dti, node, "Duplicate label '%s' on " DESCLABEL_FMT		     " and " DESCLABEL_FMT,		     label, DESCLABEL_ARGS(node, prop, mark),		     DESCLABEL_ARGS(othernode, otherprop, othermark));} static void check_duplicate_label_node(struct check *c, struct dt_info *dti,				       struct node *node){	struct label *l;	struct property *prop; 	for_each_label(node->labels, l)		check_duplicate_label(c, dti, l->label, node, NULL, NULL); 	for_each_property(node, prop) {		struct marker *m = prop->val.markers; 		for_each_label(prop->labels, l)			check_duplicate_label(c, dti, l->label, node, prop, NULL); 		for_each_marker_of_type(m, LABEL)			check_duplicate_label(c, dti, m->ref, node, prop, m);	}}ERROR(duplicate_label, check_duplicate_label_node, NULL); static cell_t check_phandle_prop(struct check *c, struct dt_info *dti,				 struct node *node, const char *propname){	struct node *root = dti->dt;	struct property *prop;	struct marker *m;	cell_t phandle; 	prop = get_property(node, propname);	if (!prop)		return 0; 	if (prop->val.len != sizeof(cell_t)) {		FAIL_PROP(c, dti, node, prop, "bad length (%d) %s property",			  prop->val.len, prop->name);

Lines 1–500 of 2089.