/** * add_changeset_node() - add @node (and children) to overlay changeset * @ovcs: overlay changeset * @target_node: where to place @node in live tree * @node: node from within overlay device tree fragment * * If @node does not already exist in @target_node, add changeset entry * to add @node in @target_node. * * If @node already exists in @target_node, and the existing node has * a phandle, the overlay node is not allowed to have a phandle. * * If @node has child nodes, add the children recursively via * build_changeset_next_level(). * * NOTE_1: A live devicetree created from a flattened device tree (FDT) will * not contain the full path in node->full_name. Thus an overlay * created from an FDT also will not contain the full path in * node->full_name. However, a live devicetree created from Open * Firmware may have the full path in node->full_name. * * add_changeset_node() follows the FDT convention and does not include * the full path in node->full_name. Even though it expects the overlay * to not contain the full path, it uses kbasename() to remove the * full path should it exist. It also uses kbasename() in comparisons * to nodes in the live devicetree so that it can apply an overlay to * a live devicetree created from Open Firmware. * * NOTE_2: Multiple mods of created nodes not supported. * If more than one fragment contains a node that does not already exist * in the live tree, then for each fragment of_changeset_attach_node() * will add a changeset entry to add the node. When the changeset is * applied, __of_attach_node() will attach the node twice (once for * each fragment). At this point the device tree will be corrupted. * * TODO: add integrity check to ensure that multiple fragments do not * create the same node. * * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if * invalid @overlay. */ static int add_changeset_node(struct overlay_changeset *ovcs, struct device_node *target_node, struct device_node *node) { const char *node_kbasename; struct device_node *tchild; int ret = 0; node_kbasename = kbasename(node->full_name); for_each_child_of_node(target_node, tchild) if (!of_node_cmp(node_kbasename, kbasename(tchild->full_name))) break; if (!tchild) { tchild = __of_node_dup(node, node_kbasename); if (!tchild) return -ENOMEM; tchild->parent = target_node; ret = of_changeset_attach_node(&ovcs->cset, tchild); if (ret) return ret; return build_changeset_next_level(ovcs, tchild, node); } if (node->phandle && tchild->phandle) ret = -EINVAL; else ret = build_changeset_next_level(ovcs, tchild, node); of_node_put(tchild); return ret; }
static int of_overlay_apply_single_device_node(struct of_overlay *ov, struct device_node *target, struct device_node *child) { const char *cname; struct device_node *tchild; int ret = 0; cname = kbasename(child->full_name); if (cname == NULL) return -ENOMEM; /* NOTE: Multiple mods of created nodes not supported */ tchild = of_get_child_by_name(target, cname); if (tchild != NULL) { /* apply overlay recursively */ ret = of_overlay_apply_one(ov, tchild, child); of_node_put(tchild); } else { /* create empty tree as a target */ tchild = __of_node_dup(child, "%s/%s", target->full_name, cname); if (!tchild) return -ENOMEM; /* point to parent */ tchild->parent = target; ret = of_changeset_attach_node(&ov->cset, tchild); if (ret) return ret; ret = of_overlay_apply_one(ov, tchild, child); if (ret) return ret; } return ret; }