cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

resolver.c (8896B)


      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);