|  | /* | 
|  | * Functions for dealing with DT resolution | 
|  | * | 
|  | * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com> | 
|  | * Copyright (C) 2012 Texas Instruments Inc. | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public License | 
|  | * version 2 as published by the Free Software Foundation. | 
|  | */ | 
|  |  | 
|  | #define pr_fmt(fmt)	"OF: resolver: " fmt | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/of_device.h> | 
|  | #include <linux/string.h> | 
|  | #include <linux/ctype.h> | 
|  | #include <linux/errno.h> | 
|  | #include <linux/string.h> | 
|  | #include <linux/slab.h> | 
|  |  | 
|  | /* illegal phandle value (set when unresolved) */ | 
|  | #define OF_PHANDLE_ILLEGAL	0xdeadbeef | 
|  |  | 
|  | /** | 
|  | * Find a node with the give full name by recursively following any of | 
|  | * the child node links. | 
|  | */ | 
|  | static struct device_node *__of_find_node_by_full_name(struct device_node *node, | 
|  | const char *full_name) | 
|  | { | 
|  | struct device_node *child, *found; | 
|  |  | 
|  | if (node == NULL) | 
|  | return NULL; | 
|  |  | 
|  | /* check */ | 
|  | if (of_node_cmp(node->full_name, full_name) == 0) | 
|  | return of_node_get(node); | 
|  |  | 
|  | for_each_child_of_node(node, child) { | 
|  | found = __of_find_node_by_full_name(child, full_name); | 
|  | if (found != NULL) { | 
|  | of_node_put(child); | 
|  | return found; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Find live tree's maximum phandle value. | 
|  | */ | 
|  | static phandle of_get_tree_max_phandle(void) | 
|  | { | 
|  | struct device_node *node; | 
|  | phandle phandle; | 
|  | unsigned long flags; | 
|  |  | 
|  | /* now search recursively */ | 
|  | raw_spin_lock_irqsave(&devtree_lock, flags); | 
|  | phandle = 0; | 
|  | for_each_of_allnodes(node) { | 
|  | if (node->phandle != OF_PHANDLE_ILLEGAL && | 
|  | node->phandle > phandle) | 
|  | phandle = node->phandle; | 
|  | } | 
|  | raw_spin_unlock_irqrestore(&devtree_lock, flags); | 
|  |  | 
|  | return phandle; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Adjust a subtree's phandle values by a given delta. | 
|  | * Makes sure not to just adjust the device node's phandle value, | 
|  | * but modify the phandle properties values as well. | 
|  | */ | 
|  | static void __of_adjust_tree_phandles(struct device_node *node, | 
|  | int phandle_delta) | 
|  | { | 
|  | struct device_node *child; | 
|  | struct property *prop; | 
|  | phandle phandle; | 
|  |  | 
|  | /* first adjust the node's phandle direct value */ | 
|  | if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL) | 
|  | node->phandle += phandle_delta; | 
|  |  | 
|  | /* now adjust phandle & linux,phandle values */ | 
|  | for_each_property_of_node(node, prop) { | 
|  |  | 
|  | /* only look for these two */ | 
|  | if (of_prop_cmp(prop->name, "phandle") != 0 && | 
|  | of_prop_cmp(prop->name, "linux,phandle") != 0) | 
|  | continue; | 
|  |  | 
|  | /* must be big enough */ | 
|  | if (prop->length < 4) | 
|  | continue; | 
|  |  | 
|  | /* read phandle value */ | 
|  | phandle = be32_to_cpup(prop->value); | 
|  | if (phandle == OF_PHANDLE_ILLEGAL)	/* unresolved */ | 
|  | continue; | 
|  |  | 
|  | /* adjust */ | 
|  | *(uint32_t *)prop->value = cpu_to_be32(node->phandle); | 
|  | } | 
|  |  | 
|  | /* now do the children recursively */ | 
|  | for_each_child_of_node(node, child) | 
|  | __of_adjust_tree_phandles(child, phandle_delta); | 
|  | } | 
|  |  | 
|  | static int __of_adjust_phandle_ref(struct device_node *node, | 
|  | struct property *rprop, int value) | 
|  | { | 
|  | phandle phandle; | 
|  | struct device_node *refnode; | 
|  | struct property *sprop; | 
|  | char *propval, *propcur, *propend, *nodestr, *propstr, *s; | 
|  | int offset, propcurlen; | 
|  | int err = 0; | 
|  |  | 
|  | /* make a copy */ | 
|  | propval = kmalloc(rprop->length, GFP_KERNEL); | 
|  | if (!propval) { | 
|  | pr_err("%s: Could not copy value of '%s'\n", | 
|  | __func__, rprop->name); | 
|  | return -ENOMEM; | 
|  | } | 
|  | memcpy(propval, rprop->value, rprop->length); | 
|  |  | 
|  | propend = propval + rprop->length; | 
|  | for (propcur = propval; propcur < propend; propcur += propcurlen + 1) { | 
|  | propcurlen = strlen(propcur); | 
|  |  | 
|  | nodestr = propcur; | 
|  | s = strchr(propcur, ':'); | 
|  | if (!s) { | 
|  | pr_err("%s: Illegal symbol entry '%s' (1)\n", | 
|  | __func__, propcur); | 
|  | err = -EINVAL; | 
|  | goto err_fail; | 
|  | } | 
|  | *s++ = '\0'; | 
|  |  | 
|  | propstr = s; | 
|  | s = strchr(s, ':'); | 
|  | if (!s) { | 
|  | pr_err("%s: Illegal symbol entry '%s' (2)\n", | 
|  | __func__, (char *)rprop->value); | 
|  | err = -EINVAL; | 
|  | goto err_fail; | 
|  | } | 
|  |  | 
|  | *s++ = '\0'; | 
|  | err = kstrtoint(s, 10, &offset); | 
|  | if (err != 0) { | 
|  | pr_err("%s: Could get offset '%s'\n", | 
|  | __func__, (char *)rprop->value); | 
|  | goto err_fail; | 
|  | } | 
|  |  | 
|  | /* look into the resolve node for the full path */ | 
|  | refnode = __of_find_node_by_full_name(node, nodestr); | 
|  | if (!refnode) { | 
|  | pr_warn("%s: Could not find refnode '%s'\n", | 
|  | __func__, (char *)rprop->value); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* now find the property */ | 
|  | for_each_property_of_node(refnode, sprop) { | 
|  | if (of_prop_cmp(sprop->name, propstr) == 0) | 
|  | break; | 
|  | } | 
|  | of_node_put(refnode); | 
|  |  | 
|  | if (!sprop) { | 
|  | pr_err("%s: Could not find property '%s'\n", | 
|  | __func__, (char *)rprop->value); | 
|  | err = -ENOENT; | 
|  | goto err_fail; | 
|  | } | 
|  |  | 
|  | phandle = value; | 
|  | *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle); | 
|  | } | 
|  |  | 
|  | err_fail: | 
|  | kfree(propval); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /* compare nodes taking into account that 'name' strips out the @ part */ | 
|  | static int __of_node_name_cmp(const struct device_node *dn1, | 
|  | const struct device_node *dn2) | 
|  | { | 
|  | const char *n1 = strrchr(dn1->full_name, '/') ? : "/"; | 
|  | const char *n2 = strrchr(dn2->full_name, '/') ? : "/"; | 
|  |  | 
|  | return of_node_cmp(n1, n2); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Adjust the local phandle references by the given phandle delta. | 
|  | * Assumes the existances of a __local_fixups__ node at the root. | 
|  | * Assumes that __of_verify_tree_phandle_references has been called. | 
|  | * Does not take any devtree locks so make sure you call this on a tree | 
|  | * which is at the detached state. | 
|  | */ | 
|  | static int __of_adjust_tree_phandle_references(struct device_node *node, | 
|  | struct device_node *target, int phandle_delta) | 
|  | { | 
|  | struct device_node *child, *childtarget; | 
|  | struct property *rprop, *sprop; | 
|  | int err, i, count; | 
|  | unsigned int off; | 
|  | phandle phandle; | 
|  |  | 
|  | if (node == NULL) | 
|  | return 0; | 
|  |  | 
|  | for_each_property_of_node(node, rprop) { | 
|  |  | 
|  | /* skip properties added automatically */ | 
|  | if (of_prop_cmp(rprop->name, "name") == 0 || | 
|  | of_prop_cmp(rprop->name, "phandle") == 0 || | 
|  | of_prop_cmp(rprop->name, "linux,phandle") == 0) | 
|  | continue; | 
|  |  | 
|  | if ((rprop->length % 4) != 0 || rprop->length == 0) { | 
|  | pr_err("%s: Illegal property (size) '%s' @%s\n", | 
|  | __func__, rprop->name, node->full_name); | 
|  | return -EINVAL; | 
|  | } | 
|  | count = rprop->length / sizeof(__be32); | 
|  |  | 
|  | /* now find the target property */ | 
|  | for_each_property_of_node(target, sprop) { | 
|  | if (of_prop_cmp(sprop->name, rprop->name) == 0) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (sprop == NULL) { | 
|  | pr_err("%s: Could not find target property '%s' @%s\n", | 
|  | __func__, rprop->name, node->full_name); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | off = be32_to_cpu(((__be32 *)rprop->value)[i]); | 
|  | /* make sure the offset doesn't overstep (even wrap) */ | 
|  | if (off >= sprop->length || | 
|  | (off + 4) > sprop->length) { | 
|  | pr_err("%s: Illegal property '%s' @%s\n", | 
|  | __func__, rprop->name, | 
|  | node->full_name); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (phandle_delta) { | 
|  | /* adjust */ | 
|  | phandle = be32_to_cpu(*(__be32 *)(sprop->value + off)); | 
|  | phandle += phandle_delta; | 
|  | *(__be32 *)(sprop->value + off) = cpu_to_be32(phandle); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for_each_child_of_node(node, child) { | 
|  |  | 
|  | for_each_child_of_node(target, childtarget) | 
|  | if (__of_node_name_cmp(child, childtarget) == 0) | 
|  | break; | 
|  |  | 
|  | if (!childtarget) { | 
|  | pr_err("%s: Could not find target child '%s' @%s\n", | 
|  | __func__, child->name, node->full_name); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | err = __of_adjust_tree_phandle_references(child, childtarget, | 
|  | phandle_delta); | 
|  | if (err != 0) | 
|  | return err; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * of_resolve	- Resolve the given node against the live tree. | 
|  | * | 
|  | * @resolve:	Node to resolve | 
|  | * | 
|  | * Perform dynamic Device Tree resolution against the live tree | 
|  | * to the given node to resolve. This depends on the live tree | 
|  | * having a __symbols__ node, and the resolve node the __fixups__ & | 
|  | * __local_fixups__ nodes (if needed). | 
|  | * The result of the operation is a resolve node that it's contents | 
|  | * are fit to be inserted or operate upon the live tree. | 
|  | * Returns 0 on success or a negative error value on error. | 
|  | */ | 
|  | int of_resolve_phandles(struct device_node *resolve) | 
|  | { | 
|  | struct device_node *child, *childroot, *refnode; | 
|  | struct device_node *root_sym, *resolve_sym, *resolve_fix; | 
|  | struct property *rprop; | 
|  | const char *refpath; | 
|  | phandle phandle, phandle_delta; | 
|  | int err; | 
|  |  | 
|  | if (!resolve) | 
|  | pr_err("%s: null node\n", __func__); | 
|  | if (resolve && !of_node_check_flag(resolve, OF_DETACHED)) | 
|  | pr_err("%s: node %s not detached\n", __func__, | 
|  | resolve->full_name); | 
|  | /* the resolve node must exist, and be detached */ | 
|  | if (!resolve || !of_node_check_flag(resolve, OF_DETACHED)) | 
|  | return -EINVAL; | 
|  |  | 
|  | /* first we need to adjust the phandles */ | 
|  | phandle_delta = of_get_tree_max_phandle() + 1; | 
|  | __of_adjust_tree_phandles(resolve, phandle_delta); | 
|  |  | 
|  | /* locate the local fixups */ | 
|  | childroot = NULL; | 
|  | for_each_child_of_node(resolve, childroot) | 
|  | if (of_node_cmp(childroot->name, "__local_fixups__") == 0) | 
|  | break; | 
|  |  | 
|  | if (childroot != NULL) { | 
|  | /* resolve root is guaranteed to be the '/' */ | 
|  | err = __of_adjust_tree_phandle_references(childroot, | 
|  | resolve, 0); | 
|  | if (err != 0) | 
|  | return err; | 
|  |  | 
|  | BUG_ON(__of_adjust_tree_phandle_references(childroot, | 
|  | resolve, phandle_delta)); | 
|  | } | 
|  |  | 
|  | root_sym = NULL; | 
|  | resolve_sym = NULL; | 
|  | resolve_fix = NULL; | 
|  |  | 
|  | /* this may fail (if no fixups are required) */ | 
|  | root_sym = of_find_node_by_path("/__symbols__"); | 
|  |  | 
|  | /* locate the symbols & fixups nodes on resolve */ | 
|  | for_each_child_of_node(resolve, child) { | 
|  |  | 
|  | if (!resolve_sym && | 
|  | of_node_cmp(child->name, "__symbols__") == 0) | 
|  | resolve_sym = child; | 
|  |  | 
|  | if (!resolve_fix && | 
|  | of_node_cmp(child->name, "__fixups__") == 0) | 
|  | resolve_fix = child; | 
|  |  | 
|  | /* both found, don't bother anymore */ | 
|  | if (resolve_sym && resolve_fix) | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* we do allow for the case where no fixups are needed */ | 
|  | if (!resolve_fix) { | 
|  | err = 0;	/* no error */ | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | /* we need to fixup, but no root symbols... */ | 
|  | if (!root_sym) { | 
|  | pr_err("%s: no symbols in root of device tree.\n", __func__); | 
|  | err = -EINVAL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | for_each_property_of_node(resolve_fix, rprop) { | 
|  |  | 
|  | /* skip properties added automatically */ | 
|  | if (of_prop_cmp(rprop->name, "name") == 0) | 
|  | continue; | 
|  |  | 
|  | err = of_property_read_string(root_sym, | 
|  | rprop->name, &refpath); | 
|  | if (err != 0) { | 
|  | pr_err("%s: Could not find symbol '%s'\n", | 
|  | __func__, rprop->name); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | refnode = of_find_node_by_path(refpath); | 
|  | if (!refnode) { | 
|  | pr_err("%s: Could not find node by path '%s'\n", | 
|  | __func__, refpath); | 
|  | err = -ENOENT; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | phandle = refnode->phandle; | 
|  | of_node_put(refnode); | 
|  |  | 
|  | pr_debug("%s: %s phandle is 0x%08x\n", | 
|  | __func__, rprop->name, phandle); | 
|  |  | 
|  | err = __of_adjust_phandle_ref(resolve, rprop, phandle); | 
|  | if (err) | 
|  | break; | 
|  | } | 
|  |  | 
|  | out: | 
|  | /* NULL is handled by of_node_put as NOP */ | 
|  | of_node_put(root_sym); | 
|  |  | 
|  | return err; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(of_resolve_phandles); |