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