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#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