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