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 *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)
  37                return NULL;
  38
  39        if (!of_node_cmp(node->full_name, full_name))
  40                return of_node_get(node);
  41
  42        for_each_child_of_node(node, child) {
  43                found = find_node_by_full_name(child, full_name);
  44                if (found != NULL) {
  45                        of_node_put(child);
  46                        return found;
  47                }
  48        }
  49
  50        return NULL;
  51}
  52
  53static phandle live_tree_max_phandle(void)
  54{
  55        struct device_node *node;
  56        phandle phandle;
  57        unsigned long flags;
  58
  59        raw_spin_lock_irqsave(&devtree_lock, flags);
  60        phandle = 0;
  61        for_each_of_allnodes(node) {
  62                if (node->phandle != OF_PHANDLE_ILLEGAL &&
  63                                node->phandle > phandle)
  64                        phandle = node->phandle;
  65        }
  66        raw_spin_unlock_irqrestore(&devtree_lock, flags);
  67
  68        return phandle;
  69}
  70
  71static void adjust_overlay_phandles(struct device_node *overlay,
  72                int phandle_delta)
  73{
  74        struct device_node *child;
  75        struct property *prop;
  76        phandle phandle;
  77
  78        /* adjust node's phandle in node */
  79        if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL)
  80                overlay->phandle += phandle_delta;
  81
  82        /* copy adjusted phandle into *phandle properties */
  83        for_each_property_of_node(overlay, prop) {
  84
  85                if (of_prop_cmp(prop->name, "phandle") &&
  86                    of_prop_cmp(prop->name, "linux,phandle"))
  87                        continue;
  88
  89                if (prop->length < 4)
  90                        continue;
  91
  92                phandle = be32_to_cpup(prop->value);
  93                if (phandle == OF_PHANDLE_ILLEGAL)
  94                        continue;
  95
  96                *(uint32_t *)prop->value = cpu_to_be32(overlay->phandle);
  97        }
  98
  99        for_each_child_of_node(overlay, child)
 100                adjust_overlay_phandles(child, phandle_delta);
 101}
 102
 103static int update_usages_of_a_phandle_reference(struct device_node *overlay,
 104                struct property *prop_fixup, phandle phandle)
 105{
 106        struct device_node *refnode;
 107        struct property *prop;
 108        char *value, *cur, *end, *node_path, *prop_name, *s;
 109        int offset, len;
 110        int err = 0;
 111
 112        value = kmalloc(prop_fixup->length, GFP_KERNEL);
 113        if (!value)
 114                return -ENOMEM;
 115        memcpy(value, prop_fixup->value, prop_fixup->length);
 116
 117        /* prop_fixup contains a list of tuples of path:property_name:offset */
 118        end = value + prop_fixup->length;
 119        for (cur = value; cur < end; cur += len + 1) {
 120                len = strlen(cur);
 121
 122                node_path = cur;
 123                s = strchr(cur, ':');
 124                if (!s) {
 125                        err = -EINVAL;
 126                        goto err_fail;
 127                }
 128                *s++ = '\0';
 129
 130                prop_name = s;
 131                s = strchr(s, ':');
 132                if (!s) {
 133                        err = -EINVAL;
 134                        goto err_fail;
 135                }
 136                *s++ = '\0';
 137
 138                err = kstrtoint(s, 10, &offset);
 139                if (err)
 140                        goto err_fail;
 141
 142                refnode = find_node_by_full_name(overlay, node_path);
 143                if (!refnode)
 144                        continue;
 145
 146                for_each_property_of_node(refnode, prop) {
 147                        if (!of_prop_cmp(prop->name, prop_name))
 148                                break;
 149                }
 150                of_node_put(refnode);
 151
 152                if (!prop) {
 153                        err = -ENOENT;
 154                        goto err_fail;
 155                }
 156
 157                *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
 158        }
 159
 160err_fail:
 161        kfree(value);
 162        return err;
 163}
 164
 165/* compare nodes taking into account that 'name' strips out the @ part */
 166static int node_name_cmp(const struct device_node *dn1,
 167                const struct device_node *dn2)
 168{
 169        const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
 170        const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
 171
 172        return of_node_cmp(n1, n2);
 173}
 174
 175/*
 176 * Adjust the local phandle references by the given phandle delta.
 177 *
 178 * Subtree @local_fixups, which is overlay node __local_fixups__,
 179 * mirrors the fragment node structure at the root of the overlay.
 180 *
 181 * For each property in the fragments that contains a phandle reference,
 182 * @local_fixups has a property of the same name that contains a list
 183 * of offsets of the phandle reference(s) within the respective property
 184 * value(s).  The values at these offsets will be fixed up.
 185 */
 186static int adjust_local_phandle_references(struct device_node *local_fixups,
 187                struct device_node *overlay, int phandle_delta)
 188{
 189        struct device_node *child, *overlay_child;
 190        struct property *prop_fix, *prop;
 191        int err, i, count;
 192        unsigned int off;
 193        phandle phandle;
 194
 195        if (!local_fixups)
 196                return 0;
 197
 198        for_each_property_of_node(local_fixups, prop_fix) {
 199
 200                /* skip properties added automatically */
 201                if (!of_prop_cmp(prop_fix->name, "name") ||
 202                    !of_prop_cmp(prop_fix->name, "phandle") ||
 203                    !of_prop_cmp(prop_fix->name, "linux,phandle"))
 204                        continue;
 205
 206                if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
 207                        return -EINVAL;
 208                count = prop_fix->length / sizeof(__be32);
 209
 210                for_each_property_of_node(overlay, prop) {
 211                        if (!of_prop_cmp(prop->name, prop_fix->name))
 212                                break;
 213                }
 214
 215                if (!prop)
 216                        return -EINVAL;
 217
 218                for (i = 0; i < count; i++) {
 219                        off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
 220                        if ((off + 4) > prop->length)
 221                                return -EINVAL;
 222
 223                        phandle = be32_to_cpu(*(__be32 *)(prop->value + off));
 224                        phandle += phandle_delta;
 225                        *(__be32 *)(prop->value + off) = cpu_to_be32(phandle);
 226                }
 227        }
 228
 229        /*
 230         * These nested loops recurse down two subtrees in parallel, where the
 231         * node names in the two subtrees match.
 232         *
 233         * The roots of the subtrees are the overlay's __local_fixups__ node
 234         * and the overlay's root node.
 235         */
 236        for_each_child_of_node(local_fixups, child) {
 237
 238                for_each_child_of_node(overlay, overlay_child)
 239                        if (!node_name_cmp(child, overlay_child))
 240                                break;
 241
 242                if (!overlay_child)
 243                        return -EINVAL;
 244
 245                err = adjust_local_phandle_references(child, overlay_child,
 246                                phandle_delta);
 247                if (err)
 248                        return err;
 249        }
 250
 251        return 0;
 252}
 253
 254/**
 255 * of_resolve_phandles - Relocate and resolve overlay against live tree
 256 *
 257 * @overlay:    Pointer to devicetree overlay to relocate and resolve
 258 *
 259 * Modify (relocate) values of local phandles in @overlay to a range that
 260 * does not conflict with the live expanded devicetree.  Update references
 261 * to the local phandles in @overlay.  Update (resolve) phandle references
 262 * in @overlay that refer to the live expanded devicetree.
 263 *
 264 * Phandle values in the live tree are in the range of
 265 * 1 .. live_tree_max_phandle().  The range of phandle values in the overlay
 266 * also begin with at 1.  Adjust the phandle values in the overlay to begin
 267 * at live_tree_max_phandle() + 1.  Update references to the phandles to
 268 * the adjusted phandle values.
 269 *
 270 * The name of each property in the "__fixups__" node in the overlay matches
 271 * the name of a symbol (a label) in the live tree.  The values of each
 272 * property in the "__fixups__" node is a list of the property values in the
 273 * overlay that need to be updated to contain the phandle reference
 274 * corresponding to that symbol in the live tree.  Update the references in
 275 * the overlay with the phandle values in the live tree.
 276 *
 277 * @overlay must be detached.
 278 *
 279 * Resolving and applying @overlay to the live expanded devicetree must be
 280 * protected by a mechanism to ensure that multiple overlays are processed
 281 * in a single threaded manner so that multiple overlays will not relocate
 282 * phandles to overlapping ranges.  The mechanism to enforce this is not
 283 * yet implemented.
 284 *
 285 * Return: %0 on success or a negative error value on error.
 286 */
 287int of_resolve_phandles(struct device_node *overlay)
 288{
 289        struct device_node *child, *local_fixups, *refnode;
 290        struct device_node *tree_symbols, *overlay_fixups;
 291        struct property *prop;
 292        const char *refpath;
 293        phandle phandle, phandle_delta;
 294        int err;
 295
 296        tree_symbols = NULL;
 297
 298        if (!overlay) {
 299                pr_err("null overlay\n");
 300                err = -EINVAL;
 301                goto out;
 302        }
 303        if (!of_node_check_flag(overlay, OF_DETACHED)) {
 304                pr_err("overlay not detached\n");
 305                err = -EINVAL;
 306                goto out;
 307        }
 308
 309        phandle_delta = live_tree_max_phandle() + 1;
 310        adjust_overlay_phandles(overlay, phandle_delta);
 311
 312        for_each_child_of_node(overlay, local_fixups)
 313                if (!of_node_cmp(local_fixups->name, "__local_fixups__"))
 314                        break;
 315
 316        err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
 317        if (err)
 318                goto out;
 319
 320        overlay_fixups = NULL;
 321
 322        for_each_child_of_node(overlay, child) {
 323                if (!of_node_cmp(child->name, "__fixups__"))
 324                        overlay_fixups = child;
 325        }
 326
 327        if (!overlay_fixups) {
 328                err = 0;
 329                goto out;
 330        }
 331
 332        tree_symbols = of_find_node_by_path("/__symbols__");
 333        if (!tree_symbols) {
 334                pr_err("no symbols in root of device tree.\n");
 335                err = -EINVAL;
 336                goto out;
 337        }
 338
 339        for_each_property_of_node(overlay_fixups, prop) {
 340
 341                /* skip properties added automatically */
 342                if (!of_prop_cmp(prop->name, "name"))
 343                        continue;
 344
 345                err = of_property_read_string(tree_symbols,
 346                                prop->name, &refpath);
 347                if (err)
 348                        goto out;
 349
 350                refnode = of_find_node_by_path(refpath);
 351                if (!refnode) {
 352                        err = -ENOENT;
 353                        goto out;
 354                }
 355
 356                phandle = refnode->phandle;
 357                of_node_put(refnode);
 358
 359                err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
 360                if (err)
 361                        break;
 362        }
 363
 364out:
 365        if (err)
 366                pr_err("overlay phandle fixup failed: %d\n", err);
 367        of_node_put(tree_symbols);
 368
 369        return err;
 370}
 371EXPORT_SYMBOL_GPL(of_resolve_phandles);
 372