# HG changeset patch
# User Jeremy Fitzhardinge <jeremy@xxxxxxxxxxxxx>
# Date 1180000047 -3600
# Node ID 020530a6ff5c24c3a8289f8f687ce2f10aff5ca7
# Parent 471478a1b89e2681c3b1efa3abde6ec47eb36d05
Implement VCPUOP_register_vcpu_info
This change implements the VCPUOP_register_vcpu_info vcpu_op. This
allows a guest to choose where each VCPU's vcpu_info structure is
placed within its address space, allowing it to put it somewhere which
is easily accessible via some per-cpu data access mechanism.
When changing the mapping of the vcpu info, there's no obvious way to
prevent the other vcpus from getting a stale pointer of the vcpu_info,
which could result in them accessing bad memory (stale pointers to the
shared_info page are not a problem, because its always valid). To
avoid this, we prevent guests from changing the vcpu_info location
more than once, since there's no obvious need to allow them to do this
at this point.
(If we really want to allow guests to update the vcpu_info location
more than once, then some sort of RCU wait between updating the
pointer and performing the unmap may be the way to do it.)
Signed-off-by: Jeremy Fitzhardinge <jeremy@xxxxxxxxxxxxx>
---
xen/arch/x86/domain.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++
xen/common/domain.c | 1
xen/include/public/vcpu.h | 3 -
xen/include/xen/sched.h | 1
4 files changed, 105 insertions(+), 2 deletions(-)
diff -r 471478a1b89e -r 020530a6ff5c xen/arch/x86/domain.c
--- a/xen/arch/x86/domain.c Thu May 24 10:45:03 2007 +0100
+++ b/xen/arch/x86/domain.c Thu May 24 10:47:27 2007 +0100
@@ -28,6 +28,7 @@
#include <xen/event.h>
#include <xen/console.h>
#include <xen/percpu.h>
+#include <xen/compat.h>
#include <asm/regs.h>
#include <asm/mc146818rtc.h>
#include <asm/system.h>
@@ -49,6 +50,8 @@ DEFINE_PER_CPU(struct vcpu *, curr_vcpu)
DEFINE_PER_CPU(struct vcpu *, curr_vcpu);
DEFINE_PER_CPU(__u64, efer);
+static void unmap_vcpu_info(struct vcpu *v);
+
static void paravirt_ctxt_switch_from(struct vcpu *v);
static void paravirt_ctxt_switch_to(struct vcpu *v);
@@ -728,11 +731,94 @@ int arch_set_info_guest(
int arch_vcpu_reset(struct vcpu *v)
{
+ unmap_vcpu_info(v);
destroy_gdt(v);
vcpu_destroy_pagetables(v);
return 0;
}
+/*
+ * Unmap the vcpu info page if the guest decided to place it somewhere
+ * else. This is only used from arch_vcpu_reset, so there's no need
+ * to do anything clever.
+ */
+static void
+unmap_vcpu_info(struct vcpu *v)
+{
+ struct domain *d = v->domain;
+ unsigned long mfn;
+
+ if ( v->vcpu_info_mfn == INVALID_MFN )
+ return;
+
+ mfn = v->vcpu_info_mfn;
+ unmap_domain_page_global( v->vcpu_info );
+
+ v->vcpu_info = shared_info_addr(d, vcpu_info[v->vcpu_id]);
+ v->vcpu_info_mfn = INVALID_MFN;
+
+ put_page_and_type(mfn_to_page(mfn));
+}
+
+/*
+ * Map a guest page in and point the vcpu_info pointer at it. This
+ * makes sure that the vcpu_info is always pointing at a valid piece
+ * of memory, and it sets a pending event to make sure that a pending
+ * event doesn't get missed.
+ */
+static int
+map_vcpu_info(struct vcpu *v, unsigned long mfn, unsigned offset)
+{
+ struct domain *d = v->domain;
+ void *mapping;
+ vcpu_info_t *new_info;
+ int i;
+
+ if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) )
+ return -EINVAL;
+
+ if ( mfn == INVALID_MFN ||
+ v->vcpu_info_mfn != INVALID_MFN )
+ return -EINVAL;
+
+ mfn = gmfn_to_mfn(d, mfn);
+ if ( !mfn_valid(mfn) ||
+ !get_page_and_type(mfn_to_page(mfn), d, PGT_writable_page) )
+ return -EINVAL;
+
+ mapping = map_domain_page_global(mfn);
+ if ( mapping == NULL )
+ {
+ put_page_and_type(mfn_to_page(mfn));
+ return -ENOMEM;
+ }
+
+ new_info = (vcpu_info_t *)(mapping + offset);
+
+ memcpy(new_info, v->vcpu_info, sizeof(*new_info));
+
+ v->vcpu_info = new_info;
+ v->vcpu_info_mfn = mfn;
+
+ /* make sure all the pointers are uptodate before setting pending */
+ wmb();
+
+ /* Mark everything as being pending just to make sure nothing gets
+ lost. The domain will get a spurious event, but it can
+ cope. */
+ vcpu_info(v, evtchn_upcall_pending) = 1;
+ for ( i = 0; i < BITS_PER_GUEST_LONG(d); i++ )
+ set_bit(i, vcpu_info_addr(v, evtchn_pending_sel));
+
+ /* Only bother to update time for the current vcpu. If we're
+ * operating on another vcpu, then it had better not be running at
+ * the time. */
+ if ( v == current )
+ update_vcpu_system_time(v);
+
+ return 0;
+}
+
long
arch_do_vcpu_op(
int cmd, struct vcpu *v, XEN_GUEST_HANDLE(void) arg)
@@ -765,6 +851,22 @@ arch_do_vcpu_op(
vcpu_runstate_get(v, &runstate);
__copy_to_guest(runstate_guest(v), &runstate, 1);
}
+
+ break;
+ }
+
+ case VCPUOP_register_vcpu_info:
+ {
+ struct domain *d = v->domain;
+ struct vcpu_register_vcpu_info info;
+
+ rc = -EFAULT;
+ if ( copy_from_guest(&info, arg, 1) )
+ break;
+
+ LOCK_BIGLOCK(d);
+ rc = map_vcpu_info(v, info.mfn, info.offset);
+ UNLOCK_BIGLOCK(d);
break;
}
diff -r 471478a1b89e -r 020530a6ff5c xen/common/domain.c
--- a/xen/common/domain.c Thu May 24 10:45:03 2007 +0100
+++ b/xen/common/domain.c Thu May 24 10:47:27 2007 +0100
@@ -136,6 +136,7 @@ struct vcpu *alloc_vcpu(
v->domain = d;
v->vcpu_id = vcpu_id;
+ v->vcpu_info_mfn = INVALID_MFN;
v->runstate.state = is_idle_vcpu(v) ? RUNSTATE_running : RUNSTATE_offline;
v->runstate.state_entry_time = NOW();
diff -r 471478a1b89e -r 020530a6ff5c xen/include/public/vcpu.h
--- a/xen/include/public/vcpu.h Thu May 24 10:45:03 2007 +0100
+++ b/xen/include/public/vcpu.h Thu May 24 10:47:27 2007 +0100
@@ -168,8 +168,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_set_singles
* The pointer need not be page aligned, but the structure must not
* cross a page boundary.
*
- * If the specified mfn is INVALID_MFN, then it reverts to using the
- * vcpu_info structure in the shared_info page.
+ * This may be called only once per vcpu.
*/
#define VCPUOP_register_vcpu_info 10 /* arg == struct vcpu_info */
struct vcpu_register_vcpu_info {
diff -r 471478a1b89e -r 020530a6ff5c xen/include/xen/sched.h
--- a/xen/include/xen/sched.h Thu May 24 10:45:03 2007 +0100
+++ b/xen/include/xen/sched.h Thu May 24 10:47:27 2007 +0100
@@ -75,6 +75,7 @@ struct vcpu
int processor;
vcpu_info_t *vcpu_info;
+ unsigned long vcpu_info_mfn;
struct domain *domain;
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|