x86: make vcpu_reset() preemptible

... as dropping the old page tables may take significant amounts of
time.

This is part of CVE-2013-1918 / XSA-45.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Tim Deegan <tim@xen.org>

--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -841,17 +841,16 @@
 #undef c
 }
 
-void arch_vcpu_reset(struct vcpu *v)
+int arch_vcpu_reset(struct vcpu *v)
 {
     if ( !is_hvm_vcpu(v) )
     {
         destroy_gdt(v);
-        vcpu_destroy_pagetables(v, 0);
-    }
-    else
-    {
-        vcpu_end_shutdown_deferral(v);
+        return vcpu_destroy_pagetables(v);
     }
+
+    vcpu_end_shutdown_deferral(v);
+    return 0;
 }
 
 /* 
@@ -1929,7 +1928,7 @@
         for_each_vcpu ( d, v )
         {
             /* Drop the in-use references to page-table bases. */
-            ret = vcpu_destroy_pagetables(v, 1);
+            ret = vcpu_destroy_pagetables(v);
             if ( ret )
                 return ret;
 
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -2583,8 +2583,11 @@
 
     for_each_vcpu ( d, v )
     {
+        int rc;
+
         vlapic_reset(vcpu_vlapic(v));
-        vcpu_reset(v);
+        rc = vcpu_reset(v);
+        ASSERT(!rc);
     }
 
     vpic_reset(d);
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -2480,7 +2480,7 @@
     return rc;
 }
 
-int vcpu_destroy_pagetables(struct vcpu *v, bool_t preemptible)
+int vcpu_destroy_pagetables(struct vcpu *v)
 {
     unsigned long mfn = pagetable_get_pfn(v->arch.guest_table);
     struct page_info *page;
@@ -2500,7 +2500,7 @@
         if ( paging_mode_refcounts(v->domain) )
             put_page(page);
         else
-            rc = put_page_and_type_preemptible(page, preemptible);
+            rc = put_page_and_type_preemptible(page, 1);
     }
 
 #ifdef __x86_64__
@@ -2526,7 +2526,7 @@
             if ( paging_mode_refcounts(v->domain) )
                 put_page(page);
             else
-                rc = put_page_and_type_preemptible(page, preemptible);
+                rc = put_page_and_type_preemptible(page, 1);
         }
         if ( !rc )
             v->arch.guest_table_user = pagetable_null();
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -720,14 +720,18 @@
     return arch_set_info_guest(v, ctxt);
 }
 
-void vcpu_reset(struct vcpu *v)
+int vcpu_reset(struct vcpu *v)
 {
     struct domain *d = v->domain;
+    int rc;
 
     vcpu_pause(v);
     domain_lock(d);
 
-    arch_vcpu_reset(v);
+    set_bit(_VPF_in_reset, &v->pause_flags);
+    rc = arch_vcpu_reset(v);
+    if ( rc )
+        goto out_unlock;
 
     set_bit(_VPF_down, &v->pause_flags);
 
@@ -743,9 +747,13 @@
 #endif
     cpus_clear(v->cpu_affinity_tmp);
     clear_bit(_VPF_blocked, &v->pause_flags);
+    clear_bit(_VPF_in_reset, &v->pause_flags);
 
+ out_unlock:
     domain_unlock(v->domain);
     vcpu_unpause(v);
+
+    return rc;
 }
 
 
--- a/xen/common/domctl.c
+++ b/xen/common/domctl.c
@@ -281,8 +281,10 @@
 
         if ( guest_handle_is_null(op->u.vcpucontext.ctxt) )
         {
-            vcpu_reset(v);
-            ret = 0;
+            ret = vcpu_reset(v);
+            if ( ret == -EAGAIN )
+                ret = hypercall_create_continuation(
+                          __HYPERVISOR_domctl, "h", u_domctl);
             goto svc_out;
         }
 
--- a/xen/include/asm-x86/mm.h
+++ b/xen/include/asm-x86/mm.h
@@ -529,7 +529,7 @@
 int new_guest_cr3(unsigned long pfn);
 void make_cr3(struct vcpu *v, unsigned long mfn);
 void update_cr3(struct vcpu *v);
-int vcpu_destroy_pagetables(struct vcpu *, bool_t preemptible);
+int vcpu_destroy_pagetables(struct vcpu *);
 void propagate_page_fault(unsigned long addr, u16 error_code);
 void *do_page_walk(struct vcpu *v, unsigned long addr);
 
--- a/xen/include/xen/domain.h
+++ b/xen/include/xen/domain.h
@@ -15,7 +15,7 @@
     struct domain *d, int vcpuid, vcpu_guest_context_u ctxt);
 struct vcpu *alloc_idle_vcpu(unsigned int cpu_id);
 struct vcpu *alloc_dom0_vcpu0(void);
-void vcpu_reset(struct vcpu *v);
+int vcpu_reset(struct vcpu *);
 
 struct xen_domctl_getdomaininfo;
 void getdomaininfo(struct domain *d, struct xen_domctl_getdomaininfo *info);
@@ -57,7 +57,7 @@
 
 void arch_dump_domain_info(struct domain *d);
 
-void arch_vcpu_reset(struct vcpu *v);
+int arch_vcpu_reset(struct vcpu *);
 
 bool_t domctl_lock_acquire(void);
 void domctl_lock_release(void);
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -558,6 +558,9 @@
  /* VCPU affinity has changed: migrating to a new CPU. */
 #define _VPF_migrating       3
 #define VPF_migrating        (1UL<<_VPF_migrating)
+ /* VCPU is being reset. */
+#define _VPF_in_reset        7
+#define VPF_in_reset         (1UL<<_VPF_in_reset)
 
 static inline int vcpu_runnable(struct vcpu *v)
 {
--- a/xen/arch/x86/hvm/vlapic.c
+++ b/xen/arch/x86/hvm/vlapic.c
@@ -245,6 +245,7 @@
     struct vcpu *v = (struct vcpu *)_vcpu;
     struct domain *d = v->domain;
     bool_t fpu_initialised;
+    int rc;
 
     /* If the VCPU is not on its way down we have nothing to do. */
     if ( !test_bit(_VPF_down, &v->pause_flags) )
@@ -259,7 +260,8 @@
     /* Reset necessary VCPU state. This does not include FPU state. */
     domain_lock(d);
     fpu_initialised = v->fpu_initialised;
-    vcpu_reset(v);
+    rc = vcpu_reset(v);
+    ASSERT(!rc);
     v->fpu_initialised = fpu_initialised;
     vlapic_reset(vcpu_vlapic(v));
     domain_unlock(d);
