[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
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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |