|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v4 12/16] xen: implement new foreign copy hypercall
From: Frediano Ziglio <frediano.ziglio@xxxxxxxxxx>
Add a sub hypercall to __HYPERVISOR_memory_op to allow to
read/write memory from/to a foreign domain.
Signed-off-by: Frediano Ziglio <frediano.ziglio@xxxxxxxxxx>
---
xen/common/memory.c | 133 ++++++++++++++++++++++++++++++++++++
xen/include/public/memory.h | 40 ++++++++++-
2 files changed, 172 insertions(+), 1 deletion(-)
diff --git a/xen/common/memory.c b/xen/common/memory.c
index 3672bda025..6a2d9c3190 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -1545,6 +1545,132 @@ static int acquire_resource(
return rc;
}
+/*
+ * The "noinline" qualifier avoid the compiler to create a large function
+ * consuming quite a lot of stack.
+ */
+static int noinline mem_foreigncopy(
+ XEN_GUEST_HANDLE_PARAM(xen_foreigncopy_t) arg)
+{
+ struct domain *d, *const currd = current->domain;
+ xen_foreigncopy_t copy;
+ int rc, direction;
+
+ if ( !arch_acquire_resource_check(currd) )
+ return -EACCES;
+
+ if ( copy_from_guest(©, arg, 1) )
+ return -EFAULT;
+
+ if ( copy.flags & ~1u )
+ return -EINVAL;
+
+ direction = copy.flags & XENMEM_foreigncopy_direction;
+
+ if ( copy.nr_frames == 0 )
+ return 0;
+
+ rc = rcu_lock_remote_domain_by_id(copy.domid, &d);
+ if ( rc )
+ return rc;
+
+ /*
+ * Check we are allowed to map and access these foreign pages.
+ */
+ rc = xsm_map_gmfn_foreign(XSM_TARGET, currd, d);
+ if ( rc )
+ goto out;
+
+ do {
+ /*
+ * Arbitrary size. Not too much stack space, and a reasonable stride
+ * for continuation checks.
+ */
+ xen_pfn_t gfn_list[32];
+ unsigned int todo = MIN(ARRAY_SIZE(gfn_list), copy.nr_frames);
+
+ rc = -EFAULT;
+ if ( copy_from_guest(gfn_list, copy.frame_list, todo) )
+ goto out;
+
+ for ( unsigned i = 0; i < todo; i++ )
+ {
+ struct page_info *foreign_page;
+ void *foreign;
+ p2m_type_t p2mt;
+
+ foreign_page = get_page_from_gfn(d, gfn_list[i], &p2mt, P2M_ALLOC);
+
+ if ( unlikely(p2mt != p2m_ram_rw
+#ifdef CONFIG_X86
+ && p2mt != p2m_ram_logdirty
+#endif
+ ) && foreign_page )
+ {
+ put_page(foreign_page);
+ foreign_page = NULL;
+ }
+ if ( unlikely(!foreign_page) )
+ {
+ gdprintk(XENLOG_WARNING,
+ "Error accessing foreign mfn %" PRI_mfn "\n",
+ gfn_list[i]);
+ rc = -EINVAL;
+ copy.nr_frames -= i;
+ guest_handle_add_offset(copy.frame_list, i);
+ goto out;
+ }
+
+ /* A page is dirtied when it's being copied to. */
+ if ( direction == XENMEM_foreigncopy_to )
+ paging_mark_dirty(d, page_to_mfn(foreign_page));
+
+ foreign = map_domain_page(page_to_mfn(foreign_page));
+ if ( direction == XENMEM_foreigncopy_from )
+ rc = copy_to_guest(copy.buffer, foreign, PAGE_SIZE);
+ else
+ rc = copy_from_guest(foreign, copy.buffer, PAGE_SIZE);
+ unmap_domain_page(foreign);
+ put_page(foreign_page);
+
+ if ( unlikely(rc) )
+ {
+ gdprintk(XENLOG_WARNING,
+ "Error copying to mfn %" PRI_mfn "\n", gfn_list[i]);
+ copy.nr_frames -= i;
+ guest_handle_add_offset(copy.frame_list, i);
+ goto out;
+ }
+
+ guest_handle_add_offset(copy.buffer, PAGE_SIZE);
+ }
+
+ copy.nr_frames -= todo;
+ guest_handle_add_offset(copy.frame_list, todo);
+
+ if ( copy.nr_frames && hypercall_preempt_check() )
+ {
+ rc = hypercall_create_continuation(
+ __HYPERVISOR_memory_op, "lh", XENMEM_foreigncopy, arg);
+ goto out;
+ }
+ } while ( copy.nr_frames );
+
+ rc = 0;
+
+ out:
+ rcu_unlock_domain(d);
+
+ /* Update in all cases, it allows the caller to know how many
+ * frames were successfully copied and the continuation to
+ * continue correctly.
+ */
+ if ( copy_to_guest(arg, ©, 1) )
+ rc = -EFAULT;
+
+ return rc;
+}
+
long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
struct domain *d, *curr_d = current->domain;
@@ -2012,6 +2138,13 @@ long do_memory_op(unsigned long cmd,
XEN_GUEST_HANDLE_PARAM(void) arg)
start_extent);
break;
+ case XENMEM_foreigncopy:
+ if ( unlikely(start_extent) )
+ return -EINVAL;
+
+ rc = mem_foreigncopy(guest_handle_cast(arg, xen_foreigncopy_t));
+ break;
+
default:
rc = arch_memory_op(cmd, arg);
break;
diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h
index bd9fc37b52..b48d1f378f 100644
--- a/xen/include/public/memory.h
+++ b/xen/include/public/memory.h
@@ -740,7 +740,45 @@ struct xen_vnuma_topology_info {
typedef struct xen_vnuma_topology_info xen_vnuma_topology_info_t;
DEFINE_XEN_GUEST_HANDLE(xen_vnuma_topology_info_t);
-/* Next available subop number is 29 */
+/*
+ * Copy memory from/to a given domain.
+ */
+#define XENMEM_foreigncopy 29
+struct xen_foreigncopy {
+ /* IN - The domain whose resource is to be copied. */
+ domid_t domid;
+
+ /* IN - Flags. */
+#define XENMEM_foreigncopy_from 0
+#define XENMEM_foreigncopy_to 1
+#define XENMEM_foreigncopy_direction 1
+ uint16_t flags;
+
+ /*
+ * IN
+ *
+ * As an IN parameter number of frames of the domain to be copied.
+ */
+ uint32_t nr_frames;
+
+ /*
+ * IN
+ *
+ * Frames to be copied.
+ */
+ XEN_GUEST_HANDLE(xen_pfn_t) frame_list;
+
+ /*
+ * IN/OUT
+ *
+ * Userspace buffer to read/write from.
+ */
+ XEN_GUEST_HANDLE(uint8) buffer;
+};
+typedef struct xen_foreigncopy xen_foreigncopy_t;
+DEFINE_XEN_GUEST_HANDLE(xen_foreigncopy_t);
+
+/* Next available subop number is 30 */
#endif /* __XEN_PUBLIC_MEMORY_H__ */
--
2.54.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |