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