Fix save/restore for HVM domains with viridian=1
authorPaul Durrant <paul.durrant@citrix.com>
Fri, 25 Nov 2011 15:30:41 +0000 (15:30 +0000)
committerPaul Durrant <paul.durrant@citrix.com>
Fri, 25 Nov 2011 15:30:41 +0000 (15:30 +0000)
xc_domain_save/restore currently pay no attention to
HVM_PARAM_VIRIDIAN which results in an HVM domain running a recent
version on Windows (post-Vista) locking up on a domain restore due to
EOIs (done via a viridian MSR write) being silently dropped.  This
patch adds an extra save entry for the viridian parameter and also
adds code in the viridian kernel module to catch attempted use of
viridian functionality when the HVM parameter has not been set.

Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
Committed-by: Keir Fraser <keir@xen.org>
tools/libxc/xc_domain_restore.c
tools/libxc/xc_domain_save.c
tools/libxc/xg_save_restore.h
xen/arch/x86/hvm/viridian.c

index 5cf099c92e9198562e5ddc0e0981c216860fc844..882678a9c5f2041b468594b339d66d4dbde6510c 100644 (file)
@@ -675,6 +675,7 @@ typedef struct {
     uint64_t vm86_tss;
     uint64_t console_pfn;
     uint64_t acpi_ioport_location;
+    uint64_t viridian;
 } pagebuf_t;
 
 static int pagebuf_init(pagebuf_t* buf)
@@ -809,6 +810,16 @@ static int pagebuf_get_one(xc_interface *xch, struct restore_ctx *ctx,
         }
         return pagebuf_get_one(xch, ctx, buf, fd, dom);
 
+    case XC_SAVE_ID_HVM_VIRIDIAN:
+        /* Skip padding 4 bytes then read the acpi ioport location. */
+        if ( RDEXACT(fd, &buf->viridian, sizeof(uint32_t)) ||
+             RDEXACT(fd, &buf->viridian, sizeof(uint64_t)) )
+        {
+            PERROR("error read the viridian flag");
+            return -1;
+        }
+        return pagebuf_get_one(xch, ctx, buf, fd, dom);
+
     default:
         if ( (count > MAX_BATCH_SIZE) || (count < 0) ) {
             ERROR("Max batch size exceeded (%d). Giving up.", count);
@@ -1440,6 +1451,9 @@ int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom,
             fcntl(io_fd, F_SETFL, orig_io_fd_flags | O_NONBLOCK);
     }
 
+    if (pagebuf.viridian != 0)
+        xc_set_hvm_param(xch, dom, HVM_PARAM_VIRIDIAN, 1);
+
     if (pagebuf.acpi_ioport_location == 1) {
         DBGPRINTF("Use new firmware ioport from the checkpoint\n");
         xc_set_hvm_param(xch, dom, HVM_PARAM_ACPI_IOPORTS_LOCATION, 1);
index 76a377acd448246bb92fb94472af450a8918cc1a..88edd37fe0233198ab2d11039f882e280b1a2952 100644 (file)
@@ -1506,6 +1506,18 @@ int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iter
             PERROR("Error when writing the firmware ioport version");
             goto out;
         }
+
+        chunk.id = XC_SAVE_ID_HVM_VIRIDIAN;
+        chunk.data = 0;
+        xc_get_hvm_param(xch, dom, HVM_PARAM_VIRIDIAN,
+                         (unsigned long *)&chunk.data);
+
+        if ( (chunk.data != 0) &&
+             wrexact(io_fd, &chunk, sizeof(chunk)) )
+        {
+            PERROR("Error when writing the viridian flag");
+            goto out;
+        }
     }
 
     if ( !callbacks->checkpoint )
index 02116012e2a55fb7268a92ad9a93db6a0b8edb25..9648e80c3a12c7a0f40e84c96718ba20da3f885e 100644 (file)
 #define XC_SAVE_ID_HVM_CONSOLE_PFN    -8 /* (HVM-only) */
 #define XC_SAVE_ID_LAST_CHECKPOINT    -9 /* Commit to restoring after completion of current iteration. */
 #define XC_SAVE_ID_HVM_ACPI_IOPORTS_LOCATION -10
+#define XC_SAVE_ID_HVM_VIRIDIAN       -11
 
 /*
 ** We process save/restore/migrate in batches of pages; the below
index 6d3b899d2e1a7bb586c83cb82729f98d8084f739..6a7067c83552e681a95e50410b22822570f7e0af 100644 (file)
@@ -206,8 +206,11 @@ int wrmsr_viridian_regs(uint32_t idx, uint64_t val)
     struct vcpu *v = current;
     struct domain *d = v->domain;
 
-    if ( !is_viridian_domain(d) )
+    if ( !is_viridian_domain(d) ) {
+        gdprintk(XENLOG_WARNING, "%s: %d not a viridian domain\n", __func__,
+                 d->domain_id);
         return 0;
+    }
 
     switch ( idx )
     {
@@ -271,8 +274,11 @@ int rdmsr_viridian_regs(uint32_t idx, uint64_t *val)
     struct vcpu *v = current;
     struct domain *d = v->domain;
     
-    if ( !is_viridian_domain(d) )
+    if ( !is_viridian_domain(d) ) {
+        gdprintk(XENLOG_WARNING, "%s: %d not a viridian domain\n", __func__,
+                 d->domain_id);
         return 0;
+    }
 
     switch ( idx )
     {
@@ -411,6 +417,8 @@ static int viridian_load_domain_ctxt(struct domain *d, hvm_domain_context_t *h)
     if ( hvm_load_entry(VIRIDIAN_DOMAIN, h, &ctxt) != 0 )
         return -EINVAL;
 
+    ASSERT(is_viridian_domain(d));
+
     d->arch.hvm_domain.viridian.hypercall_gpa.raw = ctxt.hypercall_gpa;
     d->arch.hvm_domain.viridian.guest_os_id.raw   = ctxt.guest_os_id;
 
@@ -455,6 +463,8 @@ static int viridian_load_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
     if ( hvm_load_entry(VIRIDIAN_VCPU, h, &ctxt) != 0 )
         return -EINVAL;
 
+    ASSERT(is_viridian_domain(d));
+
     v->arch.hvm_vcpu.viridian.apic_assist.raw = ctxt.apic_assist;
 
     return 0;