[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[xen staging] device-tree: Move dt-overlay.c to common/device-tree/



commit 1e81d2df4f48f37d2086bc75199c75ca93cace92
Author:     Michal Orzel <michal.orzel@xxxxxxx>
AuthorDate: Thu Oct 10 12:57:46 2024 +0200
Commit:     Julien Grall <jgrall@xxxxxxxxxx>
CommitDate: Thu Oct 17 15:40:47 2024 +0100

    device-tree: Move dt-overlay.c to common/device-tree/
    
    The code is DT specific and as such should be placed under common
    directory for DT related files. Update MAINTAINERS file accordingly
    and drop the line with a path from a top-level comment in dt-overlay.c.
    It serves no purpose and requires being updated on every code movement.
    
    Signed-off-by: Michal Orzel <michal.orzel@xxxxxxx>
    Reviewed-by: Stefano Stabellini <sstabellini@xxxxxxxxxx>
---
 MAINTAINERS                         |    1 -
 xen/common/Makefile                 |    1 -
 xen/common/device-tree/Makefile     |    1 +
 xen/common/device-tree/dt-overlay.c | 1007 ++++++++++++++++++++++++++++++++++
 xen/common/dt-overlay.c             | 1009 -----------------------------------
 5 files changed, 1008 insertions(+), 1011 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index f68ddd7f84..3bba2c8c31 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -292,7 +292,6 @@ M:  Michal Orzel <michal.orzel@xxxxxxx>
 S:     Supported
 F:     xen/common/libfdt/
 F:     xen/common/device-tree/
-F:     xen/common/dt-overlay.c
 F:     xen/include/xen/libfdt/
 F:     xen/include/xen/bootfdt.h
 F:     xen/include/xen/device_tree.h
diff --git a/xen/common/Makefile b/xen/common/Makefile
index fc48cbf688..b279b09bfb 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -11,7 +11,6 @@ obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o
 obj-$(CONFIG_HAS_DEVICE_TREE) += device-tree/
 obj-$(CONFIG_IOREQ_SERVER) += dm.o
 obj-y += domain.o
-obj-$(CONFIG_OVERLAY_DTB) += dt-overlay.o
 obj-y += event_2l.o
 obj-y += event_channel.o
 obj-y += event_fifo.o
diff --git a/xen/common/device-tree/Makefile b/xen/common/device-tree/Makefile
index 990abd571a..58052d074e 100644
--- a/xen/common/device-tree/Makefile
+++ b/xen/common/device-tree/Makefile
@@ -1,3 +1,4 @@
 obj-y += bootfdt.init.o
 obj-y += bootinfo.init.o
 obj-y += device-tree.o
+obj-$(CONFIG_OVERLAY_DTB) += dt-overlay.o
diff --git a/xen/common/device-tree/dt-overlay.c 
b/xen/common/device-tree/dt-overlay.c
new file mode 100644
index 0000000000..97fb99eaaa
--- /dev/null
+++ b/xen/common/device-tree/dt-overlay.c
@@ -0,0 +1,1007 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Device tree overlay support in Xen.
+ *
+ * Copyright (C) 2023, Advanced Micro Devices, Inc. All Rights Reserved.
+ * Written by Vikram Garhwal <vikram.garhwal@xxxxxxx>
+ *
+ */
+#include <asm/domain_build.h>
+#include <xen/dt-overlay.h>
+#include <xen/guest_access.h>
+#include <xen/iocap.h>
+#include <xen/libfdt/libfdt.h>
+#include <xen/xmalloc.h>
+
+#define DT_OVERLAY_MAX_SIZE KB(500)
+
+static LIST_HEAD(overlay_tracker);
+static DEFINE_SPINLOCK(overlay_lock);
+
+/* Find last descendants of the device_node. */
+static struct dt_device_node *
+find_last_descendants_node(const struct dt_device_node *device_node)
+{
+    struct dt_device_node *child_node;
+
+    for ( child_node = device_node->child; child_node->sibling != NULL;
+          child_node = child_node->sibling );
+
+    /* If last child_node also have children. */
+    if ( child_node->child )
+        child_node = find_last_descendants_node(child_node);
+
+    return child_node;
+}
+
+/*
+ * Returns next node to the input node. If node has children then return
+ * last descendant's next node.
+*/
+static struct dt_device_node *
+dt_find_next_node(struct dt_device_node *dt, const struct dt_device_node *node)
+{
+    struct dt_device_node *np;
+
+    dt_for_each_device_node(dt, np)
+        if ( np == node )
+            break;
+
+    if ( np->child )
+        np = find_last_descendants_node(np);
+
+    return np->allnext;
+}
+
+static int dt_overlay_remove_node(struct dt_device_node *device_node)
+{
+    struct dt_device_node *np;
+    struct dt_device_node *parent_node;
+    struct dt_device_node *last_descendant = device_node->child;
+
+    parent_node = device_node->parent;
+
+    /* Check if we are trying to remove "/" i.e. root node. */
+    if ( parent_node == NULL )
+    {
+        dt_dprintk("%s's parent node not found\n", device_node->name);
+        return -EFAULT;
+    }
+
+    /* Sanity check for linking between parent and child node. */
+    np = parent_node->child;
+    if ( np == NULL )
+    {
+        dt_dprintk("parent node %s's not found\n", parent_node->name);
+        return -EFAULT;
+    }
+
+    /* If node to be removed is only child node or first child. */
+    if ( !dt_node_cmp(np->full_name, device_node->full_name) )
+    {
+        parent_node->child = np->sibling;
+
+        /*
+         * Iterate over all child nodes of device_node. Given that we are
+         * removing a node, we need to remove all it's descendants too.
+         * Reason behind finding last_descendant:
+         * If device_node has multiple children, device_node->allnext will 
point
+         * to first_child and first_child->allnext will be a sibling. When the
+         * device_node and it's all children are removed, parent_node->allnext
+         * should point to node next to last children.
+         */
+        if ( last_descendant )
+        {
+            last_descendant = find_last_descendants_node(device_node);
+            parent_node->allnext = last_descendant->allnext;
+        }
+        else
+            parent_node->allnext = np->allnext;
+
+        return 0;
+    }
+
+    for ( np = parent_node->child; np->sibling != NULL; np = np->sibling )
+    {
+        if ( !dt_node_cmp(np->sibling->full_name, device_node->full_name) )
+        {
+            /* Found the node. Now we remove it. */
+            np->sibling = np->sibling->sibling;
+
+            if ( np->child )
+                np = find_last_descendants_node(np);
+
+            /*
+             * Iterate over all child nodes of device_node. Given that we are
+             * removing parent node, we need to remove all it's descendants 
too.
+             */
+            if ( last_descendant )
+                last_descendant = find_last_descendants_node(device_node);
+
+            if ( last_descendant )
+                np->allnext = last_descendant->allnext;
+            else
+                np->allnext = np->allnext->allnext;
+
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static int dt_overlay_add_node(struct dt_device_node *device_node,
+                               const char *parent_node_path)
+{
+    struct dt_device_node *parent_node;
+    struct dt_device_node *next_node;
+
+    parent_node = dt_find_node_by_path(parent_node_path);
+
+    if ( parent_node == NULL )
+    {
+        dt_dprintk("Parent node %s not found. Overlay node will not be 
added\n",
+                   parent_node_path);
+        return -EINVAL;
+    }
+
+    /* If parent has no child. */
+    if ( parent_node->child == NULL )
+    {
+        next_node = parent_node->allnext;
+        device_node->parent = parent_node;
+        parent_node->allnext = device_node;
+        parent_node->child = device_node;
+    }
+    else
+    {
+        struct dt_device_node *np;
+        /*
+         * If parent has at least one child node.
+         * Iterate to the last child node of parent.
+         */
+        for ( np = parent_node->child; np->sibling != NULL; np = np->sibling );
+
+        /* Iterate over all child nodes of np node. */
+        if ( np->child )
+        {
+            struct dt_device_node *np_last_descendant;
+
+            np_last_descendant = find_last_descendants_node(np);
+
+            next_node = np_last_descendant->allnext;
+            np_last_descendant->allnext = device_node;
+        }
+        else
+        {
+            next_node = np->allnext;
+            np->allnext = device_node;
+        }
+
+        device_node->parent = parent_node;
+        np->sibling = device_node;
+        np->sibling->sibling = NULL;
+    }
+
+    /* Iterate over all child nodes of device_node to add children too. */
+    if ( device_node->child )
+    {
+        struct dt_device_node *device_node_last_descendant;
+
+        device_node_last_descendant = find_last_descendants_node(device_node);
+
+        /* Plug next_node at the end of last children of device_node. */
+        device_node_last_descendant->allnext = next_node;
+    }
+    else
+    {
+        /* Now plug next_node at the end of device_node. */
+        device_node->allnext = next_node;
+    }
+
+    return 0;
+}
+
+/* Basic sanity check for the dtbo tool stack provided to Xen. */
+static int check_overlay_fdt(const void *overlay_fdt, uint32_t 
overlay_fdt_size)
+{
+    if ( (fdt_totalsize(overlay_fdt) != overlay_fdt_size) ||
+          fdt_check_header(overlay_fdt) )
+    {
+        printk(XENLOG_ERR "The overlay FDT is not a valid Flat Device Tree\n");
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int irq_remove_cb(unsigned long s, unsigned long e, void *dom,
+                         unsigned long *c)
+{
+    int rc;
+    struct domain *d = dom;
+
+    /*
+     * TODO: We don't handle shared IRQs for now. So, it is assumed that
+     * the IRQs was not shared with another devices.
+     * TODO: Undo the IRQ routing.
+     */
+    rc = irq_deny_access(d, s);
+    if ( rc )
+    {
+        printk(XENLOG_ERR "unable to revoke access for irq %lu\n", s);
+    }
+    else
+        *c += e - s + 1;
+
+    return rc;
+
+}
+
+static int iomem_remove_cb(unsigned long s, unsigned long e, void *dom,
+                           unsigned long *c)
+{
+    int rc;
+    struct domain *d = dom;
+
+    /*
+    * Remove mmio access.
+    * TODO: Support for remove/add the mapping in P2M.
+    */
+    rc = iomem_deny_access(d, s, e);
+    if ( rc )
+    {
+        printk(XENLOG_ERR "Unable to remove %pd access to %#lx - %#lx\n",
+               d, s, e);
+    }
+    else
+        *c += e - s + 1;
+
+    return rc;
+}
+
+/* Count number of nodes till one level of __overlay__ tag. */
+static unsigned int overlay_node_count(const void *overlay_fdt)
+{
+    unsigned int num_overlay_nodes = 0;
+    int fragment;
+
+    fdt_for_each_subnode(fragment, overlay_fdt, 0)
+    {
+        int subnode;
+        int overlay;
+
+        overlay = fdt_subnode_offset(overlay_fdt, fragment, "__overlay__");
+        if ( overlay < 0 )
+            continue;
+
+        fdt_for_each_subnode(subnode, overlay_fdt, overlay)
+        {
+            num_overlay_nodes++;
+        }
+    }
+
+    return num_overlay_nodes;
+}
+
+/*
+ * overlay_get_nodes_info gets full name with path for all the nodes which
+ * are in one level of __overlay__ tag. This is useful when checking node for
+ * duplication i.e. dtbo tries to add nodes which already exists in device 
tree.
+ */
+static int overlay_get_nodes_info(const void *fdto, char **nodes_full_path)
+{
+    int fragment;
+    unsigned int node_num = 0;
+
+    fdt_for_each_subnode(fragment, fdto, 0)
+    {
+        int target;
+        int overlay;
+        int subnode;
+        const char *target_path;
+
+        overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
+        if ( overlay < 0 )
+            continue;
+
+        target = fdt_overlay_target_offset(device_tree_flattened, fdto,
+                                           fragment, &target_path);
+        if ( target < 0 )
+            return target;
+
+        if ( target_path == NULL )
+            return -EINVAL;
+
+        fdt_for_each_subnode(subnode, fdto, overlay)
+        {
+            const char *node_name = NULL;
+            int node_name_len;
+            unsigned int target_path_len = strlen(target_path);
+            unsigned int node_full_name_len;
+            unsigned int extra_len;
+
+            node_name = fdt_get_name(fdto, subnode, &node_name_len);
+
+            if ( node_name == NULL )
+                return node_name_len;
+
+            /*
+             * Extra length is for adding '/' and '\0' unless the target path 
is
+             * root in which case we don't add the '/' at the beginning. This 
is
+             * done to keep the node_full_path in the correct full node name
+             * format.
+             */
+            extra_len = (target_path_len > 1) ? 2 : 1;
+            node_full_name_len = target_path_len + node_name_len + extra_len;
+
+            nodes_full_path[node_num] = xmalloc_bytes(node_full_name_len);
+
+            if ( nodes_full_path[node_num] == NULL )
+                return -ENOMEM;
+
+            memcpy(nodes_full_path[node_num], target_path, target_path_len);
+
+            /* Target is not root - add separator */
+            if ( target_path_len > 1 )
+                nodes_full_path[node_num][target_path_len++] = '/';
+
+            memcpy(nodes_full_path[node_num] + target_path_len,
+                    node_name, node_name_len);
+
+            nodes_full_path[node_num][node_full_name_len - 1] = '\0';
+
+            node_num++;
+        }
+    }
+
+    return 0;
+}
+
+/* This function should be called with the overlay_lock taken */
+static struct overlay_track *
+find_track_entry_from_tracker(const void *overlay_fdt,
+                              uint32_t overlay_fdt_size)
+{
+    struct overlay_track *entry, *temp;
+    bool found_entry = false;
+
+    ASSERT(spin_is_locked(&overlay_lock));
+
+    /*
+     * First check if dtbo is correct i.e. it should one of the dtbo which was
+     * used when dynamically adding the node.
+     * Limitation: Cases with same node names but different property are not
+     * supported currently. We are relying on user to provide the same dtbo
+     * as it was used when adding the nodes.
+     */
+    list_for_each_entry_safe( entry, temp, &overlay_tracker, entry )
+    {
+        if ( memcmp(entry->overlay_fdt, overlay_fdt, overlay_fdt_size) == 0 )
+        {
+            found_entry = true;
+            break;
+        }
+    }
+
+    if ( !found_entry )
+    {
+        printk(XENLOG_ERR "Cannot find any matching tracker with input dtbo."
+               " Operation is supported only for prior added dtbo.\n");
+        return NULL;
+    }
+
+    return entry;
+}
+
+/* Check if node itself can be removed and remove node from IOMMU. */
+static int remove_node_resources(struct dt_device_node *device_node)
+{
+    int rc = 0;
+    unsigned int len;
+    domid_t domid;
+
+    domid = dt_device_used_by(device_node);
+
+    dt_dprintk("Checking if node %s is used by any domain\n",
+               device_node->full_name);
+
+    /* Remove the node if only it's assigned to hardware domain or domain io. 
*/
+    if ( domid != hardware_domain->domain_id && domid != DOMID_IO )
+    {
+        printk(XENLOG_ERR "Device %s is being used by domain %u. Removing 
nodes failed\n",
+               device_node->full_name, domid);
+        return -EINVAL;
+    }
+
+    /* Check if iommu property exists. */
+    if ( dt_get_property(device_node, "iommus", &len) )
+    {
+        if ( dt_device_is_protected(device_node) )
+        {
+            rc = iommu_remove_dt_device(device_node);
+            if ( rc < 0 )
+                return rc;
+        }
+    }
+
+    return rc;
+}
+
+/* Remove all descendants from IOMMU. */
+static int
+remove_descendant_nodes_resources(const struct dt_device_node *device_node)
+{
+    int rc = 0;
+    struct dt_device_node *child_node;
+
+    for ( child_node = device_node->child; child_node != NULL;
+         child_node = child_node->sibling )
+    {
+        if ( child_node->child )
+        {
+            rc = remove_descendant_nodes_resources(child_node);
+            if ( rc )
+                return rc;
+        }
+
+        rc = remove_node_resources(child_node);
+        if ( rc )
+            return rc;
+    }
+
+    return rc;
+}
+
+/* Remove nodes from dt_host. */
+static int remove_nodes(const struct overlay_track *tracker)
+{
+    int rc = 0;
+    struct dt_device_node *overlay_node;
+    unsigned int j;
+    struct domain *d = hardware_domain;
+
+    for ( j = 0; j < tracker->num_nodes; j++ )
+    {
+        overlay_node = (struct dt_device_node *)tracker->nodes_address[j];
+        if ( overlay_node == NULL )
+            return -EINVAL;
+
+        write_lock(&dt_host_lock);
+
+        rc = remove_descendant_nodes_resources(overlay_node);
+        if ( rc )
+        {
+            write_unlock(&dt_host_lock);
+            return rc;
+        }
+
+        rc = remove_node_resources(overlay_node);
+        if ( rc )
+        {
+            write_unlock(&dt_host_lock);
+            return rc;
+        }
+
+        dt_dprintk("Removing node: %s\n", overlay_node->full_name);
+
+        rc = dt_overlay_remove_node(overlay_node);
+        if ( rc )
+        {
+            write_unlock(&dt_host_lock);
+            return rc;
+        }
+
+        write_unlock(&dt_host_lock);
+    }
+
+    /* Remove IRQ access. */
+    if ( tracker->irq_ranges )
+    {
+        rc = rangeset_consume_ranges(tracker->irq_ranges, irq_remove_cb, d);
+        if ( rc )
+            return rc;
+    }
+
+   /* Remove mmio access. */
+    if ( tracker->iomem_ranges )
+    {
+        rc = rangeset_consume_ranges(tracker->iomem_ranges, iomem_remove_cb, 
d);
+        if ( rc )
+            return rc;
+    }
+
+    return rc;
+}
+
+/*
+ * First finds the device node to remove. Check if the device is being used by
+ * any dom and finally remove it from dt_host. IOMMU is already being taken 
care
+ * while destroying the domain.
+ */
+static long handle_remove_overlay_nodes(const void *overlay_fdt,
+                                        uint32_t overlay_fdt_size)
+{
+    int rc;
+    struct overlay_track *entry;
+
+    rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
+    if ( rc )
+        return rc;
+
+    spin_lock(&overlay_lock);
+
+    entry = find_track_entry_from_tracker(overlay_fdt, overlay_fdt_size);
+    if ( entry == NULL )
+    {
+        rc = -EINVAL;
+        goto out;
+
+    }
+
+    rc = remove_nodes(entry);
+    if ( rc )
+    {
+        printk(XENLOG_ERR "Removing node failed\n");
+        goto out;
+    }
+
+    list_del(&entry->entry);
+
+    xfree(entry->dt_host_new);
+    xfree(entry->fdt);
+    xfree(entry->overlay_fdt);
+
+    xfree(entry->nodes_address);
+
+    rangeset_destroy(entry->irq_ranges);
+    rangeset_destroy(entry->iomem_ranges);
+
+    xfree(entry);
+
+ out:
+    spin_unlock(&overlay_lock);
+    return rc;
+}
+
+static void free_nodes_full_path(unsigned int num_nodes, char 
**nodes_full_path)
+{
+    unsigned int i;
+
+    if ( nodes_full_path == NULL )
+        return;
+
+    for ( i = 0; i < num_nodes && nodes_full_path[i] != NULL; i++ )
+    {
+        xfree(nodes_full_path[i]);
+    }
+
+    xfree(nodes_full_path);
+}
+
+static long add_nodes(struct overlay_track *tr, char **nodes_full_path)
+
+{
+    int rc;
+    unsigned int j;
+    struct dt_device_node *overlay_node;
+
+    for ( j = 0; j < tr->num_nodes; j++ )
+    {
+        struct dt_device_node *prev_node, *next_node;
+
+        dt_dprintk("Adding node: %s\n", nodes_full_path[j]);
+
+        /* Find the newly added node in tr->dt_host_new by it's full path. */
+        overlay_node = dt_find_node_by_path_from(tr->dt_host_new,
+                                                 nodes_full_path[j]);
+        if ( overlay_node == NULL )
+            return -EFAULT;
+
+        /*
+         * Find previous and next node to overlay_node in dt_host_new. We will
+         * need these nodes to fix the dt_host_new mapping. When overlay_node 
is
+         * take out of dt_host_new tree and added to dt_host, link between
+         * previous node and next_node is broken. We will need to refresh
+         * dt_host_new with correct linking for any other overlay nodes
+         * extraction in future.
+         */
+        dt_for_each_device_node(tr->dt_host_new, prev_node)
+            if ( prev_node->allnext == overlay_node )
+                break;
+
+        next_node = dt_find_next_node(tr->dt_host_new, overlay_node);
+
+        write_lock(&dt_host_lock);
+
+        /* Add the node to dt_host. */
+        rc = dt_overlay_add_node(overlay_node, 
overlay_node->parent->full_name);
+        if ( rc )
+        {
+            write_unlock(&dt_host_lock);
+
+            /* Node not added in dt_host. */
+            return rc;
+        }
+
+        prev_node->allnext = next_node;
+
+        overlay_node = dt_find_node_by_path(overlay_node->full_name);
+        if ( overlay_node == NULL )
+        {
+            /* Sanity check. But code will never come here. */
+            ASSERT_UNREACHABLE();
+            return -EFAULT;
+        }
+
+        write_unlock(&dt_host_lock);
+
+        /* Keep overlay_node address in tracker. */
+        tr->nodes_address[j] = (unsigned long)overlay_node;
+    }
+
+    return 0;
+}
+/*
+ * Adds device tree nodes under target node.
+ * We use tr->dt_host_new to unflatten the updated device_tree_flattened.
+ */
+static long handle_add_overlay_nodes(void *overlay_fdt,
+                                     uint32_t overlay_fdt_size)
+{
+    int rc;
+    unsigned int j;
+    struct dt_device_node *overlay_node;
+    struct overlay_track *tr = NULL;
+    char **nodes_full_path = NULL;
+    unsigned int new_fdt_size;
+
+    tr = xzalloc(struct overlay_track);
+    if ( tr == NULL )
+        return -ENOMEM;
+
+    new_fdt_size = fdt_totalsize(device_tree_flattened) +
+                                 fdt_totalsize(overlay_fdt);
+
+    tr->fdt = xzalloc_bytes(new_fdt_size);
+    if ( tr->fdt == NULL )
+    {
+        xfree(tr);
+        return -ENOMEM;
+    }
+
+    tr->num_nodes = overlay_node_count(overlay_fdt);
+    if ( tr->num_nodes == 0 )
+    {
+        xfree(tr->fdt);
+        xfree(tr);
+        return -ENOMEM;
+    }
+
+    tr->nodes_address = xzalloc_bytes(tr->num_nodes * sizeof(unsigned long));
+    if ( tr->nodes_address == NULL )
+    {
+        xfree(tr->fdt);
+        xfree(tr);
+        return -ENOMEM;
+    }
+
+    rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
+    if ( rc )
+    {
+        xfree(tr->nodes_address);
+        xfree(tr->fdt);
+        xfree(tr);
+        return rc;
+    }
+
+    /*
+     * Keep a copy of overlay_fdt as fdt_overlay_apply will change the input
+     * overlay's content(magic) when applying overlay.
+     */
+    tr->overlay_fdt = xzalloc_bytes(overlay_fdt_size);
+    if ( tr->overlay_fdt == NULL )
+    {
+        xfree(tr->nodes_address);
+        xfree(tr->fdt);
+        xfree(tr);
+        return -ENOMEM;
+    }
+
+    memcpy(tr->overlay_fdt, overlay_fdt, overlay_fdt_size);
+
+    spin_lock(&overlay_lock);
+
+    memcpy(tr->fdt, device_tree_flattened,
+           fdt_totalsize(device_tree_flattened));
+
+    /* Open tr->fdt with more space to accommodate the overlay_fdt. */
+    rc = fdt_open_into(tr->fdt, tr->fdt, new_fdt_size);
+    if ( rc )
+    {
+        printk(XENLOG_ERR "Increasing fdt size to accommodate overlay_fdt 
failed with error %d\n",
+               rc);
+        goto err;
+    }
+
+    nodes_full_path = xzalloc_bytes(tr->num_nodes * sizeof(char *));
+    if ( nodes_full_path == NULL )
+    {
+        rc = -ENOMEM;
+        goto err;
+    }
+
+    /*
+     * overlay_get_nodes_info is called to get the node information from dtbo.
+     * This is done before fdt_overlay_apply() because the overlay apply will
+     * erase the magic of overlay_fdt.
+     */
+    rc = overlay_get_nodes_info(overlay_fdt, nodes_full_path);
+    if ( rc )
+    {
+        printk(XENLOG_ERR "Getting nodes information failed with error %d\n",
+               rc);
+        goto err;
+    }
+
+    rc = fdt_overlay_apply(tr->fdt, overlay_fdt);
+    if ( rc )
+    {
+        printk(XENLOG_ERR "Adding overlay node failed with error %d\n", rc);
+        goto err;
+    }
+
+    /*
+     * Check if any of the node already exists in dt_host. If node already 
exits
+     * we can return here as this overlay_fdt is not suitable for overlay ops.
+     */
+    for ( j = 0; j < tr->num_nodes; j++ )
+    {
+        overlay_node = dt_find_node_by_path(nodes_full_path[j]);
+        if ( overlay_node != NULL )
+        {
+            printk(XENLOG_ERR "node %s exists in device tree\n",
+                   nodes_full_path[j]);
+            rc = -EINVAL;
+            goto err;
+        }
+    }
+
+    /*
+     * Unflatten the tr->fdt into a new dt_host.
+     * TODO: Check and add alias_scan() if it's needed for overlay in future.
+     */
+    rc = unflatten_device_tree(tr->fdt, &tr->dt_host_new);
+    if ( rc )
+    {
+        printk(XENLOG_ERR "unflatten_device_tree failed with error %d\n", rc);
+        goto err;
+    }
+
+    rc = add_nodes(tr, nodes_full_path);
+    if ( rc )
+    {
+        printk(XENLOG_ERR "Adding nodes failed. Removing the partially added 
nodes.\n");
+        goto remove_node;
+    }
+
+    INIT_LIST_HEAD(&tr->entry);
+    list_add_tail(&tr->entry, &overlay_tracker);
+
+    spin_unlock(&overlay_lock);
+
+    free_nodes_full_path(tr->num_nodes, nodes_full_path);
+
+    return rc;
+
+/*
+ * Failure case. We need to remove the nodes, free tracker(if tr exists) and
+ * tr->dt_host_new.
+ */
+ remove_node:
+    tr->num_nodes = j;
+    rc = remove_nodes(tr);
+
+    if ( rc )
+    {
+        /*
+         * User needs to provide right overlay. Incorrect node information
+         * example parent node doesn't exist in dt_host etc can cause memory
+         * leaks as removing_nodes() will fail and this means nodes memory is
+         * not freed from tracker. Which may cause memory leaks. Ideally, these
+         * device tree related mistakes will be caught by fdt_overlay_apply()
+         * but given that we don't manage that code keeping this warning 
message
+         * is better here.
+         */
+        printk(XENLOG_ERR "Removing node failed.\n");
+        spin_unlock(&overlay_lock);
+
+        free_nodes_full_path(tr->num_nodes, nodes_full_path);
+
+        return rc;
+    }
+
+ err:
+    spin_unlock(&overlay_lock);
+
+    if ( tr->dt_host_new )
+        xfree(tr->dt_host_new);
+
+    free_nodes_full_path(tr->num_nodes, nodes_full_path);
+
+    xfree(tr->overlay_fdt);
+    xfree(tr->nodes_address);
+    xfree(tr->fdt);
+
+    xfree(tr);
+
+    return rc;
+}
+
+static long handle_attach_overlay_nodes(struct domain *d,
+                                        const void *overlay_fdt,
+                                        uint32_t overlay_fdt_size)
+{
+    int rc;
+    unsigned int j;
+    struct overlay_track *entry;
+
+    rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
+    if ( rc )
+        return rc;
+
+    spin_lock(&overlay_lock);
+
+    entry = find_track_entry_from_tracker(overlay_fdt, overlay_fdt_size);
+    if ( entry == NULL )
+    {
+        rc = -EINVAL;
+        goto out;
+    }
+
+    entry->irq_ranges = rangeset_new(d, "Overlays: Interrupts", 0);
+    if (entry->irq_ranges == NULL)
+    {
+        rc = -ENOMEM;
+        printk(XENLOG_ERR "Creating IRQ rangeset failed");
+        goto out;
+    }
+
+    entry->iomem_ranges = rangeset_new(d, "Overlay: I/O Memory",
+                                       RANGESETF_prettyprint_hex);
+    if (entry->iomem_ranges == NULL)
+    {
+        rc = -ENOMEM;
+        printk(XENLOG_ERR "Creating IOMMU rangeset failed");
+        goto out;
+    }
+
+    for ( j = 0; j < entry->num_nodes; j++ )
+    {
+        struct dt_device_node *overlay_node;
+
+        overlay_node = (struct dt_device_node *)entry->nodes_address[j];
+        if ( overlay_node == NULL )
+        {
+            rc = -EINVAL;
+            goto out;
+        }
+
+        write_lock(&dt_host_lock);
+        rc = handle_device(d, overlay_node, p2m_mmio_direct_c,
+                           entry->iomem_ranges, entry->irq_ranges);
+        write_unlock(&dt_host_lock);
+        if ( rc )
+        {
+            printk(XENLOG_ERR "Adding IRQ and IOMMU failed\n");
+            goto out;
+        }
+    }
+
+    spin_unlock(&overlay_lock);
+
+    return 0;
+
+ out:
+    spin_unlock(&overlay_lock);
+
+    if ( entry )
+    {
+        rangeset_destroy(entry->irq_ranges);
+        rangeset_destroy(entry->iomem_ranges);
+    }
+
+    return rc;
+}
+
+long dt_overlay_sysctl(struct xen_sysctl_dt_overlay *op)
+{
+    long ret;
+    void *overlay_fdt;
+
+    if ( op->overlay_op != XEN_SYSCTL_DT_OVERLAY_ADD &&
+         op->overlay_op != XEN_SYSCTL_DT_OVERLAY_REMOVE )
+        return -EOPNOTSUPP;
+
+    if ( op->overlay_fdt_size == 0 ||
+         op->overlay_fdt_size > DT_OVERLAY_MAX_SIZE )
+        return -EINVAL;
+
+    if ( op->pad[0] || op->pad[1] || op->pad[2] )
+        return -EINVAL;
+
+    overlay_fdt = xmalloc_bytes(op->overlay_fdt_size);
+
+    if ( overlay_fdt == NULL )
+        return -ENOMEM;
+
+    ret = copy_from_guest(overlay_fdt, op->overlay_fdt, op->overlay_fdt_size);
+    if ( ret )
+    {
+        gprintk(XENLOG_ERR, "copy from guest failed\n");
+        xfree(overlay_fdt);
+
+        return -EFAULT;
+    }
+
+    if ( op->overlay_op == XEN_SYSCTL_DT_OVERLAY_REMOVE )
+        ret = handle_remove_overlay_nodes(overlay_fdt, op->overlay_fdt_size);
+    else
+        ret = handle_add_overlay_nodes(overlay_fdt, op->overlay_fdt_size);
+
+    xfree(overlay_fdt);
+
+    return ret;
+}
+
+long dt_overlay_domctl(struct domain *d, struct xen_domctl_dt_overlay *op)
+{
+    long ret;
+    void *overlay_fdt;
+
+    if ( op->overlay_op != XEN_DOMCTL_DT_OVERLAY_ATTACH )
+        return -EOPNOTSUPP;
+
+    if ( op->overlay_fdt_size == 0 ||
+         op->overlay_fdt_size > DT_OVERLAY_MAX_SIZE )
+        return -EINVAL;
+
+    if ( op->pad[0] || op->pad[1] || op->pad[2] )
+        return -EINVAL;
+
+    /* TODO: add support for non-1:1 domains using xen,reg */
+    if ( !is_domain_direct_mapped(d) )
+        return -EOPNOTSUPP;
+
+    overlay_fdt = xmalloc_bytes(op->overlay_fdt_size);
+
+    if ( overlay_fdt == NULL )
+        return -ENOMEM;
+
+    ret = copy_from_guest(overlay_fdt, op->overlay_fdt, op->overlay_fdt_size);
+    if ( ret )
+    {
+        gprintk(XENLOG_ERR, "copy from guest failed\n");
+        xfree(overlay_fdt);
+
+        return -EFAULT;
+    }
+
+    if ( op->overlay_op == XEN_DOMCTL_DT_OVERLAY_ATTACH )
+        ret = handle_attach_overlay_nodes(d, overlay_fdt, 
op->overlay_fdt_size);
+    else
+        ret = -EOPNOTSUPP;
+
+    xfree(overlay_fdt);
+
+    return ret;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/common/dt-overlay.c b/xen/common/dt-overlay.c
deleted file mode 100644
index 5ce00514ef..0000000000
--- a/xen/common/dt-overlay.c
+++ /dev/null
@@ -1,1009 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * xen/common/dt-overlay.c
- *
- * Device tree overlay support in Xen.
- *
- * Copyright (C) 2023, Advanced Micro Devices, Inc. All Rights Reserved.
- * Written by Vikram Garhwal <vikram.garhwal@xxxxxxx>
- *
- */
-#include <asm/domain_build.h>
-#include <xen/dt-overlay.h>
-#include <xen/guest_access.h>
-#include <xen/iocap.h>
-#include <xen/libfdt/libfdt.h>
-#include <xen/xmalloc.h>
-
-#define DT_OVERLAY_MAX_SIZE KB(500)
-
-static LIST_HEAD(overlay_tracker);
-static DEFINE_SPINLOCK(overlay_lock);
-
-/* Find last descendants of the device_node. */
-static struct dt_device_node *
-find_last_descendants_node(const struct dt_device_node *device_node)
-{
-    struct dt_device_node *child_node;
-
-    for ( child_node = device_node->child; child_node->sibling != NULL;
-          child_node = child_node->sibling );
-
-    /* If last child_node also have children. */
-    if ( child_node->child )
-        child_node = find_last_descendants_node(child_node);
-
-    return child_node;
-}
-
-/*
- * Returns next node to the input node. If node has children then return
- * last descendant's next node.
-*/
-static struct dt_device_node *
-dt_find_next_node(struct dt_device_node *dt, const struct dt_device_node *node)
-{
-    struct dt_device_node *np;
-
-    dt_for_each_device_node(dt, np)
-        if ( np == node )
-            break;
-
-    if ( np->child )
-        np = find_last_descendants_node(np);
-
-    return np->allnext;
-}
-
-static int dt_overlay_remove_node(struct dt_device_node *device_node)
-{
-    struct dt_device_node *np;
-    struct dt_device_node *parent_node;
-    struct dt_device_node *last_descendant = device_node->child;
-
-    parent_node = device_node->parent;
-
-    /* Check if we are trying to remove "/" i.e. root node. */
-    if ( parent_node == NULL )
-    {
-        dt_dprintk("%s's parent node not found\n", device_node->name);
-        return -EFAULT;
-    }
-
-    /* Sanity check for linking between parent and child node. */
-    np = parent_node->child;
-    if ( np == NULL )
-    {
-        dt_dprintk("parent node %s's not found\n", parent_node->name);
-        return -EFAULT;
-    }
-
-    /* If node to be removed is only child node or first child. */
-    if ( !dt_node_cmp(np->full_name, device_node->full_name) )
-    {
-        parent_node->child = np->sibling;
-
-        /*
-         * Iterate over all child nodes of device_node. Given that we are
-         * removing a node, we need to remove all it's descendants too.
-         * Reason behind finding last_descendant:
-         * If device_node has multiple children, device_node->allnext will 
point
-         * to first_child and first_child->allnext will be a sibling. When the
-         * device_node and it's all children are removed, parent_node->allnext
-         * should point to node next to last children.
-         */
-        if ( last_descendant )
-        {
-            last_descendant = find_last_descendants_node(device_node);
-            parent_node->allnext = last_descendant->allnext;
-        }
-        else
-            parent_node->allnext = np->allnext;
-
-        return 0;
-    }
-
-    for ( np = parent_node->child; np->sibling != NULL; np = np->sibling )
-    {
-        if ( !dt_node_cmp(np->sibling->full_name, device_node->full_name) )
-        {
-            /* Found the node. Now we remove it. */
-            np->sibling = np->sibling->sibling;
-
-            if ( np->child )
-                np = find_last_descendants_node(np);
-
-            /*
-             * Iterate over all child nodes of device_node. Given that we are
-             * removing parent node, we need to remove all it's descendants 
too.
-             */
-            if ( last_descendant )
-                last_descendant = find_last_descendants_node(device_node);
-
-            if ( last_descendant )
-                np->allnext = last_descendant->allnext;
-            else
-                np->allnext = np->allnext->allnext;
-
-            break;
-        }
-    }
-
-    return 0;
-}
-
-static int dt_overlay_add_node(struct dt_device_node *device_node,
-                               const char *parent_node_path)
-{
-    struct dt_device_node *parent_node;
-    struct dt_device_node *next_node;
-
-    parent_node = dt_find_node_by_path(parent_node_path);
-
-    if ( parent_node == NULL )
-    {
-        dt_dprintk("Parent node %s not found. Overlay node will not be 
added\n",
-                   parent_node_path);
-        return -EINVAL;
-    }
-
-    /* If parent has no child. */
-    if ( parent_node->child == NULL )
-    {
-        next_node = parent_node->allnext;
-        device_node->parent = parent_node;
-        parent_node->allnext = device_node;
-        parent_node->child = device_node;
-    }
-    else
-    {
-        struct dt_device_node *np;
-        /*
-         * If parent has at least one child node.
-         * Iterate to the last child node of parent.
-         */
-        for ( np = parent_node->child; np->sibling != NULL; np = np->sibling );
-
-        /* Iterate over all child nodes of np node. */
-        if ( np->child )
-        {
-            struct dt_device_node *np_last_descendant;
-
-            np_last_descendant = find_last_descendants_node(np);
-
-            next_node = np_last_descendant->allnext;
-            np_last_descendant->allnext = device_node;
-        }
-        else
-        {
-            next_node = np->allnext;
-            np->allnext = device_node;
-        }
-
-        device_node->parent = parent_node;
-        np->sibling = device_node;
-        np->sibling->sibling = NULL;
-    }
-
-    /* Iterate over all child nodes of device_node to add children too. */
-    if ( device_node->child )
-    {
-        struct dt_device_node *device_node_last_descendant;
-
-        device_node_last_descendant = find_last_descendants_node(device_node);
-
-        /* Plug next_node at the end of last children of device_node. */
-        device_node_last_descendant->allnext = next_node;
-    }
-    else
-    {
-        /* Now plug next_node at the end of device_node. */
-        device_node->allnext = next_node;
-    }
-
-    return 0;
-}
-
-/* Basic sanity check for the dtbo tool stack provided to Xen. */
-static int check_overlay_fdt(const void *overlay_fdt, uint32_t 
overlay_fdt_size)
-{
-    if ( (fdt_totalsize(overlay_fdt) != overlay_fdt_size) ||
-          fdt_check_header(overlay_fdt) )
-    {
-        printk(XENLOG_ERR "The overlay FDT is not a valid Flat Device Tree\n");
-        return -EINVAL;
-    }
-
-    return 0;
-}
-
-static int irq_remove_cb(unsigned long s, unsigned long e, void *dom,
-                         unsigned long *c)
-{
-    int rc;
-    struct domain *d = dom;
-
-    /*
-     * TODO: We don't handle shared IRQs for now. So, it is assumed that
-     * the IRQs was not shared with another devices.
-     * TODO: Undo the IRQ routing.
-     */
-    rc = irq_deny_access(d, s);
-    if ( rc )
-    {
-        printk(XENLOG_ERR "unable to revoke access for irq %lu\n", s);
-    }
-    else
-        *c += e - s + 1;
-
-    return rc;
-
-}
-
-static int iomem_remove_cb(unsigned long s, unsigned long e, void *dom,
-                           unsigned long *c)
-{
-    int rc;
-    struct domain *d = dom;
-
-    /*
-    * Remove mmio access.
-    * TODO: Support for remove/add the mapping in P2M.
-    */
-    rc = iomem_deny_access(d, s, e);
-    if ( rc )
-    {
-        printk(XENLOG_ERR "Unable to remove %pd access to %#lx - %#lx\n",
-               d, s, e);
-    }
-    else
-        *c += e - s + 1;
-
-    return rc;
-}
-
-/* Count number of nodes till one level of __overlay__ tag. */
-static unsigned int overlay_node_count(const void *overlay_fdt)
-{
-    unsigned int num_overlay_nodes = 0;
-    int fragment;
-
-    fdt_for_each_subnode(fragment, overlay_fdt, 0)
-    {
-        int subnode;
-        int overlay;
-
-        overlay = fdt_subnode_offset(overlay_fdt, fragment, "__overlay__");
-        if ( overlay < 0 )
-            continue;
-
-        fdt_for_each_subnode(subnode, overlay_fdt, overlay)
-        {
-            num_overlay_nodes++;
-        }
-    }
-
-    return num_overlay_nodes;
-}
-
-/*
- * overlay_get_nodes_info gets full name with path for all the nodes which
- * are in one level of __overlay__ tag. This is useful when checking node for
- * duplication i.e. dtbo tries to add nodes which already exists in device 
tree.
- */
-static int overlay_get_nodes_info(const void *fdto, char **nodes_full_path)
-{
-    int fragment;
-    unsigned int node_num = 0;
-
-    fdt_for_each_subnode(fragment, fdto, 0)
-    {
-        int target;
-        int overlay;
-        int subnode;
-        const char *target_path;
-
-        overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
-        if ( overlay < 0 )
-            continue;
-
-        target = fdt_overlay_target_offset(device_tree_flattened, fdto,
-                                           fragment, &target_path);
-        if ( target < 0 )
-            return target;
-
-        if ( target_path == NULL )
-            return -EINVAL;
-
-        fdt_for_each_subnode(subnode, fdto, overlay)
-        {
-            const char *node_name = NULL;
-            int node_name_len;
-            unsigned int target_path_len = strlen(target_path);
-            unsigned int node_full_name_len;
-            unsigned int extra_len;
-
-            node_name = fdt_get_name(fdto, subnode, &node_name_len);
-
-            if ( node_name == NULL )
-                return node_name_len;
-
-            /*
-             * Extra length is for adding '/' and '\0' unless the target path 
is
-             * root in which case we don't add the '/' at the beginning. This 
is
-             * done to keep the node_full_path in the correct full node name
-             * format.
-             */
-            extra_len = (target_path_len > 1) ? 2 : 1;
-            node_full_name_len = target_path_len + node_name_len + extra_len;
-
-            nodes_full_path[node_num] = xmalloc_bytes(node_full_name_len);
-
-            if ( nodes_full_path[node_num] == NULL )
-                return -ENOMEM;
-
-            memcpy(nodes_full_path[node_num], target_path, target_path_len);
-
-            /* Target is not root - add separator */
-            if ( target_path_len > 1 )
-                nodes_full_path[node_num][target_path_len++] = '/';
-
-            memcpy(nodes_full_path[node_num] + target_path_len,
-                    node_name, node_name_len);
-
-            nodes_full_path[node_num][node_full_name_len - 1] = '\0';
-
-            node_num++;
-        }
-    }
-
-    return 0;
-}
-
-/* This function should be called with the overlay_lock taken */
-static struct overlay_track *
-find_track_entry_from_tracker(const void *overlay_fdt,
-                              uint32_t overlay_fdt_size)
-{
-    struct overlay_track *entry, *temp;
-    bool found_entry = false;
-
-    ASSERT(spin_is_locked(&overlay_lock));
-
-    /*
-     * First check if dtbo is correct i.e. it should one of the dtbo which was
-     * used when dynamically adding the node.
-     * Limitation: Cases with same node names but different property are not
-     * supported currently. We are relying on user to provide the same dtbo
-     * as it was used when adding the nodes.
-     */
-    list_for_each_entry_safe( entry, temp, &overlay_tracker, entry )
-    {
-        if ( memcmp(entry->overlay_fdt, overlay_fdt, overlay_fdt_size) == 0 )
-        {
-            found_entry = true;
-            break;
-        }
-    }
-
-    if ( !found_entry )
-    {
-        printk(XENLOG_ERR "Cannot find any matching tracker with input dtbo."
-               " Operation is supported only for prior added dtbo.\n");
-        return NULL;
-    }
-
-    return entry;
-}
-
-/* Check if node itself can be removed and remove node from IOMMU. */
-static int remove_node_resources(struct dt_device_node *device_node)
-{
-    int rc = 0;
-    unsigned int len;
-    domid_t domid;
-
-    domid = dt_device_used_by(device_node);
-
-    dt_dprintk("Checking if node %s is used by any domain\n",
-               device_node->full_name);
-
-    /* Remove the node if only it's assigned to hardware domain or domain io. 
*/
-    if ( domid != hardware_domain->domain_id && domid != DOMID_IO )
-    {
-        printk(XENLOG_ERR "Device %s is being used by domain %u. Removing 
nodes failed\n",
-               device_node->full_name, domid);
-        return -EINVAL;
-    }
-
-    /* Check if iommu property exists. */
-    if ( dt_get_property(device_node, "iommus", &len) )
-    {
-        if ( dt_device_is_protected(device_node) )
-        {
-            rc = iommu_remove_dt_device(device_node);
-            if ( rc < 0 )
-                return rc;
-        }
-    }
-
-    return rc;
-}
-
-/* Remove all descendants from IOMMU. */
-static int
-remove_descendant_nodes_resources(const struct dt_device_node *device_node)
-{
-    int rc = 0;
-    struct dt_device_node *child_node;
-
-    for ( child_node = device_node->child; child_node != NULL;
-         child_node = child_node->sibling )
-    {
-        if ( child_node->child )
-        {
-            rc = remove_descendant_nodes_resources(child_node);
-            if ( rc )
-                return rc;
-        }
-
-        rc = remove_node_resources(child_node);
-        if ( rc )
-            return rc;
-    }
-
-    return rc;
-}
-
-/* Remove nodes from dt_host. */
-static int remove_nodes(const struct overlay_track *tracker)
-{
-    int rc = 0;
-    struct dt_device_node *overlay_node;
-    unsigned int j;
-    struct domain *d = hardware_domain;
-
-    for ( j = 0; j < tracker->num_nodes; j++ )
-    {
-        overlay_node = (struct dt_device_node *)tracker->nodes_address[j];
-        if ( overlay_node == NULL )
-            return -EINVAL;
-
-        write_lock(&dt_host_lock);
-
-        rc = remove_descendant_nodes_resources(overlay_node);
-        if ( rc )
-        {
-            write_unlock(&dt_host_lock);
-            return rc;
-        }
-
-        rc = remove_node_resources(overlay_node);
-        if ( rc )
-        {
-            write_unlock(&dt_host_lock);
-            return rc;
-        }
-
-        dt_dprintk("Removing node: %s\n", overlay_node->full_name);
-
-        rc = dt_overlay_remove_node(overlay_node);
-        if ( rc )
-        {
-            write_unlock(&dt_host_lock);
-            return rc;
-        }
-
-        write_unlock(&dt_host_lock);
-    }
-
-    /* Remove IRQ access. */
-    if ( tracker->irq_ranges )
-    {
-        rc = rangeset_consume_ranges(tracker->irq_ranges, irq_remove_cb, d);
-        if ( rc )
-            return rc;
-    }
-
-   /* Remove mmio access. */
-    if ( tracker->iomem_ranges )
-    {
-        rc = rangeset_consume_ranges(tracker->iomem_ranges, iomem_remove_cb, 
d);
-        if ( rc )
-            return rc;
-    }
-
-    return rc;
-}
-
-/*
- * First finds the device node to remove. Check if the device is being used by
- * any dom and finally remove it from dt_host. IOMMU is already being taken 
care
- * while destroying the domain.
- */
-static long handle_remove_overlay_nodes(const void *overlay_fdt,
-                                        uint32_t overlay_fdt_size)
-{
-    int rc;
-    struct overlay_track *entry;
-
-    rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
-    if ( rc )
-        return rc;
-
-    spin_lock(&overlay_lock);
-
-    entry = find_track_entry_from_tracker(overlay_fdt, overlay_fdt_size);
-    if ( entry == NULL )
-    {
-        rc = -EINVAL;
-        goto out;
-
-    }
-
-    rc = remove_nodes(entry);
-    if ( rc )
-    {
-        printk(XENLOG_ERR "Removing node failed\n");
-        goto out;
-    }
-
-    list_del(&entry->entry);
-
-    xfree(entry->dt_host_new);
-    xfree(entry->fdt);
-    xfree(entry->overlay_fdt);
-
-    xfree(entry->nodes_address);
-
-    rangeset_destroy(entry->irq_ranges);
-    rangeset_destroy(entry->iomem_ranges);
-
-    xfree(entry);
-
- out:
-    spin_unlock(&overlay_lock);
-    return rc;
-}
-
-static void free_nodes_full_path(unsigned int num_nodes, char 
**nodes_full_path)
-{
-    unsigned int i;
-
-    if ( nodes_full_path == NULL )
-        return;
-
-    for ( i = 0; i < num_nodes && nodes_full_path[i] != NULL; i++ )
-    {
-        xfree(nodes_full_path[i]);
-    }
-
-    xfree(nodes_full_path);
-}
-
-static long add_nodes(struct overlay_track *tr, char **nodes_full_path)
-
-{
-    int rc;
-    unsigned int j;
-    struct dt_device_node *overlay_node;
-
-    for ( j = 0; j < tr->num_nodes; j++ )
-    {
-        struct dt_device_node *prev_node, *next_node;
-
-        dt_dprintk("Adding node: %s\n", nodes_full_path[j]);
-
-        /* Find the newly added node in tr->dt_host_new by it's full path. */
-        overlay_node = dt_find_node_by_path_from(tr->dt_host_new,
-                                                 nodes_full_path[j]);
-        if ( overlay_node == NULL )
-            return -EFAULT;
-
-        /*
-         * Find previous and next node to overlay_node in dt_host_new. We will
-         * need these nodes to fix the dt_host_new mapping. When overlay_node 
is
-         * take out of dt_host_new tree and added to dt_host, link between
-         * previous node and next_node is broken. We will need to refresh
-         * dt_host_new with correct linking for any other overlay nodes
-         * extraction in future.
-         */
-        dt_for_each_device_node(tr->dt_host_new, prev_node)
-            if ( prev_node->allnext == overlay_node )
-                break;
-
-        next_node = dt_find_next_node(tr->dt_host_new, overlay_node);
-
-        write_lock(&dt_host_lock);
-
-        /* Add the node to dt_host. */
-        rc = dt_overlay_add_node(overlay_node, 
overlay_node->parent->full_name);
-        if ( rc )
-        {
-            write_unlock(&dt_host_lock);
-
-            /* Node not added in dt_host. */
-            return rc;
-        }
-
-        prev_node->allnext = next_node;
-
-        overlay_node = dt_find_node_by_path(overlay_node->full_name);
-        if ( overlay_node == NULL )
-        {
-            /* Sanity check. But code will never come here. */
-            ASSERT_UNREACHABLE();
-            return -EFAULT;
-        }
-
-        write_unlock(&dt_host_lock);
-
-        /* Keep overlay_node address in tracker. */
-        tr->nodes_address[j] = (unsigned long)overlay_node;
-    }
-
-    return 0;
-}
-/*
- * Adds device tree nodes under target node.
- * We use tr->dt_host_new to unflatten the updated device_tree_flattened.
- */
-static long handle_add_overlay_nodes(void *overlay_fdt,
-                                     uint32_t overlay_fdt_size)
-{
-    int rc;
-    unsigned int j;
-    struct dt_device_node *overlay_node;
-    struct overlay_track *tr = NULL;
-    char **nodes_full_path = NULL;
-    unsigned int new_fdt_size;
-
-    tr = xzalloc(struct overlay_track);
-    if ( tr == NULL )
-        return -ENOMEM;
-
-    new_fdt_size = fdt_totalsize(device_tree_flattened) +
-                                 fdt_totalsize(overlay_fdt);
-
-    tr->fdt = xzalloc_bytes(new_fdt_size);
-    if ( tr->fdt == NULL )
-    {
-        xfree(tr);
-        return -ENOMEM;
-    }
-
-    tr->num_nodes = overlay_node_count(overlay_fdt);
-    if ( tr->num_nodes == 0 )
-    {
-        xfree(tr->fdt);
-        xfree(tr);
-        return -ENOMEM;
-    }
-
-    tr->nodes_address = xzalloc_bytes(tr->num_nodes * sizeof(unsigned long));
-    if ( tr->nodes_address == NULL )
-    {
-        xfree(tr->fdt);
-        xfree(tr);
-        return -ENOMEM;
-    }
-
-    rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
-    if ( rc )
-    {
-        xfree(tr->nodes_address);
-        xfree(tr->fdt);
-        xfree(tr);
-        return rc;
-    }
-
-    /*
-     * Keep a copy of overlay_fdt as fdt_overlay_apply will change the input
-     * overlay's content(magic) when applying overlay.
-     */
-    tr->overlay_fdt = xzalloc_bytes(overlay_fdt_size);
-    if ( tr->overlay_fdt == NULL )
-    {
-        xfree(tr->nodes_address);
-        xfree(tr->fdt);
-        xfree(tr);
-        return -ENOMEM;
-    }
-
-    memcpy(tr->overlay_fdt, overlay_fdt, overlay_fdt_size);
-
-    spin_lock(&overlay_lock);
-
-    memcpy(tr->fdt, device_tree_flattened,
-           fdt_totalsize(device_tree_flattened));
-
-    /* Open tr->fdt with more space to accommodate the overlay_fdt. */
-    rc = fdt_open_into(tr->fdt, tr->fdt, new_fdt_size);
-    if ( rc )
-    {
-        printk(XENLOG_ERR "Increasing fdt size to accommodate overlay_fdt 
failed with error %d\n",
-               rc);
-        goto err;
-    }
-
-    nodes_full_path = xzalloc_bytes(tr->num_nodes * sizeof(char *));
-    if ( nodes_full_path == NULL )
-    {
-        rc = -ENOMEM;
-        goto err;
-    }
-
-    /*
-     * overlay_get_nodes_info is called to get the node information from dtbo.
-     * This is done before fdt_overlay_apply() because the overlay apply will
-     * erase the magic of overlay_fdt.
-     */
-    rc = overlay_get_nodes_info(overlay_fdt, nodes_full_path);
-    if ( rc )
-    {
-        printk(XENLOG_ERR "Getting nodes information failed with error %d\n",
-               rc);
-        goto err;
-    }
-
-    rc = fdt_overlay_apply(tr->fdt, overlay_fdt);
-    if ( rc )
-    {
-        printk(XENLOG_ERR "Adding overlay node failed with error %d\n", rc);
-        goto err;
-    }
-
-    /*
-     * Check if any of the node already exists in dt_host. If node already 
exits
-     * we can return here as this overlay_fdt is not suitable for overlay ops.
-     */
-    for ( j = 0; j < tr->num_nodes; j++ )
-    {
-        overlay_node = dt_find_node_by_path(nodes_full_path[j]);
-        if ( overlay_node != NULL )
-        {
-            printk(XENLOG_ERR "node %s exists in device tree\n",
-                   nodes_full_path[j]);
-            rc = -EINVAL;
-            goto err;
-        }
-    }
-
-    /*
-     * Unflatten the tr->fdt into a new dt_host.
-     * TODO: Check and add alias_scan() if it's needed for overlay in future.
-     */
-    rc = unflatten_device_tree(tr->fdt, &tr->dt_host_new);
-    if ( rc )
-    {
-        printk(XENLOG_ERR "unflatten_device_tree failed with error %d\n", rc);
-        goto err;
-    }
-
-    rc = add_nodes(tr, nodes_full_path);
-    if ( rc )
-    {
-        printk(XENLOG_ERR "Adding nodes failed. Removing the partially added 
nodes.\n");
-        goto remove_node;
-    }
-
-    INIT_LIST_HEAD(&tr->entry);
-    list_add_tail(&tr->entry, &overlay_tracker);
-
-    spin_unlock(&overlay_lock);
-
-    free_nodes_full_path(tr->num_nodes, nodes_full_path);
-
-    return rc;
-
-/*
- * Failure case. We need to remove the nodes, free tracker(if tr exists) and
- * tr->dt_host_new.
- */
- remove_node:
-    tr->num_nodes = j;
-    rc = remove_nodes(tr);
-
-    if ( rc )
-    {
-        /*
-         * User needs to provide right overlay. Incorrect node information
-         * example parent node doesn't exist in dt_host etc can cause memory
-         * leaks as removing_nodes() will fail and this means nodes memory is
-         * not freed from tracker. Which may cause memory leaks. Ideally, these
-         * device tree related mistakes will be caught by fdt_overlay_apply()
-         * but given that we don't manage that code keeping this warning 
message
-         * is better here.
-         */
-        printk(XENLOG_ERR "Removing node failed.\n");
-        spin_unlock(&overlay_lock);
-
-        free_nodes_full_path(tr->num_nodes, nodes_full_path);
-
-        return rc;
-    }
-
- err:
-    spin_unlock(&overlay_lock);
-
-    if ( tr->dt_host_new )
-        xfree(tr->dt_host_new);
-
-    free_nodes_full_path(tr->num_nodes, nodes_full_path);
-
-    xfree(tr->overlay_fdt);
-    xfree(tr->nodes_address);
-    xfree(tr->fdt);
-
-    xfree(tr);
-
-    return rc;
-}
-
-static long handle_attach_overlay_nodes(struct domain *d,
-                                        const void *overlay_fdt,
-                                        uint32_t overlay_fdt_size)
-{
-    int rc;
-    unsigned int j;
-    struct overlay_track *entry;
-
-    rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
-    if ( rc )
-        return rc;
-
-    spin_lock(&overlay_lock);
-
-    entry = find_track_entry_from_tracker(overlay_fdt, overlay_fdt_size);
-    if ( entry == NULL )
-    {
-        rc = -EINVAL;
-        goto out;
-    }
-
-    entry->irq_ranges = rangeset_new(d, "Overlays: Interrupts", 0);
-    if (entry->irq_ranges == NULL)
-    {
-        rc = -ENOMEM;
-        printk(XENLOG_ERR "Creating IRQ rangeset failed");
-        goto out;
-    }
-
-    entry->iomem_ranges = rangeset_new(d, "Overlay: I/O Memory",
-                                       RANGESETF_prettyprint_hex);
-    if (entry->iomem_ranges == NULL)
-    {
-        rc = -ENOMEM;
-        printk(XENLOG_ERR "Creating IOMMU rangeset failed");
-        goto out;
-    }
-
-    for ( j = 0; j < entry->num_nodes; j++ )
-    {
-        struct dt_device_node *overlay_node;
-
-        overlay_node = (struct dt_device_node *)entry->nodes_address[j];
-        if ( overlay_node == NULL )
-        {
-            rc = -EINVAL;
-            goto out;
-        }
-
-        write_lock(&dt_host_lock);
-        rc = handle_device(d, overlay_node, p2m_mmio_direct_c,
-                           entry->iomem_ranges, entry->irq_ranges);
-        write_unlock(&dt_host_lock);
-        if ( rc )
-        {
-            printk(XENLOG_ERR "Adding IRQ and IOMMU failed\n");
-            goto out;
-        }
-    }
-
-    spin_unlock(&overlay_lock);
-
-    return 0;
-
- out:
-    spin_unlock(&overlay_lock);
-
-    if ( entry )
-    {
-        rangeset_destroy(entry->irq_ranges);
-        rangeset_destroy(entry->iomem_ranges);
-    }
-
-    return rc;
-}
-
-long dt_overlay_sysctl(struct xen_sysctl_dt_overlay *op)
-{
-    long ret;
-    void *overlay_fdt;
-
-    if ( op->overlay_op != XEN_SYSCTL_DT_OVERLAY_ADD &&
-         op->overlay_op != XEN_SYSCTL_DT_OVERLAY_REMOVE )
-        return -EOPNOTSUPP;
-
-    if ( op->overlay_fdt_size == 0 ||
-         op->overlay_fdt_size > DT_OVERLAY_MAX_SIZE )
-        return -EINVAL;
-
-    if ( op->pad[0] || op->pad[1] || op->pad[2] )
-        return -EINVAL;
-
-    overlay_fdt = xmalloc_bytes(op->overlay_fdt_size);
-
-    if ( overlay_fdt == NULL )
-        return -ENOMEM;
-
-    ret = copy_from_guest(overlay_fdt, op->overlay_fdt, op->overlay_fdt_size);
-    if ( ret )
-    {
-        gprintk(XENLOG_ERR, "copy from guest failed\n");
-        xfree(overlay_fdt);
-
-        return -EFAULT;
-    }
-
-    if ( op->overlay_op == XEN_SYSCTL_DT_OVERLAY_REMOVE )
-        ret = handle_remove_overlay_nodes(overlay_fdt, op->overlay_fdt_size);
-    else
-        ret = handle_add_overlay_nodes(overlay_fdt, op->overlay_fdt_size);
-
-    xfree(overlay_fdt);
-
-    return ret;
-}
-
-long dt_overlay_domctl(struct domain *d, struct xen_domctl_dt_overlay *op)
-{
-    long ret;
-    void *overlay_fdt;
-
-    if ( op->overlay_op != XEN_DOMCTL_DT_OVERLAY_ATTACH )
-        return -EOPNOTSUPP;
-
-    if ( op->overlay_fdt_size == 0 ||
-         op->overlay_fdt_size > DT_OVERLAY_MAX_SIZE )
-        return -EINVAL;
-
-    if ( op->pad[0] || op->pad[1] || op->pad[2] )
-        return -EINVAL;
-
-    /* TODO: add support for non-1:1 domains using xen,reg */
-    if ( !is_domain_direct_mapped(d) )
-        return -EOPNOTSUPP;
-
-    overlay_fdt = xmalloc_bytes(op->overlay_fdt_size);
-
-    if ( overlay_fdt == NULL )
-        return -ENOMEM;
-
-    ret = copy_from_guest(overlay_fdt, op->overlay_fdt, op->overlay_fdt_size);
-    if ( ret )
-    {
-        gprintk(XENLOG_ERR, "copy from guest failed\n");
-        xfree(overlay_fdt);
-
-        return -EFAULT;
-    }
-
-    if ( op->overlay_op == XEN_DOMCTL_DT_OVERLAY_ATTACH )
-        ret = handle_attach_overlay_nodes(d, overlay_fdt, 
op->overlay_fdt_size);
-    else
-        ret = -EOPNOTSUPP;
-
-    xfree(overlay_fdt);
-
-    return ret;
-}
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
--
generated by git-patchbot for /home/xen/git/xen.git#staging



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.