1 /* 2 * Functions for dealing with DT resolution 3 * 4 * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com> 5 * Copyright (C) 2012 Texas Instruments Inc. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * version 2 as published by the Free Software Foundation. 10 */ 11 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/of_device.h> 16 #include <linux/string.h> 17 #include <linux/ctype.h> 18 #include <linux/errno.h> 19 #include <linux/string.h> 20 #include <linux/slab.h> 21 22 /* illegal phandle value (set when unresolved) */ 23 #define OF_PHANDLE_ILLEGAL 0xdeadbeef 24 25 /** 26 * Find a node with the give full name by recursively following any of 27 * the child node links. 28 */ 29 static struct device_node *__of_find_node_by_full_name(struct device_node *node, 30 const char *full_name) 31 { 32 struct device_node *child, *found; 33 34 if (node == NULL) 35 return NULL; 36 37 /* check */ 38 if (of_node_cmp(node->full_name, full_name) == 0) 39 return of_node_get(node); 40 41 for_each_child_of_node(node, child) { 42 found = __of_find_node_by_full_name(child, full_name); 43 if (found != NULL) { 44 of_node_put(child); 45 return found; 46 } 47 } 48 49 return NULL; 50 } 51 52 /* 53 * Find live tree's maximum phandle value. 54 */ 55 static phandle of_get_tree_max_phandle(void) 56 { 57 struct device_node *node; 58 phandle phandle; 59 unsigned long flags; 60 61 /* now search recursively */ 62 raw_spin_lock_irqsave(&devtree_lock, flags); 63 phandle = 0; 64 for_each_of_allnodes(node) { 65 if (node->phandle != OF_PHANDLE_ILLEGAL && 66 node->phandle > phandle) 67 phandle = node->phandle; 68 } 69 raw_spin_unlock_irqrestore(&devtree_lock, flags); 70 71 return phandle; 72 } 73 74 /* 75 * Adjust a subtree's phandle values by a given delta. 76 * Makes sure not to just adjust the device node's phandle value, 77 * but modify the phandle properties values as well. 78 */ 79 static void __of_adjust_tree_phandles(struct device_node *node, 80 int phandle_delta) 81 { 82 struct device_node *child; 83 struct property *prop; 84 phandle phandle; 85 86 /* first adjust the node's phandle direct value */ 87 if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL) 88 node->phandle += phandle_delta; 89 90 /* now adjust phandle & linux,phandle values */ 91 for_each_property_of_node(node, prop) { 92 93 /* only look for these two */ 94 if (of_prop_cmp(prop->name, "phandle") != 0 && 95 of_prop_cmp(prop->name, "linux,phandle") != 0) 96 continue; 97 98 /* must be big enough */ 99 if (prop->length < 4) 100 continue; 101 102 /* read phandle value */ 103 phandle = be32_to_cpup(prop->value); 104 if (phandle == OF_PHANDLE_ILLEGAL) /* unresolved */ 105 continue; 106 107 /* adjust */ 108 *(uint32_t *)prop->value = cpu_to_be32(node->phandle); 109 } 110 111 /* now do the children recursively */ 112 for_each_child_of_node(node, child) 113 __of_adjust_tree_phandles(child, phandle_delta); 114 } 115 116 static int __of_adjust_phandle_ref(struct device_node *node, 117 struct property *rprop, int value) 118 { 119 phandle phandle; 120 struct device_node *refnode; 121 struct property *sprop; 122 char *propval, *propcur, *propend, *nodestr, *propstr, *s; 123 int offset, propcurlen; 124 int err = 0; 125 126 /* make a copy */ 127 propval = kmalloc(rprop->length, GFP_KERNEL); 128 if (!propval) { 129 pr_err("%s: Could not copy value of '%s'\n", 130 __func__, rprop->name); 131 return -ENOMEM; 132 } 133 memcpy(propval, rprop->value, rprop->length); 134 135 propend = propval + rprop->length; 136 for (propcur = propval; propcur < propend; propcur += propcurlen + 1) { 137 propcurlen = strlen(propcur); 138 139 nodestr = propcur; 140 s = strchr(propcur, ':'); 141 if (!s) { 142 pr_err("%s: Illegal symbol entry '%s' (1)\n", 143 __func__, propcur); 144 err = -EINVAL; 145 goto err_fail; 146 } 147 *s++ = '\0'; 148 149 propstr = s; 150 s = strchr(s, ':'); 151 if (!s) { 152 pr_err("%s: Illegal symbol entry '%s' (2)\n", 153 __func__, (char *)rprop->value); 154 err = -EINVAL; 155 goto err_fail; 156 } 157 158 *s++ = '\0'; 159 err = kstrtoint(s, 10, &offset); 160 if (err != 0) { 161 pr_err("%s: Could get offset '%s'\n", 162 __func__, (char *)rprop->value); 163 goto err_fail; 164 } 165 166 /* look into the resolve node for the full path */ 167 refnode = __of_find_node_by_full_name(node, nodestr); 168 if (!refnode) { 169 pr_warn("%s: Could not find refnode '%s'\n", 170 __func__, (char *)rprop->value); 171 continue; 172 } 173 174 /* now find the property */ 175 for_each_property_of_node(refnode, sprop) { 176 if (of_prop_cmp(sprop->name, propstr) == 0) 177 break; 178 } 179 of_node_put(refnode); 180 181 if (!sprop) { 182 pr_err("%s: Could not find property '%s'\n", 183 __func__, (char *)rprop->value); 184 err = -ENOENT; 185 goto err_fail; 186 } 187 188 phandle = value; 189 *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle); 190 } 191 192 err_fail: 193 kfree(propval); 194 return err; 195 } 196 197 /* compare nodes taking into account that 'name' strips out the @ part */ 198 static int __of_node_name_cmp(const struct device_node *dn1, 199 const struct device_node *dn2) 200 { 201 const char *n1 = strrchr(dn1->full_name, '/') ? : "/"; 202 const char *n2 = strrchr(dn2->full_name, '/') ? : "/"; 203 204 return of_node_cmp(n1, n2); 205 } 206 207 /* 208 * Adjust the local phandle references by the given phandle delta. 209 * Assumes the existances of a __local_fixups__ node at the root. 210 * Assumes that __of_verify_tree_phandle_references has been called. 211 * Does not take any devtree locks so make sure you call this on a tree 212 * which is at the detached state. 213 */ 214 static int __of_adjust_tree_phandle_references(struct device_node *node, 215 struct device_node *target, int phandle_delta) 216 { 217 struct device_node *child, *childtarget; 218 struct property *rprop, *sprop; 219 int err, i, count; 220 unsigned int off; 221 phandle phandle; 222 223 if (node == NULL) 224 return 0; 225 226 for_each_property_of_node(node, rprop) { 227 228 /* skip properties added automatically */ 229 if (of_prop_cmp(rprop->name, "name") == 0 || 230 of_prop_cmp(rprop->name, "phandle") == 0 || 231 of_prop_cmp(rprop->name, "linux,phandle") == 0) 232 continue; 233 234 if ((rprop->length % 4) != 0 || rprop->length == 0) { 235 pr_err("%s: Illegal property (size) '%s' @%s\n", 236 __func__, rprop->name, node->full_name); 237 return -EINVAL; 238 } 239 count = rprop->length / sizeof(__be32); 240 241 /* now find the target property */ 242 for_each_property_of_node(target, sprop) { 243 if (of_prop_cmp(sprop->name, rprop->name) == 0) 244 break; 245 } 246 247 if (sprop == NULL) { 248 pr_err("%s: Could not find target property '%s' @%s\n", 249 __func__, rprop->name, node->full_name); 250 return -EINVAL; 251 } 252 253 for (i = 0; i < count; i++) { 254 off = be32_to_cpu(((__be32 *)rprop->value)[i]); 255 /* make sure the offset doesn't overstep (even wrap) */ 256 if (off >= sprop->length || 257 (off + 4) > sprop->length) { 258 pr_err("%s: Illegal property '%s' @%s\n", 259 __func__, rprop->name, 260 node->full_name); 261 return -EINVAL; 262 } 263 264 if (phandle_delta) { 265 /* adjust */ 266 phandle = be32_to_cpu(*(__be32 *)(sprop->value + off)); 267 phandle += phandle_delta; 268 *(__be32 *)(sprop->value + off) = cpu_to_be32(phandle); 269 } 270 } 271 } 272 273 for_each_child_of_node(node, child) { 274 275 for_each_child_of_node(target, childtarget) 276 if (__of_node_name_cmp(child, childtarget) == 0) 277 break; 278 279 if (!childtarget) { 280 pr_err("%s: Could not find target child '%s' @%s\n", 281 __func__, child->name, node->full_name); 282 return -EINVAL; 283 } 284 285 err = __of_adjust_tree_phandle_references(child, childtarget, 286 phandle_delta); 287 if (err != 0) 288 return err; 289 } 290 291 return 0; 292 } 293 294 /** 295 * of_resolve - Resolve the given node against the live tree. 296 * 297 * @resolve: Node to resolve 298 * 299 * Perform dynamic Device Tree resolution against the live tree 300 * to the given node to resolve. This depends on the live tree 301 * having a __symbols__ node, and the resolve node the __fixups__ & 302 * __local_fixups__ nodes (if needed). 303 * The result of the operation is a resolve node that it's contents 304 * are fit to be inserted or operate upon the live tree. 305 * Returns 0 on success or a negative error value on error. 306 */ 307 int of_resolve_phandles(struct device_node *resolve) 308 { 309 struct device_node *child, *childroot, *refnode; 310 struct device_node *root_sym, *resolve_sym, *resolve_fix; 311 struct property *rprop; 312 const char *refpath; 313 phandle phandle, phandle_delta; 314 int err; 315 316 /* the resolve node must exist, and be detached */ 317 if (!resolve || !of_node_check_flag(resolve, OF_DETACHED)) 318 return -EINVAL; 319 320 /* first we need to adjust the phandles */ 321 phandle_delta = of_get_tree_max_phandle() + 1; 322 __of_adjust_tree_phandles(resolve, phandle_delta); 323 324 /* locate the local fixups */ 325 childroot = NULL; 326 for_each_child_of_node(resolve, childroot) 327 if (of_node_cmp(childroot->name, "__local_fixups__") == 0) 328 break; 329 330 if (childroot != NULL) { 331 /* resolve root is guaranteed to be the '/' */ 332 err = __of_adjust_tree_phandle_references(childroot, 333 resolve, 0); 334 if (err != 0) 335 return err; 336 337 BUG_ON(__of_adjust_tree_phandle_references(childroot, 338 resolve, phandle_delta)); 339 } 340 341 root_sym = NULL; 342 resolve_sym = NULL; 343 resolve_fix = NULL; 344 345 /* this may fail (if no fixups are required) */ 346 root_sym = of_find_node_by_path("/__symbols__"); 347 348 /* locate the symbols & fixups nodes on resolve */ 349 for_each_child_of_node(resolve, child) { 350 351 if (!resolve_sym && 352 of_node_cmp(child->name, "__symbols__") == 0) 353 resolve_sym = child; 354 355 if (!resolve_fix && 356 of_node_cmp(child->name, "__fixups__") == 0) 357 resolve_fix = child; 358 359 /* both found, don't bother anymore */ 360 if (resolve_sym && resolve_fix) 361 break; 362 } 363 364 /* we do allow for the case where no fixups are needed */ 365 if (!resolve_fix) { 366 err = 0; /* no error */ 367 goto out; 368 } 369 370 /* we need to fixup, but no root symbols... */ 371 if (!root_sym) { 372 err = -EINVAL; 373 goto out; 374 } 375 376 for_each_property_of_node(resolve_fix, rprop) { 377 378 /* skip properties added automatically */ 379 if (of_prop_cmp(rprop->name, "name") == 0) 380 continue; 381 382 err = of_property_read_string(root_sym, 383 rprop->name, &refpath); 384 if (err != 0) { 385 pr_err("%s: Could not find symbol '%s'\n", 386 __func__, rprop->name); 387 goto out; 388 } 389 390 refnode = of_find_node_by_path(refpath); 391 if (!refnode) { 392 pr_err("%s: Could not find node by path '%s'\n", 393 __func__, refpath); 394 err = -ENOENT; 395 goto out; 396 } 397 398 phandle = refnode->phandle; 399 of_node_put(refnode); 400 401 pr_debug("%s: %s phandle is 0x%08x\n", 402 __func__, rprop->name, phandle); 403 404 err = __of_adjust_phandle_ref(resolve, rprop, phandle); 405 if (err) 406 break; 407 } 408 409 out: 410 /* NULL is handled by of_node_put as NOP */ 411 of_node_put(root_sym); 412 413 return err; 414 } 415 EXPORT_SYMBOL_GPL(of_resolve_phandles); 416