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