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

[XEN][RFC PATCH 09/13] xen/arm: Implement device tree node removal functionalities


  • To: <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Vikram Garhwal <fnu.vikram@xxxxxxxxxx>
  • Date: Wed, 1 Sep 2021 23:05:59 -0700
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 149.199.62.198) smtp.rcpttodomain=lists.xenproject.org smtp.mailfrom=xilinx.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=xilinx.com; dkim=none (message not signed); arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Bk58T2PdmP0MpUXqDTQL+WHaYdm6dJQvCtJfKGAwTtY=; b=Gr3IkqVkZ3nbcF2g6AmzHCrwhD7TpaUL/J623YInRRQQwNuMt0oEvfHv5RSjQtxp3YhARmZ3vE0TTPOP7MKObZb4/07WxGKA915Kle7tovwA9WvaGcIFyFQvFp9h1oKBBd6PbzfsfeI5r+XzZjGW8aPlErba6h9U94tvttjLgmLyrV3+26LieVWrzMbj6GESn/dMMgvM3MLMeCxsanApdfTAgNQVUrVt8r8O+4U0He9qiA7a0XfqalW14nCi+lmvZ/Bf53OgWkHqnfDfsAGh5qEsg6TmhfOoJaiV9vHZzNYkARYxCAWTRmSj2JRtpbVSVKNX/B5QhCDefVSLvqeucg==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=DqkxgLDre73op25XKcR7jwvLLzEuiKnjoIesQcZcYYRcePW/Wczl6JfiC9Uir3gQK8nM3zNoTtErqKygYvEy1dkmH4H9s4dfeAcxub7lv7x/8yGC94r71oG++PKDB7197eywaey815bCUl1WDPiL8AKZgQForTwgwTew6DltJlL/GvTk59TdUROA7meHHXL0d3lvAienNWYmBxeeNvKJEwzNCesJMfJkCd0wPd22bYrW0rpilKaP9Dp0YrOpwS27MpaU4WA3ZWmtN2KLkmXp00ruvPiNHODynhRfxIUs5VHSzlRewe7EzZa2eajXfdcANqW+hcYQsQEiO2yS8zjMAQ==
  • Cc: <sstabellini@xxxxxxxxxx>, <julien@xxxxxxx>, Vikram Garhwal <fnu.vikram@xxxxxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, George Dunlap <george.dunlap@xxxxxxxxxx>, Ian Jackson <iwj@xxxxxxxxxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Wei Liu <wl@xxxxxxx>
  • Delivery-date: Thu, 02 Sep 2021 06:51:01 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

Introduce domctl XEN_DOMCTL_delfpga to remove a device-tree node added through
device tree overlay. Currently, this supports removing one node at a time.

DT node removal works with the following steps:
    1. finds a node with given path.
    2. Check if the node is used by any of dom0 or domus. It removes the node
only when it's not used by any domain.
    3. Removes IRQ permissions.
    4. Remove MMIO access.
    5. Find the node in dt_host and delete the device node entry from dt_host.
    6. Free the overlay_tracker node.

Also, added overlay_track struct to keep the track of added node through device
tree overlay. overlay_track has dt_host_new which is unflattened form of updated
fdt and name of overlay node.

When a node is removed, we also free the memory used by overlay_track for the
particular overlay node.

Signed-off-by: Vikram Garhwal <fnu.vikram@xxxxxxxxxx>
---
 xen/arch/arm/domctl.c         | 183 ++++++++++++++++++++++++++++++++++++++++++
 xen/common/device_tree.c      |  51 ++++++++++++
 xen/include/public/domctl.h   |   9 +++
 xen/include/xen/device_tree.h |   1 +
 4 files changed, 244 insertions(+)

diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c
index b7d27f3..5986934 100644
--- a/xen/arch/arm/domctl.c
+++ b/xen/arch/arm/domctl.c
@@ -9,11 +9,34 @@
 #include <xen/hypercall.h>
 #include <xen/iocap.h>
 #include <xen/lib.h>
+#include <xen/list.h>
 #include <xen/mm.h>
 #include <xen/sched.h>
 #include <xen/types.h>
 #include <xsm/xsm.h>
 #include <public/domctl.h>
+#include <xen/xmalloc.h>
+#include <xen/device_tree.h>
+#include <asm/domain_build.h>
+
+/*
+ * overlay_node_track describes information about added nodes through dtbo.
+ * @dt_host_new: Pointer to the updated dt_host_new unflattened 'updated fdt'.
+ * @node_fullname: Store the name of nodes.
+ * @entry: List pointer.
+ */
+struct overlay_track {
+    struct list_head entry;
+    struct dt_device_node *dt_host_new;
+    /*
+     * TODO: We keep max nodes to 10 in an overlay. But for now we will be
+     * adding one node only.
+     */
+    char *node_fullname;
+};
+
+static LIST_HEAD(overlay_tracker);
+static DEFINE_SPINLOCK(overlay_lock);
 
 void arch_get_domain_info(const struct domain *d,
                           struct xen_domctl_getdomaininfo *info)
@@ -45,6 +68,132 @@ static int handle_vuart_init(struct domain *d,
     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_del_fpga_nodes(char *full_dt_node_path)
+{
+    struct domain *d = hardware_domain;
+    int rc = 0;
+    uint32_t ret = 0;
+    struct dt_device_node *fpga_device;
+    struct overlay_track *entry, *temp;
+    unsigned int naddr;
+    unsigned int i, nirq;
+    struct dt_raw_irq rirq;
+    u64 addr, size;
+
+    fpga_device = dt_find_node_by_path(full_dt_node_path);
+
+    if ( fpga_device == NULL )
+    {
+        printk(XENLOG_G_ERR "Device %s is not present in the tree\n",
+               full_dt_node_path);
+        return -EINVAL;
+    }
+
+    ret = dt_device_used_by(fpga_device);
+
+    if ( ret != 0 && ret != DOMID_IO )
+    {
+        printk(XENLOG_G_ERR "Cannot remove the device as it is being used by"
+               "domain %d\n", ret);
+        return -EPERM;
+    }
+
+    spin_lock(&overlay_lock);
+
+    nirq = dt_number_of_irq(fpga_device);
+
+    /* Remove IRQ permission */
+    for ( i = 0; i < nirq; i++ )
+    {
+        rc = dt_device_get_raw_irq(fpga_device, i, &rirq);
+        if ( rc )
+        {
+            printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
+                   i, dt_node_full_name(fpga_device));
+            goto out;
+        }
+
+        rc = platform_get_irq(fpga_device, i);
+        if ( rc < 0 )
+        {
+            printk(XENLOG_ERR "Unable to get irq %u for %s\n",
+                   i, dt_node_full_name(fpga_device));
+            goto out;
+        }
+
+        rc = irq_deny_access(d, rc);
+
+        if ( rc )
+        {
+            printk(XENLOG_ERR "unable to revoke access for irq %u for %s\n",
+                   i, dt_node_full_name(fpga_device));
+            goto out;
+        }
+    }
+
+    rc = iommu_remove_dt_device(fpga_device);
+
+    if ( rc )
+        goto out;
+
+    naddr = dt_number_of_address(fpga_device);
+
+    /* Remove mmio access. */
+    for ( i = 0; i < naddr; i++ )
+    {
+        rc = dt_device_get_address(fpga_device, i, &addr, &size);
+
+        if ( rc )
+        {
+            printk(XENLOG_ERR "Unable to retrieve address %u for %s\n",
+                   i, dt_node_full_name(fpga_device));
+            goto out;
+        }
+
+        rc = iomem_deny_access(d, paddr_to_pfn(addr),
+                               paddr_to_pfn(PAGE_ALIGN(addr + size - 1)));
+
+        if ( rc )
+        {
+            printk(XENLOG_ERR "Unable to remove dom%d access to"
+                    " 0x%"PRIx64" - 0x%"PRIx64"\n",
+                    d->domain_id,
+                    addr & PAGE_MASK, PAGE_ALIGN(addr + size) - 1);
+            goto out;
+        }
+    }
+
+    rc = fpga_del_node(fpga_device);
+
+    if ( rc )
+        goto out;
+
+    list_for_each_entry_safe( entry, temp, &overlay_tracker, entry )
+    {
+        if ( (strcmp(full_dt_node_path, entry->node_fullname) == 0) )
+        {
+            list_del(&entry->entry);
+            xfree(entry->node_fullname);
+            xfree(entry->dt_host_new);
+            xfree(entry);
+            goto out;
+        }
+    }
+
+    printk(XENLOG_G_ERR "Cannot find the node in tracker. Memory will not"
+           "be freed\n");
+    rc = -ENOENT;
+
+out:
+    spin_unlock(&overlay_lock);
+    return rc;
+}
+
 long arch_do_domctl(struct xen_domctl *domctl, struct domain *d,
                     XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
 {
@@ -173,6 +322,40 @@ long arch_do_domctl(struct xen_domctl *domctl, struct 
domain *d,
 
         return rc;
     }
+
+    case XEN_DOMCTL_delfpga:
+    {
+        char *full_dt_node_path;
+        int rc;
+
+        if ( domctl->u.fpga_del_dt.size > 0 )
+            full_dt_node_path = xmalloc_bytes(domctl->u.fpga_del_dt.size);
+        else
+            return -EINVAL;
+
+        if ( full_dt_node_path == NULL )
+            return -ENOMEM;
+
+        rc = copy_from_guest(full_dt_node_path,
+                             domctl->u.fpga_del_dt.full_dt_node_path,
+                             domctl->u.fpga_del_dt.size);
+        if ( rc )
+        {
+            gprintk(XENLOG_ERR, "copy from guest failed\n");
+            xfree(full_dt_node_path);
+
+            return -EFAULT;
+        }
+
+        full_dt_node_path[domctl->u.fpga_del_dt.size - 1] = '\0';
+
+        rc = handle_del_fpga_nodes(full_dt_node_path);
+
+        xfree(full_dt_node_path);
+
+        return rc;
+    }
+
     default:
     {
         int rc;
diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
index 4946e83..04f2578 100644
--- a/xen/common/device_tree.c
+++ b/xen/common/device_tree.c
@@ -324,6 +324,57 @@ void dt_print_node_names(struct dt_device_node *dt)
     return;
 }
 
+int fpga_del_node(struct dt_device_node *device_node)
+{
+    struct dt_device_node *np;
+    struct dt_device_node *parent_node;
+    struct dt_device_node *current_node;
+
+    parent_node = device_node->parent;
+
+    current_node = parent_node;
+
+    if ( parent_node == NULL )
+    {
+        dt_dprintk("%s's parent node not found\n", device_node->name);
+        return -EFAULT;
+    }
+
+    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 ( np->name == device_node->name )
+    {
+        current_node->allnext = np->next;
+        return 0;
+    }
+
+    for ( np = parent_node->child; np->sibling != NULL; np = np->sibling )
+    {
+        current_node = np;
+        if ( np->sibling->name == device_node->name )
+        {
+            /* Found the node. Now we remove it. */
+            current_node->allnext = np->allnext->allnext;
+
+            if ( np->sibling->sibling )
+                current_node->sibling = np->sibling->sibling;
+            else
+                current_node->sibling = NULL;
+
+            break;
+        }
+    }
+
+    return 0;
+}
+
 int dt_find_node_by_gpath(XEN_GUEST_HANDLE(char) u_path, uint32_t u_plen,
                           struct dt_device_node **node)
 {
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 96696e3..b1b8efd 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -1169,6 +1169,13 @@ struct xen_domctl_vmtrace_op {
 typedef struct xen_domctl_vmtrace_op xen_domctl_vmtrace_op_t;
 DEFINE_XEN_GUEST_HANDLE(xen_domctl_vmtrace_op_t);
 
+/* XEN_DOMCTL_fpga_del. */
+struct xen_domctl_fpga_del_dt {
+    XEN_GUEST_HANDLE_64(char) full_dt_node_path;
+    uint32_t size;
+};
+
+
 struct xen_domctl {
     uint32_t cmd;
 #define XEN_DOMCTL_createdomain                   1
@@ -1254,6 +1261,7 @@ struct xen_domctl {
 #define XEN_DOMCTL_get_cpu_policy                82
 #define XEN_DOMCTL_set_cpu_policy                83
 #define XEN_DOMCTL_vmtrace_op                    84
+#define XEN_DOMCTL_delfpga                      86
 #define XEN_DOMCTL_gdbsx_guestmemio            1000
 #define XEN_DOMCTL_gdbsx_pausevcpu             1001
 #define XEN_DOMCTL_gdbsx_unpausevcpu           1002
@@ -1315,6 +1323,7 @@ struct xen_domctl {
         struct xen_domctl_psr_alloc         psr_alloc;
         struct xen_domctl_vuart_op          vuart_op;
         struct xen_domctl_vmtrace_op        vmtrace_op;
+        struct xen_domctl_fpga_del_dt       fpga_del_dt;
         uint8_t                             pad[128];
     } u;
 };
diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h
index 7cc6093..eb7f645 100644
--- a/xen/include/xen/device_tree.h
+++ b/xen/include/xen/device_tree.h
@@ -496,6 +496,7 @@ int dt_find_node_by_gpath(XEN_GUEST_HANDLE(char) u_path, 
uint32_t u_plen,
  * Prints all node names.
  */
 void dt_print_node_names(struct dt_device_node *dt);
+int fpga_del_node(struct dt_device_node *device_node);
 
 /**
  * dt_get_parent - Get a node's parent if any
-- 
2.7.4




 


Rackspace

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