Linux Audio

Check our new training course

Loading...
v4.6
 
  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 of_node_get(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			of_node_put(child);
 45			return found;
 46		}
 47	}
 48
 49	return NULL;
 50}
 51
 52/*
 53 * Find live tree's maximum phandle value.
 54 */
 55static phandle of_get_tree_max_phandle(void)
 56{
 57	struct device_node *node;
 58	phandle phandle;
 59	unsigned long flags;
 60
 61	/* now search recursively */
 62	raw_spin_lock_irqsave(&devtree_lock, flags);
 63	phandle = 0;
 64	for_each_of_allnodes(node) {
 65		if (node->phandle != OF_PHANDLE_ILLEGAL &&
 66				node->phandle > phandle)
 67			phandle = node->phandle;
 68	}
 69	raw_spin_unlock_irqrestore(&devtree_lock, flags);
 70
 71	return phandle;
 72}
 73
 74/*
 75 * Adjust a subtree's phandle values by a given delta.
 76 * Makes sure not to just adjust the device node's phandle value,
 77 * but modify the phandle properties values as well.
 78 */
 79static void __of_adjust_tree_phandles(struct device_node *node,
 80		int phandle_delta)
 81{
 82	struct device_node *child;
 83	struct property *prop;
 84	phandle phandle;
 85
 86	/* first adjust the node's phandle direct value */
 87	if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
 88		node->phandle += phandle_delta;
 89
 90	/* now adjust phandle & linux,phandle values */
 91	for_each_property_of_node(node, prop) {
 92
 93		/* only look for these two */
 94		if (of_prop_cmp(prop->name, "phandle") != 0 &&
 95		    of_prop_cmp(prop->name, "linux,phandle") != 0)
 96			continue;
 97
 98		/* must be big enough */
 99		if (prop->length < 4)
100			continue;
101
102		/* read phandle value */
103		phandle = be32_to_cpup(prop->value);
104		if (phandle == OF_PHANDLE_ILLEGAL)	/* unresolved */
105			continue;
106
107		/* adjust */
108		*(uint32_t *)prop->value = cpu_to_be32(node->phandle);
109	}
110
111	/* now do the children recursively */
112	for_each_child_of_node(node, child)
113		__of_adjust_tree_phandles(child, phandle_delta);
114}
115
116static int __of_adjust_phandle_ref(struct device_node *node,
117		struct property *rprop, int value)
118{
119	phandle phandle;
120	struct device_node *refnode;
121	struct property *sprop;
122	char *propval, *propcur, *propend, *nodestr, *propstr, *s;
123	int offset, propcurlen;
124	int err = 0;
125
126	/* make a copy */
127	propval = kmalloc(rprop->length, GFP_KERNEL);
128	if (!propval) {
129		pr_err("%s: Could not copy value of '%s'\n",
130				__func__, rprop->name);
131		return -ENOMEM;
132	}
133	memcpy(propval, rprop->value, rprop->length);
134
135	propend = propval + rprop->length;
136	for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
137		propcurlen = strlen(propcur);
 
138
139		nodestr = propcur;
140		s = strchr(propcur, ':');
141		if (!s) {
142			pr_err("%s: Illegal symbol entry '%s' (1)\n",
143				__func__, propcur);
144			err = -EINVAL;
145			goto err_fail;
146		}
147		*s++ = '\0';
148
149		propstr = s;
150		s = strchr(s, ':');
151		if (!s) {
152			pr_err("%s: Illegal symbol entry '%s' (2)\n",
153				__func__, (char *)rprop->value);
154			err = -EINVAL;
155			goto err_fail;
156		}
157
158		*s++ = '\0';
 
159		err = kstrtoint(s, 10, &offset);
160		if (err != 0) {
161			pr_err("%s: Could get offset '%s'\n",
162				__func__, (char *)rprop->value);
163			goto err_fail;
164		}
165
166		/* look into the resolve node for the full path */
167		refnode = __of_find_node_by_full_name(node, nodestr);
168		if (!refnode) {
169			pr_warn("%s: Could not find refnode '%s'\n",
170				__func__, (char *)rprop->value);
171			continue;
172		}
173
174		/* now find the property */
175		for_each_property_of_node(refnode, sprop) {
176			if (of_prop_cmp(sprop->name, propstr) == 0)
177				break;
178		}
179		of_node_put(refnode);
180
181		if (!sprop) {
182			pr_err("%s: Could not find property '%s'\n",
183				__func__, (char *)rprop->value);
184			err = -ENOENT;
185			goto err_fail;
186		}
187
188		phandle = value;
189		*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
190	}
191
192err_fail:
193	kfree(propval);
194	return err;
195}
196
197/* compare nodes taking into account that 'name' strips out the @ part */
198static int __of_node_name_cmp(const struct device_node *dn1,
199		const struct device_node *dn2)
200{
201	const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
202	const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
203
204	return of_node_cmp(n1, n2);
205}
206
207/*
208 * Adjust the local phandle references by the given phandle delta.
209 * Assumes the existances of a __local_fixups__ node at the root.
210 * Assumes that __of_verify_tree_phandle_references has been called.
211 * Does not take any devtree locks so make sure you call this on a tree
212 * which is at the detached state.
 
 
 
 
213 */
214static int __of_adjust_tree_phandle_references(struct device_node *node,
215		struct device_node *target, int phandle_delta)
216{
217	struct device_node *child, *childtarget;
218	struct property *rprop, *sprop;
219	int err, i, count;
220	unsigned int off;
221	phandle phandle;
222
223	if (node == NULL)
224		return 0;
225
226	for_each_property_of_node(node, rprop) {
227
228		/* skip properties added automatically */
229		if (of_prop_cmp(rprop->name, "name") == 0 ||
230		    of_prop_cmp(rprop->name, "phandle") == 0 ||
231		    of_prop_cmp(rprop->name, "linux,phandle") == 0)
232			continue;
233
234		if ((rprop->length % 4) != 0 || rprop->length == 0) {
235			pr_err("%s: Illegal property (size) '%s' @%s\n",
236					__func__, rprop->name, node->full_name);
237			return -EINVAL;
238		}
239		count = rprop->length / sizeof(__be32);
240
241		/* now find the target property */
242		for_each_property_of_node(target, sprop) {
243			if (of_prop_cmp(sprop->name, rprop->name) == 0)
244				break;
245		}
246
247		if (sprop == NULL) {
248			pr_err("%s: Could not find target property '%s' @%s\n",
249					__func__, rprop->name, node->full_name);
250			return -EINVAL;
251		}
252
253		for (i = 0; i < count; i++) {
254			off = be32_to_cpu(((__be32 *)rprop->value)[i]);
255			/* make sure the offset doesn't overstep (even wrap) */
256			if (off >= sprop->length ||
257					(off + 4) > sprop->length) {
258				pr_err("%s: Illegal property '%s' @%s\n",
259						__func__, rprop->name,
260						node->full_name);
261				return -EINVAL;
262			}
263
264			if (phandle_delta) {
265				/* adjust */
266				phandle = be32_to_cpu(*(__be32 *)(sprop->value + off));
267				phandle += phandle_delta;
268				*(__be32 *)(sprop->value + off) = cpu_to_be32(phandle);
269			}
270		}
271	}
272
273	for_each_child_of_node(node, child) {
 
 
 
 
 
 
 
274
275		for_each_child_of_node(target, childtarget)
276			if (__of_node_name_cmp(child, childtarget) == 0)
277				break;
278
279		if (!childtarget) {
280			pr_err("%s: Could not find target child '%s' @%s\n",
281					__func__, child->name, node->full_name);
282			return -EINVAL;
283		}
284
285		err = __of_adjust_tree_phandle_references(child, childtarget,
286				phandle_delta);
287		if (err != 0)
288			return err;
289	}
290
291	return 0;
292}
293
294/**
295 * of_resolve	- Resolve the given node against the live tree.
296 *
297 * @resolve:	Node to resolve
298 *
299 * Perform dynamic Device Tree resolution against the live tree
300 * to the given node to resolve. This depends on the live tree
301 * having a __symbols__ node, and the resolve node the __fixups__ &
302 * __local_fixups__ nodes (if needed).
303 * The result of the operation is a resolve node that it's contents
304 * are fit to be inserted or operate upon the live tree.
305 * Returns 0 on success or a negative error value on error.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306 */
307int of_resolve_phandles(struct device_node *resolve)
308{
309	struct device_node *child, *childroot, *refnode;
310	struct device_node *root_sym, *resolve_sym, *resolve_fix;
311	struct property *rprop;
312	const char *refpath;
313	phandle phandle, phandle_delta;
314	int err;
315
316	/* the resolve node must exist, and be detached */
317	if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
318		return -EINVAL;
319
320	/* first we need to adjust the phandles */
321	phandle_delta = of_get_tree_max_phandle() + 1;
322	__of_adjust_tree_phandles(resolve, phandle_delta);
323
324	/* locate the local fixups */
325	childroot = NULL;
326	for_each_child_of_node(resolve, childroot)
327		if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
328			break;
329
330	if (childroot != NULL) {
331		/* resolve root is guaranteed to be the '/' */
332		err = __of_adjust_tree_phandle_references(childroot,
333				resolve, 0);
334		if (err != 0)
335			return err;
336
337		BUG_ON(__of_adjust_tree_phandle_references(childroot,
338				resolve, phandle_delta));
339	}
340
341	root_sym = NULL;
342	resolve_sym = NULL;
343	resolve_fix = NULL;
 
 
344
345	/* this may fail (if no fixups are required) */
346	root_sym = of_find_node_by_path("/__symbols__");
347
348	/* locate the symbols & fixups nodes on resolve */
349	for_each_child_of_node(resolve, child) {
 
350
351		if (!resolve_sym &&
352				of_node_cmp(child->name, "__symbols__") == 0)
353			resolve_sym = child;
354
355		if (!resolve_fix &&
356				of_node_cmp(child->name, "__fixups__") == 0)
357			resolve_fix = child;
358
359		/* both found, don't bother anymore */
360		if (resolve_sym && resolve_fix)
361			break;
362	}
363
364	/* we do allow for the case where no fixups are needed */
365	if (!resolve_fix) {
366		err = 0;	/* no error */
367		goto out;
368	}
369
370	/* we need to fixup, but no root symbols... */
371	if (!root_sym) {
 
372		err = -EINVAL;
373		goto out;
374	}
375
376	for_each_property_of_node(resolve_fix, rprop) {
377
378		/* skip properties added automatically */
379		if (of_prop_cmp(rprop->name, "name") == 0)
380			continue;
381
382		err = of_property_read_string(root_sym,
383				rprop->name, &refpath);
384		if (err != 0) {
385			pr_err("%s: Could not find symbol '%s'\n",
386					__func__, rprop->name);
387			goto out;
388		}
389
390		refnode = of_find_node_by_path(refpath);
391		if (!refnode) {
392			pr_err("%s: Could not find node by path '%s'\n",
393					__func__, refpath);
394			err = -ENOENT;
395			goto out;
396		}
397
398		phandle = refnode->phandle;
399		of_node_put(refnode);
400
401		pr_debug("%s: %s phandle is 0x%08x\n",
402				__func__, rprop->name, phandle);
403
404		err = __of_adjust_phandle_ref(resolve, rprop, phandle);
405		if (err)
406			break;
407	}
408
409out:
410	/* NULL is handled by of_node_put as NOP */
411	of_node_put(root_sym);
 
412
413	return err;
414}
415EXPORT_SYMBOL_GPL(of_resolve_phandles);
v4.17
  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		*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
 
126	}
127
128err_fail:
129	kfree(value);
130	return err;
131}
132
133/* compare nodes taking into account that 'name' strips out the @ part */
134static int node_name_cmp(const struct device_node *dn1,
135		const struct device_node *dn2)
136{
137	const char *n1 = kbasename(dn1->full_name);
138	const char *n2 = kbasename(dn2->full_name);
139
140	return of_node_cmp(n1, n2);
141}
142
143/*
144 * Adjust the local phandle references by the given phandle delta.
145 *
146 * Subtree @local_fixups, which is overlay node __local_fixups__,
147 * mirrors the fragment node structure at the root of the overlay.
148 *
149 * For each property in the fragments that contains a phandle reference,
150 * @local_fixups has a property of the same name that contains a list
151 * of offsets of the phandle reference(s) within the respective property
152 * value(s).  The values at these offsets will be fixed up.
153 */
154static int adjust_local_phandle_references(struct device_node *local_fixups,
155		struct device_node *overlay, int phandle_delta)
156{
157	struct device_node *child, *overlay_child;
158	struct property *prop_fix, *prop;
159	int err, i, count;
160	unsigned int off;
 
161
162	if (!local_fixups)
163		return 0;
164
165	for_each_property_of_node(local_fixups, prop_fix) {
166
167		/* skip properties added automatically */
168		if (!of_prop_cmp(prop_fix->name, "name") ||
169		    !of_prop_cmp(prop_fix->name, "phandle") ||
170		    !of_prop_cmp(prop_fix->name, "linux,phandle"))
171			continue;
172
173		if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
 
 
174			return -EINVAL;
175		count = prop_fix->length / sizeof(__be32);
 
176
177		for_each_property_of_node(overlay, prop) {
178			if (!of_prop_cmp(prop->name, prop_fix->name))
 
179				break;
180		}
181
182		if (!prop)
 
 
183			return -EINVAL;
 
184
185		for (i = 0; i < count; i++) {
186			off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
187			if ((off + 4) > prop->length)
 
 
 
 
 
188				return -EINVAL;
 
189
190			be32_add_cpu(prop->value + off, phandle_delta);
 
 
 
 
 
191		}
192	}
193
194	/*
195	 * These nested loops recurse down two subtrees in parallel, where the
196	 * node names in the two subtrees match.
197	 *
198	 * The roots of the subtrees are the overlay's __local_fixups__ node
199	 * and the overlay's root node.
200	 */
201	for_each_child_of_node(local_fixups, child) {
202
203		for_each_child_of_node(overlay, overlay_child)
204			if (!node_name_cmp(child, overlay_child))
205				break;
206
207		if (!overlay_child)
 
 
208			return -EINVAL;
 
209
210		err = adjust_local_phandle_references(child, overlay_child,
211				phandle_delta);
212		if (err)
213			return err;
214	}
215
216	return 0;
217}
218
219/**
220 * of_resolve_phandles - Relocate and resolve overlay against live tree
221 *
222 * @overlay:	Pointer to devicetree overlay to relocate and resolve
223 *
224 * Modify (relocate) values of local phandles in @overlay to a range that
225 * does not conflict with the live expanded devicetree.  Update references
226 * to the local phandles in @overlay.  Update (resolve) phandle references
227 * in @overlay that refer to the live expanded devicetree.
228 *
229 * Phandle values in the live tree are in the range of
230 * 1 .. live_tree_max_phandle().  The range of phandle values in the overlay
231 * also begin with at 1.  Adjust the phandle values in the overlay to begin
232 * at live_tree_max_phandle() + 1.  Update references to the phandles to
233 * the adjusted phandle values.
234 *
235 * The name of each property in the "__fixups__" node in the overlay matches
236 * the name of a symbol (a label) in the live tree.  The values of each
237 * property in the "__fixups__" node is a list of the property values in the
238 * overlay that need to be updated to contain the phandle reference
239 * corresponding to that symbol in the live tree.  Update the references in
240 * the overlay with the phandle values in the live tree.
241 *
242 * @overlay must be detached.
243 *
244 * Resolving and applying @overlay to the live expanded devicetree must be
245 * protected by a mechanism to ensure that multiple overlays are processed
246 * in a single threaded manner so that multiple overlays will not relocate
247 * phandles to overlapping ranges.  The mechanism to enforce this is not
248 * yet implemented.
249 *
250 * Return: %0 on success or a negative error value on error.
251 */
252int of_resolve_phandles(struct device_node *overlay)
253{
254	struct device_node *child, *local_fixups, *refnode;
255	struct device_node *tree_symbols, *overlay_fixups;
256	struct property *prop;
257	const char *refpath;
258	phandle phandle, phandle_delta;
259	int err;
260
261	tree_symbols = NULL;
 
 
 
 
 
 
 
 
 
 
 
 
262
263	if (!overlay) {
264		pr_err("null overlay\n");
265		err = -EINVAL;
266		goto out;
 
 
 
 
 
267	}
268
269	if (!of_node_check_flag(overlay, OF_DETACHED)) {
270		pr_err("overlay not detached\n");
271		err = -EINVAL;
272		goto out;
273	}
274
275	phandle_delta = live_tree_max_phandle() + 1;
276	adjust_overlay_phandles(overlay, phandle_delta);
277
278	for_each_child_of_node(overlay, local_fixups)
279		if (!of_node_cmp(local_fixups->name, "__local_fixups__"))
280			break;
281
282	err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
283	if (err)
284		goto out;
285
286	overlay_fixups = NULL;
 
 
287
288	for_each_child_of_node(overlay, child) {
289		if (!of_node_cmp(child->name, "__fixups__"))
290			overlay_fixups = child;
291	}
292
293	if (!overlay_fixups) {
294		err = 0;
 
295		goto out;
296	}
297
298	tree_symbols = of_find_node_by_path("/__symbols__");
299	if (!tree_symbols) {
300		pr_err("no symbols in root of device tree.\n");
301		err = -EINVAL;
302		goto out;
303	}
304
305	for_each_property_of_node(overlay_fixups, prop) {
306
307		/* skip properties added automatically */
308		if (!of_prop_cmp(prop->name, "name"))
309			continue;
310
311		err = of_property_read_string(tree_symbols,
312				prop->name, &refpath);
313		if (err)
 
 
314			goto out;
 
315
316		refnode = of_find_node_by_path(refpath);
317		if (!refnode) {
 
 
318			err = -ENOENT;
319			goto out;
320		}
321
322		phandle = refnode->phandle;
323		of_node_put(refnode);
324
325		err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
 
 
 
326		if (err)
327			break;
328	}
329
330out:
331	if (err)
332		pr_err("overlay phandle fixup failed: %d\n", err);
333	of_node_put(tree_symbols);
334
335	return err;
336}
337EXPORT_SYMBOL_GPL(of_resolve_phandles);