Memory paging support for HVM guest emulation.
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 17 Dec 2009 06:27:55 +0000 (06:27 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 17 Dec 2009 06:27:55 +0000 (06:27 +0000)
A new HVMCOPY return value, HVMCOPY_gfn_paged_out is defined to indicate that
a gfn was paged out. This value and PFEC_page_paged, as appropriate, are
caught and passed up as X86EMUL_RETRY to the emulator. This will cause the
emulator to keep retrying the operation until is succeeds (once the page has
been paged in).

Signed-off-by: Patrick Colp <Patrick.Colp@citrix.com>
xen/arch/x86/hvm/emulate.c
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/intercept.c
xen/arch/x86/hvm/io.c
xen/include/asm-x86/hvm/support.h

index dc7a6b5f895368c55cbbb217907e4b1c2eb2c1de..a636c9bef65ad2a7d6d97d8dca11fcacb2f41e61 100644 (file)
@@ -56,8 +56,19 @@ static int hvmemul_do_io(
     int value_is_ptr = (p_data == NULL);
     struct vcpu *curr = current;
     ioreq_t *p = get_ioreq(curr);
+    unsigned long ram_gfn = paddr_to_pfn(ram_gpa);
+    p2m_type_t p2mt;
+    mfn_t ram_mfn;
     int rc;
 
+    /* Check for paged out page */
+    ram_mfn = gfn_to_mfn(current->domain, ram_gfn, &p2mt);
+    if ( p2m_is_paging(p2mt) )
+    {
+        p2m_mem_paging_populate(curr->domain, ram_gfn);
+        return X86EMUL_RETRY;
+    }
+
     /*
      * Weird-sized accesses have undefined behaviour: we discard writes
      * and read all-ones.
@@ -271,6 +282,8 @@ static int hvmemul_linear_to_phys(
     }
     else if ( (pfn = paging_gva_to_gfn(curr, addr, &pfec)) == INVALID_GFN )
     {
+        if ( pfec == PFEC_page_paged )
+            return X86EMUL_RETRY;
         hvm_inject_exception(TRAP_page_fault, pfec, addr);
         return X86EMUL_EXCEPTION;
     }
@@ -286,6 +299,8 @@ static int hvmemul_linear_to_phys(
         /* Is it contiguous with the preceding PFNs? If not then we're done. */
         if ( (npfn == INVALID_GFN) || (npfn != (pfn + (reverse ? -i : i))) )
         {
+            if ( pfec == PFEC_page_paged )
+                return X86EMUL_RETRY;
             done /= bytes_per_rep;
             if ( done == 0 )
             {
@@ -424,6 +439,8 @@ static int __hvmemul_read(
         if ( rc != X86EMUL_OKAY )
             return rc;
         return hvmemul_do_mmio(gpa, &reps, bytes, 0, IOREQ_READ, 0, p_data);
+    case HVMCOPY_gfn_paged_out:
+        return X86EMUL_RETRY;
     default:
         break;
     }
@@ -514,6 +531,8 @@ static int hvmemul_write(
             return rc;
         return hvmemul_do_mmio(gpa, &reps, bytes, 0,
                                IOREQ_WRITE, 0, p_data);
+    case HVMCOPY_gfn_paged_out:
+        return X86EMUL_RETRY;
     default:
         break;
     }
@@ -687,6 +706,8 @@ static int hvmemul_rep_movs(
 
     xfree(buf);
 
+    if ( rc == HVMCOPY_gfn_paged_out )
+        return X86EMUL_RETRY;
     if ( rc != HVMCOPY_okay )
     {
         gdprintk(XENLOG_WARNING, "Failed memory-to-memory REP MOVS: sgpa=%"
index c898ad69fbc6a239b3ba0184d9c504419a2f8995..1477099fe1b1f000357009371e24561729762187 100644 (file)
@@ -314,6 +314,11 @@ static int hvm_set_ioreq_page(
     mfn = mfn_x(gfn_to_mfn(d, gmfn, &p2mt));
     if ( !p2m_is_ram(p2mt) )
         return -EINVAL;
+    if ( p2m_is_paging(p2mt) )
+    {
+        p2m_mem_paging_populate(d, gmfn);
+        return -ENOENT;
+    }
     ASSERT(mfn_valid(mfn));
 
     page = mfn_to_page(mfn);
@@ -1318,6 +1323,8 @@ static void *hvm_map_entry(unsigned long va)
      * we still treat it as a kernel-mode read (i.e. no access checks). */
     pfec = PFEC_page_present;
     gfn = paging_gva_to_gfn(current, va, &pfec);
+    if ( pfec == PFEC_page_paged )
+        return NULL;
     mfn = mfn_x(gfn_to_mfn_current(gfn, &p2mt));
     if ( p2m_is_paging(p2mt) )
     {
@@ -1546,6 +1553,8 @@ void hvm_task_switch(
         &tss, prev_tr.base, sizeof(tss), PFEC_page_present);
     if ( rc == HVMCOPY_bad_gva_to_gfn )
         goto out;
+    if ( rc == HVMCOPY_gfn_paged_out )
+        goto out;
 
     eflags = regs->eflags;
     if ( taskswitch_reason == TSW_iret )
@@ -1582,11 +1591,15 @@ void hvm_task_switch(
         prev_tr.base, &tss, sizeof(tss), PFEC_page_present);
     if ( rc == HVMCOPY_bad_gva_to_gfn )
         goto out;
+    if ( rc == HVMCOPY_gfn_paged_out )
+        goto out;
 
     rc = hvm_copy_from_guest_virt(
         &tss, tr.base, sizeof(tss), PFEC_page_present);
     if ( rc == HVMCOPY_bad_gva_to_gfn )
         goto out;
+    if ( rc == HVMCOPY_gfn_paged_out )
+        goto out;
 
     if ( hvm_set_cr3(tss.cr3) )
         goto out;
@@ -1622,6 +1635,8 @@ void hvm_task_switch(
         tr.base, &tss, sizeof(tss), PFEC_page_present);
     if ( rc == HVMCOPY_bad_gva_to_gfn )
         exn_raised = 1;
+    if ( rc == HVMCOPY_gfn_paged_out )
+        goto out;
 
     if ( (tss.trace & 1) && !exn_raised )
         hvm_inject_exception(TRAP_debug, tss_sel & 0xfff8, 0);
@@ -1681,6 +1696,8 @@ static enum hvm_copy_result __hvm_copy(
             gfn = paging_gva_to_gfn(curr, addr, &pfec);
             if ( gfn == INVALID_GFN )
             {
+                if ( pfec == PFEC_page_paged )
+                    return HVMCOPY_gfn_paged_out;
                 if ( flags & HVMCOPY_fault )
                     hvm_inject_exception(TRAP_page_fault, pfec, addr);
                 return HVMCOPY_bad_gva_to_gfn;
@@ -1693,6 +1710,11 @@ static enum hvm_copy_result __hvm_copy(
 
         mfn = mfn_x(gfn_to_mfn_current(gfn, &p2mt));
 
+        if ( p2m_is_paging(p2mt) )
+        {
+            p2m_mem_paging_populate(curr->domain, gfn);
+            return HVMCOPY_gfn_paged_out;
+        }
         if ( p2m_is_grant(p2mt) )
             return HVMCOPY_unhandleable;
         if ( !p2m_is_ram(p2mt) )
index 06421fe46234460c1c137e1ae5fd63fd6d5d5242..841dec89216b1a69797bd116d284d5fb0c1b12cf 100644 (file)
@@ -72,30 +72,31 @@ static int hvm_mmio_access(struct vcpu *v,
     {
         for ( i = 0; i < p->count; i++ )
         {
-            rc = read_handler(
-                v,
-                p->addr + (sign * i * p->size),
-                p->size, &data);
+            rc = read_handler(v, p->addr + (sign * i * p->size), p->size,
+                              &data);
             if ( rc != X86EMUL_OKAY )
                 break;
-            (void)hvm_copy_to_guest_phys(
-                p->data + (sign * i * p->size),
-                &data,
-                p->size);
+            if ( hvm_copy_to_guest_phys(p->data + (sign * i * p->size), &data,
+                                        p->size) == HVMCOPY_gfn_paged_out )
+            {
+                rc = X86EMUL_RETRY;
+                break;
+            }
         }
     }
     else
     {
         for ( i = 0; i < p->count; i++ )
         {
-            (void)hvm_copy_from_guest_phys(
-                &data,
-                p->data + (sign * i * p->size),
-                p->size);
-            rc = write_handler(
-                v,
-                p->addr + (sign * i * p->size),
-                p->size, data);
+            if ( hvm_copy_from_guest_phys(&data,
+                                          p->data + (sign * i * p->size),
+                                          p->size) == HVMCOPY_gfn_paged_out )
+            {
+                rc = X86EMUL_RETRY;
+                break;
+            }
+            rc = write_handler(v, p->addr + (sign * i * p->size), p->size,
+                               data);
             if ( rc != X86EMUL_OKAY )
                 break;
         }
@@ -190,8 +191,12 @@ int hvm_io_intercept(ioreq_t *p, int type)
     int i;
     unsigned long addr, size;
 
-    if ( (type == HVM_PORTIO) && (dpci_ioport_intercept(p)) )
-        return X86EMUL_OKAY;
+    if ( type == HVM_PORTIO )
+    {
+        int rc = dpci_ioport_intercept(p);
+        if ( (rc == X86EMUL_OKAY) || (rc == X86EMUL_RETRY) )
+            return rc;
+    }
 
     for ( i = 0; i < handler->num_slot; i++ )
     {
index 8519bfad5133adbb5aef403e974921da4ec959bc..29079f296d27067f83618c8f87a3eae92f282d7b 100644 (file)
@@ -239,7 +239,7 @@ void hvm_io_assist(void)
         vcpu_end_shutdown_deferral(curr);
 }
 
-static void dpci_ioport_read(uint32_t mport, ioreq_t *p)
+static int dpci_ioport_read(uint32_t mport, ioreq_t *p)
 {
     int i, sign = p->df ? -1 : 1;
     uint32_t data = 0;
@@ -262,14 +262,19 @@ static void dpci_ioport_read(uint32_t mport, ioreq_t *p)
         }
 
         if ( p->data_is_ptr )
-            (void)hvm_copy_to_guest_phys(
-                p->data + (sign * i * p->size), &data, p->size);
+        {
+            if ( hvm_copy_to_guest_phys(p->data + (sign * i * p->size), &data,
+                                        p->size) ==  HVMCOPY_gfn_paged_out )
+                return X86EMUL_RETRY;
+        }
         else
             p->data = data;
     }
+    
+    return X86EMUL_OKAY;
 }
 
-static void dpci_ioport_write(uint32_t mport, ioreq_t *p)
+static int dpci_ioport_write(uint32_t mport, ioreq_t *p)
 {
     int i, sign = p->df ? -1 : 1;
     uint32_t data;
@@ -278,8 +283,11 @@ static void dpci_ioport_write(uint32_t mport, ioreq_t *p)
     {
         data = p->data;
         if ( p->data_is_ptr )
-            (void)hvm_copy_from_guest_phys(
-                &data, p->data + (sign * i * p->size), p->size);
+        {
+            if ( hvm_copy_from_guest_phys(&data, p->data + (sign * i * p->size),
+                                          p->size) ==  HVMCOPY_gfn_paged_out )
+                return X86EMUL_RETRY;
+        }
 
         switch ( p->size )
         {
@@ -296,6 +304,8 @@ static void dpci_ioport_write(uint32_t mport, ioreq_t *p)
             BUG();
         }
     }
+
+    return X86EMUL_OKAY;
 }
 
 int dpci_ioport_intercept(ioreq_t *p)
@@ -305,6 +315,7 @@ int dpci_ioport_intercept(ioreq_t *p)
     struct g2m_ioport *g2m_ioport;
     unsigned int mport, gport = p->addr;
     unsigned int s = 0, e = 0;
+    int rc;
 
     list_for_each_entry( g2m_ioport, &hd->g2m_ioport_list, list )
     {
@@ -314,7 +325,7 @@ int dpci_ioport_intercept(ioreq_t *p)
             goto found;
     }
 
-    return 0;
+    return X86EMUL_UNHANDLEABLE;
 
  found:
     mport = (gport - s) + g2m_ioport->mport;
@@ -323,22 +334,23 @@ int dpci_ioport_intercept(ioreq_t *p)
     {
         gdprintk(XENLOG_ERR, "Error: access to gport=0x%x denied!\n",
                  (uint32_t)p->addr);
-        return 0;
+        return X86EMUL_UNHANDLEABLE;
     }
 
     switch ( p->dir )
     {
     case IOREQ_READ:
-        dpci_ioport_read(mport, p);
+        rc = dpci_ioport_read(mport, p);
         break;
     case IOREQ_WRITE:
-        dpci_ioport_write(mport, p);
+        rc = dpci_ioport_write(mport, p);
         break;
     default:
         gdprintk(XENLOG_ERR, "Error: couldn't handle p->dir = %d", p->dir);
+        rc = X86EMUL_UNHANDLEABLE;
     }
 
-    return 1;
+    return rc;
 }
 
 /*
index a3e41655f397a4c4f89d75f31cd386e9c02358d1..e98daca9372e3846d070fb9d4cb328ad49e9c4f5 100644 (file)
@@ -72,7 +72,8 @@ enum hvm_copy_result {
     HVMCOPY_okay = 0,
     HVMCOPY_bad_gva_to_gfn,
     HVMCOPY_bad_gfn_to_mfn,
-    HVMCOPY_unhandleable
+    HVMCOPY_unhandleable,
+    HVMCOPY_gfn_paged_out,
 };
 
 /*