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