Commit | Line | Data |
---|---|---|
a4da2e3e DG |
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 | ||
a4da2e3e DG |
34 | enum checkstatus { |
35 | UNCHECKED = 0, | |
36 | PREREQ, | |
37 | PASSED, | |
38 | FAILED, | |
39 | }; | |
40 | ||
41 | struct check; | |
42 | ||
6f05afcb | 43 | typedef void (*check_fn)(struct check *c, struct dt_info *dti, struct node *node); |
a4da2e3e DG |
44 | |
45 | struct check { | |
46 | const char *name; | |
6f05afcb | 47 | check_fn fn; |
a4da2e3e | 48 | void *data; |
cd296721 | 49 | bool warn, error; |
a4da2e3e | 50 | enum checkstatus status; |
47605971 | 51 | bool inprogress; |
a4da2e3e DG |
52 | int num_prereqs; |
53 | struct check **prereq; | |
54 | }; | |
55 | ||
6f05afcb RH |
56 | #define CHECK_ENTRY(_nm, _fn, _d, _w, _e, ...) \ |
57 | static struct check *_nm##_prereqs[] = { __VA_ARGS__ }; \ | |
58 | static struct check _nm = { \ | |
59 | .name = #_nm, \ | |
60 | .fn = (_fn), \ | |
61 | .data = (_d), \ | |
62 | .warn = (_w), \ | |
63 | .error = (_e), \ | |
a4da2e3e | 64 | .status = UNCHECKED, \ |
6f05afcb RH |
65 | .num_prereqs = ARRAY_SIZE(_nm##_prereqs), \ |
66 | .prereq = _nm##_prereqs, \ | |
a4da2e3e | 67 | }; |
6f05afcb RH |
68 | #define WARNING(_nm, _fn, _d, ...) \ |
69 | CHECK_ENTRY(_nm, _fn, _d, true, false, __VA_ARGS__) | |
70 | #define ERROR(_nm, _fn, _d, ...) \ | |
71 | CHECK_ENTRY(_nm, _fn, _d, false, true, __VA_ARGS__) | |
72 | #define CHECK(_nm, _fn, _d, ...) \ | |
73 | CHECK_ENTRY(_nm, _fn, _d, false, false, __VA_ARGS__) | |
a4da2e3e DG |
74 | |
75 | #ifdef __GNUC__ | |
76 | static inline void check_msg(struct check *c, const char *fmt, ...) __attribute__((format (printf, 2, 3))); | |
77 | #endif | |
78 | static inline void check_msg(struct check *c, const char *fmt, ...) | |
79 | { | |
80 | va_list ap; | |
81 | va_start(ap, fmt); | |
82 | ||
cd296721 SW |
83 | if ((c->warn && (quiet < 1)) |
84 | || (c->error && (quiet < 2))) { | |
85 | fprintf(stderr, "%s (%s): ", | |
86 | (c->error) ? "ERROR" : "Warning", c->name); | |
87 | vfprintf(stderr, fmt, ap); | |
88 | fprintf(stderr, "\n"); | |
89 | } | |
47605971 | 90 | va_end(ap); |
a4da2e3e DG |
91 | } |
92 | ||
93 | #define FAIL(c, ...) \ | |
94 | do { \ | |
95 | TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__); \ | |
96 | (c)->status = FAILED; \ | |
97 | check_msg((c), __VA_ARGS__); \ | |
98 | } while (0) | |
99 | ||
6f05afcb | 100 | static void check_nodes_props(struct check *c, struct dt_info *dti, struct node *node) |
a4da2e3e DG |
101 | { |
102 | struct node *child; | |
a4da2e3e DG |
103 | |
104 | TRACE(c, "%s", node->fullpath); | |
6f05afcb RH |
105 | if (c->fn) |
106 | c->fn(c, dti, node); | |
a4da2e3e DG |
107 | |
108 | for_each_child(node, child) | |
6f05afcb | 109 | check_nodes_props(c, dti, child); |
a4da2e3e DG |
110 | } |
111 | ||
6f05afcb | 112 | static bool run_check(struct check *c, struct dt_info *dti) |
a4da2e3e | 113 | { |
6f05afcb | 114 | struct node *dt = dti->dt; |
47605971 | 115 | bool error = false; |
a4da2e3e DG |
116 | int i; |
117 | ||
118 | assert(!c->inprogress); | |
119 | ||
120 | if (c->status != UNCHECKED) | |
121 | goto out; | |
122 | ||
47605971 | 123 | c->inprogress = true; |
a4da2e3e DG |
124 | |
125 | for (i = 0; i < c->num_prereqs; i++) { | |
126 | struct check *prq = c->prereq[i]; | |
6f05afcb | 127 | error = error || run_check(prq, dti); |
a4da2e3e DG |
128 | if (prq->status != PASSED) { |
129 | c->status = PREREQ; | |
130 | check_msg(c, "Failed prerequisite '%s'", | |
131 | c->prereq[i]->name); | |
132 | } | |
133 | } | |
134 | ||
135 | if (c->status != UNCHECKED) | |
136 | goto out; | |
137 | ||
6f05afcb | 138 | check_nodes_props(c, dti, dt); |
a4da2e3e | 139 | |
a4da2e3e DG |
140 | if (c->status == UNCHECKED) |
141 | c->status = PASSED; | |
142 | ||
143 | TRACE(c, "\tCompleted, status %d", c->status); | |
144 | ||
145 | out: | |
47605971 | 146 | c->inprogress = false; |
cd296721 | 147 | if ((c->status != PASSED) && (c->error)) |
47605971 | 148 | error = true; |
a4da2e3e DG |
149 | return error; |
150 | } | |
151 | ||
152 | /* | |
153 | * Utility check functions | |
154 | */ | |
155 | ||
cd296721 | 156 | /* A check which always fails, for testing purposes only */ |
6f05afcb RH |
157 | static inline void check_always_fail(struct check *c, struct dt_info *dti, |
158 | struct node *node) | |
cd296721 SW |
159 | { |
160 | FAIL(c, "always_fail check"); | |
161 | } | |
6f05afcb | 162 | CHECK(always_fail, check_always_fail, NULL); |
cd296721 | 163 | |
6f05afcb | 164 | static void check_is_string(struct check *c, struct dt_info *dti, |
a4da2e3e DG |
165 | struct node *node) |
166 | { | |
167 | struct property *prop; | |
168 | char *propname = c->data; | |
169 | ||
170 | prop = get_property(node, propname); | |
171 | if (!prop) | |
172 | return; /* Not present, assumed ok */ | |
173 | ||
174 | if (!data_is_one_string(prop->val)) | |
175 | FAIL(c, "\"%s\" property in %s is not a string", | |
176 | propname, node->fullpath); | |
177 | } | |
cd296721 | 178 | #define WARNING_IF_NOT_STRING(nm, propname) \ |
6f05afcb | 179 | WARNING(nm, check_is_string, (propname)) |
cd296721 | 180 | #define ERROR_IF_NOT_STRING(nm, propname) \ |
6f05afcb | 181 | ERROR(nm, check_is_string, (propname)) |
a4da2e3e | 182 | |
6f05afcb | 183 | static void check_is_cell(struct check *c, struct dt_info *dti, |
a4da2e3e DG |
184 | struct node *node) |
185 | { | |
186 | struct property *prop; | |
187 | char *propname = c->data; | |
188 | ||
189 | prop = get_property(node, propname); | |
190 | if (!prop) | |
191 | return; /* Not present, assumed ok */ | |
192 | ||
193 | if (prop->val.len != sizeof(cell_t)) | |
194 | FAIL(c, "\"%s\" property in %s is not a single cell", | |
195 | propname, node->fullpath); | |
196 | } | |
cd296721 | 197 | #define WARNING_IF_NOT_CELL(nm, propname) \ |
6f05afcb | 198 | WARNING(nm, check_is_cell, (propname)) |
cd296721 | 199 | #define ERROR_IF_NOT_CELL(nm, propname) \ |
6f05afcb | 200 | ERROR(nm, check_is_cell, (propname)) |
a4da2e3e DG |
201 | |
202 | /* | |
203 | * Structural check functions | |
204 | */ | |
205 | ||
6f05afcb | 206 | static void check_duplicate_node_names(struct check *c, struct dt_info *dti, |
a4da2e3e DG |
207 | struct node *node) |
208 | { | |
209 | struct node *child, *child2; | |
210 | ||
211 | for_each_child(node, child) | |
212 | for (child2 = child->next_sibling; | |
213 | child2; | |
214 | child2 = child2->next_sibling) | |
215 | if (streq(child->name, child2->name)) | |
216 | FAIL(c, "Duplicate node name %s", | |
217 | child->fullpath); | |
218 | } | |
6f05afcb | 219 | ERROR(duplicate_node_names, check_duplicate_node_names, NULL); |
a4da2e3e | 220 | |
6f05afcb | 221 | static void check_duplicate_property_names(struct check *c, struct dt_info *dti, |
a4da2e3e DG |
222 | struct node *node) |
223 | { | |
224 | struct property *prop, *prop2; | |
225 | ||
cd296721 SW |
226 | for_each_property(node, prop) { |
227 | for (prop2 = prop->next; prop2; prop2 = prop2->next) { | |
228 | if (prop2->deleted) | |
229 | continue; | |
a4da2e3e DG |
230 | if (streq(prop->name, prop2->name)) |
231 | FAIL(c, "Duplicate property name %s in %s", | |
232 | prop->name, node->fullpath); | |
cd296721 SW |
233 | } |
234 | } | |
a4da2e3e | 235 | } |
6f05afcb | 236 | ERROR(duplicate_property_names, check_duplicate_property_names, NULL); |
a4da2e3e | 237 | |
ed95d745 DG |
238 | #define LOWERCASE "abcdefghijklmnopqrstuvwxyz" |
239 | #define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
240 | #define DIGITS "0123456789" | |
241 | #define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-" | |
242 | ||
6f05afcb | 243 | static void check_node_name_chars(struct check *c, struct dt_info *dti, |
ed95d745 DG |
244 | struct node *node) |
245 | { | |
246 | int n = strspn(node->name, c->data); | |
247 | ||
248 | if (n < strlen(node->name)) | |
249 | FAIL(c, "Bad character '%c' in node %s", | |
250 | node->name[n], node->fullpath); | |
251 | } | |
6f05afcb | 252 | ERROR(node_name_chars, check_node_name_chars, PROPNODECHARS "@"); |
ed95d745 | 253 | |
6f05afcb | 254 | static void check_node_name_format(struct check *c, struct dt_info *dti, |
ed95d745 DG |
255 | struct node *node) |
256 | { | |
257 | if (strchr(get_unitname(node), '@')) | |
258 | FAIL(c, "Node %s has multiple '@' characters in name", | |
259 | node->fullpath); | |
260 | } | |
6f05afcb | 261 | ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars); |
ed95d745 | 262 | |
6f05afcb RH |
263 | static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti, |
264 | struct node *node) | |
b9937347 RH |
265 | { |
266 | const char *unitname = get_unitname(node); | |
267 | struct property *prop = get_property(node, "reg"); | |
268 | ||
269 | if (!prop) { | |
270 | prop = get_property(node, "ranges"); | |
271 | if (prop && !prop->val.len) | |
272 | prop = NULL; | |
273 | } | |
274 | ||
275 | if (prop) { | |
276 | if (!unitname[0]) | |
277 | FAIL(c, "Node %s has a reg or ranges property, but no unit name", | |
278 | node->fullpath); | |
279 | } else { | |
280 | if (unitname[0]) | |
281 | FAIL(c, "Node %s has a unit name, but no reg property", | |
282 | node->fullpath); | |
283 | } | |
284 | } | |
6f05afcb | 285 | WARNING(unit_address_vs_reg, check_unit_address_vs_reg, NULL); |
b9937347 | 286 | |
6f05afcb RH |
287 | static void check_property_name_chars(struct check *c, struct dt_info *dti, |
288 | struct node *node) | |
ed95d745 | 289 | { |
6f05afcb RH |
290 | struct property *prop; |
291 | ||
292 | for_each_property(node, prop) { | |
293 | int n = strspn(prop->name, c->data); | |
ed95d745 | 294 | |
6f05afcb RH |
295 | if (n < strlen(prop->name)) |
296 | FAIL(c, "Bad character '%c' in property name \"%s\", node %s", | |
297 | prop->name[n], prop->name, node->fullpath); | |
298 | } | |
ed95d745 | 299 | } |
6f05afcb | 300 | ERROR(property_name_chars, check_property_name_chars, PROPNODECHARS); |
ed95d745 | 301 | |
658f29a5 JB |
302 | #define DESCLABEL_FMT "%s%s%s%s%s" |
303 | #define DESCLABEL_ARGS(node,prop,mark) \ | |
304 | ((mark) ? "value of " : ""), \ | |
305 | ((prop) ? "'" : ""), \ | |
306 | ((prop) ? (prop)->name : ""), \ | |
307 | ((prop) ? "' in " : ""), (node)->fullpath | |
308 | ||
6f05afcb | 309 | static void check_duplicate_label(struct check *c, struct dt_info *dti, |
658f29a5 JB |
310 | const char *label, struct node *node, |
311 | struct property *prop, struct marker *mark) | |
312 | { | |
6f05afcb | 313 | struct node *dt = dti->dt; |
658f29a5 JB |
314 | struct node *othernode = NULL; |
315 | struct property *otherprop = NULL; | |
316 | struct marker *othermark = NULL; | |
317 | ||
318 | othernode = get_node_by_label(dt, label); | |
319 | ||
320 | if (!othernode) | |
321 | otherprop = get_property_by_label(dt, label, &othernode); | |
322 | if (!othernode) | |
323 | othermark = get_marker_label(dt, label, &othernode, | |
324 | &otherprop); | |
325 | ||
326 | if (!othernode) | |
327 | return; | |
328 | ||
329 | if ((othernode != node) || (otherprop != prop) || (othermark != mark)) | |
330 | FAIL(c, "Duplicate label '%s' on " DESCLABEL_FMT | |
331 | " and " DESCLABEL_FMT, | |
332 | label, DESCLABEL_ARGS(node, prop, mark), | |
333 | DESCLABEL_ARGS(othernode, otherprop, othermark)); | |
334 | } | |
335 | ||
6f05afcb | 336 | static void check_duplicate_label_node(struct check *c, struct dt_info *dti, |
658f29a5 JB |
337 | struct node *node) |
338 | { | |
339 | struct label *l; | |
6f05afcb | 340 | struct property *prop; |
658f29a5 JB |
341 | |
342 | for_each_label(node->labels, l) | |
6f05afcb RH |
343 | check_duplicate_label(c, dti, l->label, node, NULL, NULL); |
344 | ||
345 | for_each_property(node, prop) { | |
346 | struct marker *m = prop->val.markers; | |
658f29a5 | 347 | |
6f05afcb RH |
348 | for_each_label(prop->labels, l) |
349 | check_duplicate_label(c, dti, l->label, node, prop, NULL); | |
658f29a5 | 350 | |
6f05afcb RH |
351 | for_each_marker_of_type(m, LABEL) |
352 | check_duplicate_label(c, dti, m->ref, node, prop, m); | |
353 | } | |
658f29a5 | 354 | } |
6f05afcb | 355 | ERROR(duplicate_label, check_duplicate_label_node, NULL); |
658f29a5 | 356 | |
6f05afcb RH |
357 | static cell_t check_phandle_prop(struct check *c, struct dt_info *dti, |
358 | struct node *node, const char *propname) | |
a4da2e3e | 359 | { |
6f05afcb RH |
360 | struct node *root = dti->dt; |
361 | struct property *prop; | |
658f29a5 | 362 | struct marker *m; |
a4da2e3e DG |
363 | cell_t phandle; |
364 | ||
6f05afcb RH |
365 | prop = get_property(node, propname); |
366 | if (!prop) | |
367 | return 0; | |
a4da2e3e DG |
368 | |
369 | if (prop->val.len != sizeof(cell_t)) { | |
658f29a5 JB |
370 | FAIL(c, "%s has bad length (%d) %s property", |
371 | node->fullpath, prop->val.len, prop->name); | |
6f05afcb | 372 | return 0; |
658f29a5 JB |
373 | } |
374 | ||
375 | m = prop->val.markers; | |
376 | for_each_marker_of_type(m, REF_PHANDLE) { | |
377 | assert(m->offset == 0); | |
378 | if (node != get_node_by_ref(root, m->ref)) | |
379 | /* "Set this node's phandle equal to some | |
380 | * other node's phandle". That's nonsensical | |
381 | * by construction. */ { | |
382 | FAIL(c, "%s in %s is a reference to another node", | |
383 | prop->name, node->fullpath); | |
658f29a5 JB |
384 | } |
385 | /* But setting this node's phandle equal to its own | |
386 | * phandle is allowed - that means allocate a unique | |
387 | * phandle for this node, even if it's not otherwise | |
388 | * referenced. The value will be filled in later, so | |
6f05afcb RH |
389 | * we treat it as having no phandle data for now. */ |
390 | return 0; | |
a4da2e3e DG |
391 | } |
392 | ||
393 | phandle = propval_cell(prop); | |
658f29a5 | 394 | |
a4da2e3e | 395 | if ((phandle == 0) || (phandle == -1)) { |
658f29a5 JB |
396 | FAIL(c, "%s has bad value (0x%x) in %s property", |
397 | node->fullpath, phandle, prop->name); | |
6f05afcb | 398 | return 0; |
a4da2e3e DG |
399 | } |
400 | ||
6f05afcb RH |
401 | return phandle; |
402 | } | |
403 | ||
404 | static void check_explicit_phandles(struct check *c, struct dt_info *dti, | |
405 | struct node *node) | |
406 | { | |
407 | struct node *root = dti->dt; | |
408 | struct node *other; | |
409 | cell_t phandle, linux_phandle; | |
410 | ||
411 | /* Nothing should have assigned phandles yet */ | |
412 | assert(!node->phandle); | |
413 | ||
414 | phandle = check_phandle_prop(c, dti, node, "phandle"); | |
415 | ||
416 | linux_phandle = check_phandle_prop(c, dti, node, "linux,phandle"); | |
417 | ||
418 | if (!phandle && !linux_phandle) | |
419 | /* No valid phandles; nothing further to check */ | |
420 | return; | |
421 | ||
422 | if (linux_phandle && phandle && (phandle != linux_phandle)) | |
423 | FAIL(c, "%s has mismatching 'phandle' and 'linux,phandle'" | |
424 | " properties", node->fullpath); | |
425 | ||
426 | if (linux_phandle && !phandle) | |
427 | phandle = linux_phandle; | |
658f29a5 | 428 | |
a4da2e3e | 429 | other = get_node_by_phandle(root, phandle); |
658f29a5 | 430 | if (other && (other != node)) { |
a4da2e3e DG |
431 | FAIL(c, "%s has duplicated phandle 0x%x (seen before at %s)", |
432 | node->fullpath, phandle, other->fullpath); | |
433 | return; | |
434 | } | |
435 | ||
436 | node->phandle = phandle; | |
437 | } | |
6f05afcb | 438 | ERROR(explicit_phandles, check_explicit_phandles, NULL); |
a4da2e3e | 439 | |
6f05afcb | 440 | static void check_name_properties(struct check *c, struct dt_info *dti, |
a4da2e3e DG |
441 | struct node *node) |
442 | { | |
ed95d745 DG |
443 | struct property **pp, *prop = NULL; |
444 | ||
445 | for (pp = &node->proplist; *pp; pp = &((*pp)->next)) | |
446 | if (streq((*pp)->name, "name")) { | |
447 | prop = *pp; | |
448 | break; | |
449 | } | |
a4da2e3e | 450 | |
a4da2e3e DG |
451 | if (!prop) |
452 | return; /* No name property, that's fine */ | |
453 | ||
454 | if ((prop->val.len != node->basenamelen+1) | |
ed95d745 | 455 | || (memcmp(prop->val.val, node->name, node->basenamelen) != 0)) { |
a4da2e3e DG |
456 | FAIL(c, "\"name\" property in %s is incorrect (\"%s\" instead" |
457 | " of base node name)", node->fullpath, prop->val.val); | |
ed95d745 DG |
458 | } else { |
459 | /* The name property is correct, and therefore redundant. | |
460 | * Delete it */ | |
461 | *pp = prop->next; | |
462 | free(prop->name); | |
463 | data_free(prop->val); | |
464 | free(prop); | |
465 | } | |
a4da2e3e | 466 | } |
cd296721 | 467 | ERROR_IF_NOT_STRING(name_is_string, "name"); |
6f05afcb | 468 | ERROR(name_properties, check_name_properties, NULL, &name_is_string); |
a4da2e3e DG |
469 | |
470 | /* | |
471 | * Reference fixup functions | |
472 | */ | |
473 | ||
6f05afcb RH |
474 | static void fixup_phandle_references(struct check *c, struct dt_info *dti, |
475 | struct node *node) | |
a4da2e3e | 476 | { |
6f05afcb RH |
477 | struct node *dt = dti->dt; |
478 | struct property *prop; | |
ed95d745 | 479 | |
6f05afcb RH |
480 | for_each_property(node, prop) { |
481 | struct marker *m = prop->val.markers; | |
482 | struct node *refnode; | |
483 | cell_t phandle; | |
484 | ||
485 | for_each_marker_of_type(m, REF_PHANDLE) { | |
486 | assert(m->offset + sizeof(cell_t) <= prop->val.len); | |
487 | ||
488 | refnode = get_node_by_ref(dt, m->ref); | |
489 | if (! refnode) { | |
490 | if (!(dti->dtsflags & DTSF_PLUGIN)) | |
491 | FAIL(c, "Reference to non-existent node or " | |
492 | "label \"%s\"\n", m->ref); | |
493 | else /* mark the entry as unresolved */ | |
494 | *((cell_t *)(prop->val.val + m->offset)) = | |
495 | cpu_to_fdt32(0xffffffff); | |
496 | continue; | |
497 | } | |
ed95d745 | 498 | |
6f05afcb RH |
499 | phandle = get_node_phandle(dt, refnode); |
500 | *((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle); | |
ed95d745 | 501 | } |
ed95d745 | 502 | } |
a4da2e3e | 503 | } |
6f05afcb | 504 | ERROR(phandle_references, fixup_phandle_references, NULL, |
a4da2e3e DG |
505 | &duplicate_node_names, &explicit_phandles); |
506 | ||
6f05afcb RH |
507 | static void fixup_path_references(struct check *c, struct dt_info *dti, |
508 | struct node *node) | |
a4da2e3e | 509 | { |
6f05afcb RH |
510 | struct node *dt = dti->dt; |
511 | struct property *prop; | |
512 | ||
513 | for_each_property(node, prop) { | |
514 | struct marker *m = prop->val.markers; | |
515 | struct node *refnode; | |
516 | char *path; | |
517 | ||
518 | for_each_marker_of_type(m, REF_PATH) { | |
519 | assert(m->offset <= prop->val.len); | |
a4da2e3e | 520 | |
6f05afcb RH |
521 | refnode = get_node_by_ref(dt, m->ref); |
522 | if (!refnode) { | |
523 | FAIL(c, "Reference to non-existent node or label \"%s\"\n", | |
524 | m->ref); | |
525 | continue; | |
526 | } | |
527 | ||
528 | path = refnode->fullpath; | |
529 | prop->val = data_insert_at_marker(prop->val, m, path, | |
530 | strlen(path) + 1); | |
531 | } | |
a4da2e3e DG |
532 | } |
533 | } | |
6f05afcb | 534 | ERROR(path_references, fixup_path_references, NULL, &duplicate_node_names); |
a4da2e3e DG |
535 | |
536 | /* | |
537 | * Semantic checks | |
538 | */ | |
cd296721 SW |
539 | WARNING_IF_NOT_CELL(address_cells_is_cell, "#address-cells"); |
540 | WARNING_IF_NOT_CELL(size_cells_is_cell, "#size-cells"); | |
541 | WARNING_IF_NOT_CELL(interrupt_cells_is_cell, "#interrupt-cells"); | |
a4da2e3e | 542 | |
cd296721 SW |
543 | WARNING_IF_NOT_STRING(device_type_is_string, "device_type"); |
544 | WARNING_IF_NOT_STRING(model_is_string, "model"); | |
545 | WARNING_IF_NOT_STRING(status_is_string, "status"); | |
a4da2e3e | 546 | |
6f05afcb | 547 | static void fixup_addr_size_cells(struct check *c, struct dt_info *dti, |
a4da2e3e DG |
548 | struct node *node) |
549 | { | |
550 | struct property *prop; | |
551 | ||
552 | node->addr_cells = -1; | |
553 | node->size_cells = -1; | |
554 | ||
555 | prop = get_property(node, "#address-cells"); | |
556 | if (prop) | |
557 | node->addr_cells = propval_cell(prop); | |
558 | ||
559 | prop = get_property(node, "#size-cells"); | |
560 | if (prop) | |
561 | node->size_cells = propval_cell(prop); | |
562 | } | |
6f05afcb | 563 | WARNING(addr_size_cells, fixup_addr_size_cells, NULL, |
cd296721 | 564 | &address_cells_is_cell, &size_cells_is_cell); |
a4da2e3e DG |
565 | |
566 | #define node_addr_cells(n) \ | |
567 | (((n)->addr_cells == -1) ? 2 : (n)->addr_cells) | |
568 | #define node_size_cells(n) \ | |
569 | (((n)->size_cells == -1) ? 1 : (n)->size_cells) | |
570 | ||
6f05afcb | 571 | static void check_reg_format(struct check *c, struct dt_info *dti, |
a4da2e3e DG |
572 | struct node *node) |
573 | { | |
574 | struct property *prop; | |
575 | int addr_cells, size_cells, entrylen; | |
576 | ||
577 | prop = get_property(node, "reg"); | |
578 | if (!prop) | |
579 | return; /* No "reg", that's fine */ | |
580 | ||
581 | if (!node->parent) { | |
582 | FAIL(c, "Root node has a \"reg\" property"); | |
583 | return; | |
584 | } | |
585 | ||
586 | if (prop->val.len == 0) | |
587 | FAIL(c, "\"reg\" property in %s is empty", node->fullpath); | |
588 | ||
589 | addr_cells = node_addr_cells(node->parent); | |
590 | size_cells = node_size_cells(node->parent); | |
591 | entrylen = (addr_cells + size_cells) * sizeof(cell_t); | |
592 | ||
91feabc2 | 593 | if (!entrylen || (prop->val.len % entrylen) != 0) |
a4da2e3e DG |
594 | FAIL(c, "\"reg\" property in %s has invalid length (%d bytes) " |
595 | "(#address-cells == %d, #size-cells == %d)", | |
596 | node->fullpath, prop->val.len, addr_cells, size_cells); | |
597 | } | |
6f05afcb | 598 | WARNING(reg_format, check_reg_format, NULL, &addr_size_cells); |
a4da2e3e | 599 | |
6f05afcb | 600 | static void check_ranges_format(struct check *c, struct dt_info *dti, |
a4da2e3e DG |
601 | struct node *node) |
602 | { | |
603 | struct property *prop; | |
604 | int c_addr_cells, p_addr_cells, c_size_cells, p_size_cells, entrylen; | |
605 | ||
606 | prop = get_property(node, "ranges"); | |
607 | if (!prop) | |
608 | return; | |
609 | ||
610 | if (!node->parent) { | |
611 | FAIL(c, "Root node has a \"ranges\" property"); | |
612 | return; | |
613 | } | |
614 | ||
615 | p_addr_cells = node_addr_cells(node->parent); | |
616 | p_size_cells = node_size_cells(node->parent); | |
617 | c_addr_cells = node_addr_cells(node); | |
618 | c_size_cells = node_size_cells(node); | |
619 | entrylen = (p_addr_cells + c_addr_cells + c_size_cells) * sizeof(cell_t); | |
620 | ||
621 | if (prop->val.len == 0) { | |
622 | if (p_addr_cells != c_addr_cells) | |
623 | FAIL(c, "%s has empty \"ranges\" property but its " | |
624 | "#address-cells (%d) differs from %s (%d)", | |
625 | node->fullpath, c_addr_cells, node->parent->fullpath, | |
626 | p_addr_cells); | |
627 | if (p_size_cells != c_size_cells) | |
628 | FAIL(c, "%s has empty \"ranges\" property but its " | |
629 | "#size-cells (%d) differs from %s (%d)", | |
630 | node->fullpath, c_size_cells, node->parent->fullpath, | |
631 | p_size_cells); | |
632 | } else if ((prop->val.len % entrylen) != 0) { | |
633 | FAIL(c, "\"ranges\" property in %s has invalid length (%d bytes) " | |
634 | "(parent #address-cells == %d, child #address-cells == %d, " | |
635 | "#size-cells == %d)", node->fullpath, prop->val.len, | |
636 | p_addr_cells, c_addr_cells, c_size_cells); | |
637 | } | |
638 | } | |
6f05afcb | 639 | WARNING(ranges_format, check_ranges_format, NULL, &addr_size_cells); |
a4da2e3e DG |
640 | |
641 | /* | |
642 | * Style checks | |
643 | */ | |
6f05afcb | 644 | static void check_avoid_default_addr_size(struct check *c, struct dt_info *dti, |
a4da2e3e DG |
645 | struct node *node) |
646 | { | |
647 | struct property *reg, *ranges; | |
648 | ||
649 | if (!node->parent) | |
650 | return; /* Ignore root node */ | |
651 | ||
652 | reg = get_property(node, "reg"); | |
653 | ranges = get_property(node, "ranges"); | |
654 | ||
655 | if (!reg && !ranges) | |
656 | return; | |
657 | ||
47605971 | 658 | if (node->parent->addr_cells == -1) |
a4da2e3e DG |
659 | FAIL(c, "Relying on default #address-cells value for %s", |
660 | node->fullpath); | |
661 | ||
47605971 | 662 | if (node->parent->size_cells == -1) |
a4da2e3e DG |
663 | FAIL(c, "Relying on default #size-cells value for %s", |
664 | node->fullpath); | |
665 | } | |
6f05afcb RH |
666 | WARNING(avoid_default_addr_size, check_avoid_default_addr_size, NULL, |
667 | &addr_size_cells); | |
a4da2e3e DG |
668 | |
669 | static void check_obsolete_chosen_interrupt_controller(struct check *c, | |
6f05afcb RH |
670 | struct dt_info *dti, |
671 | struct node *node) | |
a4da2e3e | 672 | { |
6f05afcb | 673 | struct node *dt = dti->dt; |
a4da2e3e DG |
674 | struct node *chosen; |
675 | struct property *prop; | |
676 | ||
6f05afcb RH |
677 | if (node != dt) |
678 | return; | |
679 | ||
680 | ||
a4da2e3e DG |
681 | chosen = get_node_by_path(dt, "/chosen"); |
682 | if (!chosen) | |
683 | return; | |
684 | ||
685 | prop = get_property(chosen, "interrupt-controller"); | |
686 | if (prop) | |
687 | FAIL(c, "/chosen has obsolete \"interrupt-controller\" " | |
688 | "property"); | |
689 | } | |
6f05afcb RH |
690 | WARNING(obsolete_chosen_interrupt_controller, |
691 | check_obsolete_chosen_interrupt_controller, NULL); | |
a4da2e3e DG |
692 | |
693 | static struct check *check_table[] = { | |
694 | &duplicate_node_names, &duplicate_property_names, | |
ed95d745 | 695 | &node_name_chars, &node_name_format, &property_name_chars, |
a4da2e3e | 696 | &name_is_string, &name_properties, |
658f29a5 JB |
697 | |
698 | &duplicate_label, | |
699 | ||
a4da2e3e DG |
700 | &explicit_phandles, |
701 | &phandle_references, &path_references, | |
702 | ||
703 | &address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell, | |
704 | &device_type_is_string, &model_is_string, &status_is_string, | |
705 | ||
706 | &addr_size_cells, ®_format, &ranges_format, | |
707 | ||
b9937347 RH |
708 | &unit_address_vs_reg, |
709 | ||
a4da2e3e DG |
710 | &avoid_default_addr_size, |
711 | &obsolete_chosen_interrupt_controller, | |
cd296721 SW |
712 | |
713 | &always_fail, | |
a4da2e3e DG |
714 | }; |
715 | ||
cd296721 SW |
716 | static void enable_warning_error(struct check *c, bool warn, bool error) |
717 | { | |
718 | int i; | |
719 | ||
720 | /* Raising level, also raise it for prereqs */ | |
721 | if ((warn && !c->warn) || (error && !c->error)) | |
722 | for (i = 0; i < c->num_prereqs; i++) | |
723 | enable_warning_error(c->prereq[i], warn, error); | |
724 | ||
725 | c->warn = c->warn || warn; | |
726 | c->error = c->error || error; | |
727 | } | |
728 | ||
729 | static void disable_warning_error(struct check *c, bool warn, bool error) | |
730 | { | |
731 | int i; | |
732 | ||
733 | /* Lowering level, also lower it for things this is the prereq | |
734 | * for */ | |
735 | if ((warn && c->warn) || (error && c->error)) { | |
736 | for (i = 0; i < ARRAY_SIZE(check_table); i++) { | |
737 | struct check *cc = check_table[i]; | |
738 | int j; | |
739 | ||
740 | for (j = 0; j < cc->num_prereqs; j++) | |
741 | if (cc->prereq[j] == c) | |
742 | disable_warning_error(cc, warn, error); | |
743 | } | |
744 | } | |
745 | ||
746 | c->warn = c->warn && !warn; | |
747 | c->error = c->error && !error; | |
748 | } | |
749 | ||
47605971 | 750 | void parse_checks_option(bool warn, bool error, const char *arg) |
cd296721 SW |
751 | { |
752 | int i; | |
47605971 | 753 | const char *name = arg; |
cd296721 SW |
754 | bool enable = true; |
755 | ||
47605971 RH |
756 | if ((strncmp(arg, "no-", 3) == 0) |
757 | || (strncmp(arg, "no_", 3) == 0)) { | |
758 | name = arg + 3; | |
cd296721 SW |
759 | enable = false; |
760 | } | |
761 | ||
762 | for (i = 0; i < ARRAY_SIZE(check_table); i++) { | |
763 | struct check *c = check_table[i]; | |
764 | ||
765 | if (streq(c->name, name)) { | |
766 | if (enable) | |
767 | enable_warning_error(c, warn, error); | |
768 | else | |
769 | disable_warning_error(c, warn, error); | |
770 | return; | |
771 | } | |
772 | } | |
773 | ||
774 | die("Unrecognized check name \"%s\"\n", name); | |
775 | } | |
776 | ||
6f05afcb | 777 | void process_checks(bool force, struct dt_info *dti) |
a4da2e3e | 778 | { |
a4da2e3e DG |
779 | int i; |
780 | int error = 0; | |
781 | ||
782 | for (i = 0; i < ARRAY_SIZE(check_table); i++) { | |
783 | struct check *c = check_table[i]; | |
784 | ||
cd296721 | 785 | if (c->warn || c->error) |
6f05afcb | 786 | error = error || run_check(c, dti); |
a4da2e3e DG |
787 | } |
788 | ||
789 | if (error) { | |
790 | if (!force) { | |
791 | fprintf(stderr, "ERROR: Input tree has errors, aborting " | |
792 | "(use -f to force output)\n"); | |
793 | exit(2); | |
794 | } else if (quiet < 3) { | |
795 | fprintf(stderr, "Warning: Input tree has errors, " | |
796 | "output forced\n"); | |
797 | } | |
798 | } | |
a4da2e3e | 799 | } |