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, struct property *rprop, int value, bool is_delta) 115{ 116 phandle phandle; 117 struct device_node *refnode; 118 struct property *sprop; 119 char *propval, *propcur, *propend, *nodestr, *propstr, *s; 120 int offset, propcurlen; 121 int err = 0; 122 123 /* make a copy */ 124 propval = kmalloc(rprop->length, GFP_KERNEL); 125 if (!propval) { 126 pr_err("%s: Could not copy value of '%s'\n", 127 __func__, rprop->name); 128 return -ENOMEM; 129 } 130 memcpy(propval, rprop->value, rprop->length); 131 132 propend = propval + rprop->length; 133 for (propcur = propval; propcur < propend; propcur += propcurlen + 1) { 134 propcurlen = strlen(propcur); 135 136 nodestr = propcur; 137 s = strchr(propcur, ':'); 138 if (!s) { 139 pr_err("%s: Illegal symbol entry '%s' (1)\n", 140 __func__, propcur); 141 err = -EINVAL; 142 goto err_fail; 143 } 144 *s++ = '\0'; 145 146 propstr = s; 147 s = strchr(s, ':'); 148 if (!s) { 149 pr_err("%s: Illegal symbol entry '%s' (2)\n", 150 __func__, (char *)rprop->value); 151 err = -EINVAL; 152 goto err_fail; 153 } 154 155 *s++ = '\0'; 156 err = kstrtoint(s, 10, &offset); 157 if (err != 0) { 158 pr_err("%s: Could get offset '%s'\n", 159 __func__, (char *)rprop->value); 160 goto err_fail; 161 } 162 163 /* look into the resolve node for the full path */ 164 refnode = __of_find_node_by_full_name(node, nodestr); 165 if (!refnode) { 166 pr_warn("%s: Could not find refnode '%s'\n", 167 __func__, (char *)rprop->value); 168 continue; 169 } 170 171 /* now find the property */ 172 for_each_property_of_node(refnode, sprop) { 173 if (of_prop_cmp(sprop->name, propstr) == 0) 174 break; 175 } 176 177 if (!sprop) { 178 pr_err("%s: Could not find property '%s'\n", 179 __func__, (char *)rprop->value); 180 err = -ENOENT; 181 goto err_fail; 182 } 183 184 phandle = is_delta ? be32_to_cpup(sprop->value + offset) + value : value; 185 *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle); 186 } 187 188err_fail: 189 kfree(propval); 190 return err; 191} 192 193/* 194 * Adjust the local phandle references by the given phandle delta. 195 * Assumes the existances of a __local_fixups__ node at the root 196 * of the tree. Does not take any devtree locks so make sure you 197 * call this on a tree which is at the detached state. 198 */ 199static int __of_adjust_tree_phandle_references(struct device_node *node, 200 int phandle_delta) 201{ 202 struct device_node *child; 203 struct property *rprop; 204 int err; 205 206 /* locate the symbols & fixups nodes on resolve */ 207 for_each_child_of_node(node, child) 208 if (of_node_cmp(child->name, "__local_fixups__") == 0) 209 break; 210 211 /* no local fixups */ 212 if (!child) 213 return 0; 214 215 /* find the local fixups property */ 216 for_each_property_of_node(child, rprop) { 217 /* skip properties added automatically */ 218 if (of_prop_cmp(rprop->name, "name") == 0) 219 continue; 220 221 err = __of_adjust_phandle_ref(node, rprop, phandle_delta, true); 222 if (err) 223 return err; 224 } 225 226 return 0; 227} 228 229/** 230 * of_resolve - Resolve the given node against the live tree. 231 * 232 * @resolve: Node to resolve 233 * 234 * Perform dynamic Device Tree resolution against the live tree 235 * to the given node to resolve. This depends on the live tree 236 * having a __symbols__ node, and the resolve node the __fixups__ & 237 * __local_fixups__ nodes (if needed). 238 * The result of the operation is a resolve node that it's contents 239 * are fit to be inserted or operate upon the live tree. 240 * Returns 0 on success or a negative error value on error. 241 */ 242int of_resolve_phandles(struct device_node *resolve) 243{ 244 struct device_node *child, *refnode; 245 struct device_node *root_sym, *resolve_sym, *resolve_fix; 246 struct property *rprop; 247 const char *refpath; 248 phandle phandle, phandle_delta; 249 int err; 250 251 /* the resolve node must exist, and be detached */ 252 if (!resolve || !of_node_check_flag(resolve, OF_DETACHED)) 253 return -EINVAL; 254 255 /* first we need to adjust the phandles */ 256 phandle_delta = of_get_tree_max_phandle() + 1; 257 __of_adjust_tree_phandles(resolve, phandle_delta); 258 err = __of_adjust_tree_phandle_references(resolve, phandle_delta); 259 if (err != 0) 260 return err; 261 262 root_sym = NULL; 263 resolve_sym = NULL; 264 resolve_fix = NULL; 265 266 /* this may fail (if no fixups are required) */ 267 root_sym = of_find_node_by_path("/__symbols__"); 268 269 /* locate the symbols & fixups nodes on resolve */ 270 for_each_child_of_node(resolve, child) { 271 272 if (!resolve_sym && 273 of_node_cmp(child->name, "__symbols__") == 0) 274 resolve_sym = child; 275 276 if (!resolve_fix && 277 of_node_cmp(child->name, "__fixups__") == 0) 278 resolve_fix = child; 279 280 /* both found, don't bother anymore */ 281 if (resolve_sym && resolve_fix) 282 break; 283 } 284 285 /* we do allow for the case where no fixups are needed */ 286 if (!resolve_fix) { 287 err = 0; /* no error */ 288 goto out; 289 } 290 291 /* we need to fixup, but no root symbols... */ 292 if (!root_sym) { 293 err = -EINVAL; 294 goto out; 295 } 296 297 for_each_property_of_node(resolve_fix, rprop) { 298 299 /* skip properties added automatically */ 300 if (of_prop_cmp(rprop->name, "name") == 0) 301 continue; 302 303 err = of_property_read_string(root_sym, 304 rprop->name, &refpath); 305 if (err != 0) { 306 pr_err("%s: Could not find symbol '%s'\n", 307 __func__, rprop->name); 308 goto out; 309 } 310 311 refnode = of_find_node_by_path(refpath); 312 if (!refnode) { 313 pr_err("%s: Could not find node by path '%s'\n", 314 __func__, refpath); 315 err = -ENOENT; 316 goto out; 317 } 318 319 phandle = refnode->phandle; 320 of_node_put(refnode); 321 322 pr_debug("%s: %s phandle is 0x%08x\n", 323 __func__, rprop->name, phandle); 324 325 err = __of_adjust_phandle_ref(resolve, rprop, phandle, false); 326 if (err) 327 break; 328 } 329 330out: 331 /* NULL is handled by of_node_put as NOP */ 332 of_node_put(root_sym); 333 334 return err; 335} 336EXPORT_SYMBOL_GPL(of_resolve_phandles); 337