[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 1 of 5] mem_access: hvm changes to page faults


  • To: xen-devel@xxxxxxxxxxxxxxxxxxx
  • From: Joe Epstein <jepstein@xxxxxxxxxxxxxxxxxxxx>
  • Date: Tue, 28 Dec 2010 23:27:17 -0800
  • Delivery-date: Tue, 28 Dec 2010 23:29:56 -0800
  • Domainkey-signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:sender:from:date:x-google-sender-auth:message-id :subject:to:content-type; b=o1g2nkWg8jAkwhJ5g/ns1XrddNrq0beV5XwxPXNx7eWNOKeqIncf9BFMqZakjofw4K vmQ0w5ZfEBtcBiDPpGQslHR9uLx4Fu3QzkX+9W0pNOiLBrkBNKWg5l9c/tPYoY5+X7vq 13XS2ehOfDk7LlGIoBTj5iY3gyrzEqWQU9VxA=
  • List-id: Xen developer discussion <xen-devel.lists.xensource.com>

Changes the nested page fault to send memory access failures to the
event handler, if there is one.  Also adds HVMOPs to set and get memory
access.

Signed-off-by: Joe Epstein <jepstein98@xxxxxxxxx>

diff -r 4e108cf56d07 xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c    Mon Dec 27 08:00:09 2010 +0000
+++ b/xen/arch/x86/hvm/hvm.c    Tue Dec 28 22:35:35 2010 -0800
@@ -1086,61 +1086,91 @@
     domain_shutdown(v->domain, SHUTDOWN_reboot);
 }

-bool_t hvm_hap_nested_page_fault(unsigned long gfn)
+bool_t hvm_hap_nested_page_fault(unsigned long gpa,
+                                 bool_t gla_valid,
+                                 unsigned long gla,
+                                 bool_t access_r,
+                                 bool_t access_w,
+                                 bool_t access_x)
 {
+    unsigned long gfn = gpa >> PAGE_SHIFT;
     p2m_type_t p2mt;
     mfn_t mfn;
     struct vcpu *v = current;
     struct p2m_domain *p2m = p2m_get_hostp2m(v->domain);
+    int guest_fault = 0;

     mfn = gfn_to_mfn_guest(p2m, gfn, &p2mt);

-    /*
-     * If this GFN is emulated MMIO or marked as read-only, pass the fault
-     * to the mmio handler.
-     */
-    if ( (p2mt == p2m_mmio_dm) || (p2mt == p2m_ram_ro) )
+#ifdef __x86_64__
+    /* Check if the page has been paged out */
+    if ( p2m_is_paged(p2mt) || (p2mt == p2m_ram_paging_out) )
     {
-        if ( !handle_mmio() )
-            hvm_inject_exception(TRAP_gp_fault, 0, 0);
+        p2m_mem_paging_populate(p2m, gfn);
         return 1;
     }

-#ifdef __x86_64__
-    /* Check if the page has been paged out */
-    if ( p2m_is_paged(p2mt) || (p2mt == p2m_ram_paging_out) )
-        p2m_mem_paging_populate(p2m, gfn);
-
     /* Mem sharing: unshare the page and try again */
-    if ( p2mt == p2m_ram_shared )
+    if ( p2mt == p2m_ram_shared && access_w )
     {
         mem_sharing_unshare_page(p2m, gfn, 0);
         return 1;
     }
 #endif
-
-    /* Spurious fault? PoD and log-dirty also take this path. */
-    if ( p2m_is_ram(p2mt) )
+
+    /*
+     * If this GFN is emulated MMIO or marked as read-only (which old
MMIO is),
+     * pass the fault to the mmio handler first.
+     */
+    if ( (p2mt == p2m_mmio_dm) || (p2mt == p2m_ram_ro) )
     {
-        /*
-         * Page log dirty is always done with order 0. If this mfn resides in
-         * a large page, we do not change other pages type within that large
-         * page.
-         */
-        paging_mark_dirty(v->domain, mfn_x(mfn));
-        p2m_change_type(p2m, gfn, p2m_ram_logdirty, p2m_ram_rw);
+        if ( !handle_mmio() )
+        {
+            guest_fault = 1;
+            goto check_access_handler;
+        }
         return 1;
     }

-    /* Shouldn't happen: Maybe the guest was writing to a r/o grant mapping? */
-    if ( p2mt == p2m_grant_map_ro )
+    /* Was it a write access: log-dirty, etc... */
+    if ( access_w ) {
+        /* PoD and log-dirty also take this path. */
+        if ( p2mt != p2m_ram_rw && p2m_is_ram(p2mt) )
+        {
+            /*
+             * Page log dirty is always done with order 0. If this
mfn resides in
+             * a large page, we do not change other pages type within
that large
+             * page.
+             */
+            paging_mark_dirty(v->domain, mfn_x(mfn));
+            p2m_change_type(p2m, gfn, p2m_ram_logdirty, p2m_ram_rw);
+            return 1;
+        }
+
+        /* Shouldn't happen: Maybe the guest was writing to a r/o
grant mapping? */
+        if ( p2mt == p2m_grant_map_ro )
+        {
+            gdprintk(XENLOG_WARNING,
+                     "trying to write to read-only grant mapping\n");
+            guest_fault = 1;
+            goto check_access_handler;
+        }
+    } /* end access_w */
+
+ check_access_handler:
+    /* Even if it is the guest's "fault", check with the mem_event
interface instead if
+     * one is there */
+    if ( p2m_mem_access_check(gpa, gla_valid, gla, access_r,
access_w, access_x) )
+        return 1;
+
+    /* If there is no handler, then fault if guest_fault = 1 */
+    if ( guest_fault )
     {
-        gdprintk(XENLOG_WARNING,
-                 "trying to write to read-only grant mapping\n");
         hvm_inject_exception(TRAP_gp_fault, 0, 0);
         return 1;
     }

+    /* Nothing handled it: it must be an access error with no memory
handler, so fail */
     return 0;
 }

@@ -3412,6 +3442,143 @@
         break;
     }

+    case HVMOP_set_mem_access:
+    {
+        struct xen_hvm_set_mem_access a;
+        struct domain *d;
+        struct p2m_domain *p2m;
+        unsigned long pfn;
+
+        p2m_access_t memaccess[] = {
+            p2m_access_n,
+            p2m_access_r,
+            p2m_access_w,
+            p2m_access_rw,
+            p2m_access_x,
+            p2m_access_rx,
+            p2m_access_wx,
+            p2m_access_rwx,
+            p2m_access_rx2rw,
+            0,  /* HVMMEM_access_default -- will get set below */
+        };
+
+        if ( copy_from_guest(&a, arg, 1) )
+            return -EFAULT;
+
+        rc = rcu_lock_target_domain_by_id(a.domid, &d);
+        if ( rc != 0 )
+            return rc;
+
+        rc = -EINVAL;
+        if ( !is_hvm_domain(d) )
+            goto param_fail5;
+
+        p2m = p2m_get_hostp2m(d);
+        memaccess[HVMMEM_access_default] = p2m->default_access;
+
+        /* If request to set default access */
+        if ( a.first_pfn == ~0ull )
+        {
+            rc = 0;
+            p2m->default_access = memaccess[a.hvmmem_access];
+            goto param_fail5;
+        }
+
+        rc = -EINVAL;
+        if ( (a.first_pfn > domain_get_maximum_gpfn(d)) ||
+             ((a.first_pfn + a.nr - 1) < a.first_pfn) ||
+             ((a.first_pfn + a.nr - 1) > domain_get_maximum_gpfn(d)) )
+            goto param_fail5;
+
+        if ( a.hvmmem_access >= ARRAY_SIZE(memaccess) )
+            goto param_fail5;
+
+        for ( pfn = a.first_pfn; pfn < a.first_pfn + a.nr; pfn++ )
+        {
+            p2m_type_t t;
+            mfn_t mfn;
+            int success;
+
+            mfn = gfn_to_mfn_unshare(p2m, pfn, &t, 0);
+
+            p2m_lock(p2m);
+            success = p2m->set_entry(p2m, pfn, mfn, 0, t,
memaccess[a.hvmmem_access]);
+            p2m_unlock(p2m);
+            if ( !success )
+                goto param_fail5;
+        }
+
+        rc = 0;
+
+    param_fail5:
+        rcu_unlock_domain(d);
+        break;
+    }
+
+    case HVMOP_get_mem_access:
+    {
+        struct xen_hvm_get_mem_access a;
+        struct domain *d;
+        struct p2m_domain *p2m;
+        p2m_type_t t;
+        p2m_access_t ac;
+        mfn_t mfn;
+
+        /* Interface access to internal p2m accesses */
+        hvmmem_access_t memaccess[] = {
+            HVMMEM_access_n,
+            HVMMEM_access_r,
+            HVMMEM_access_w,
+            HVMMEM_access_rw,
+            HVMMEM_access_x,
+            HVMMEM_access_rx,
+            HVMMEM_access_wx,
+            HVMMEM_access_rwx,
+            HVMMEM_access_rx2rw
+        };
+
+        if ( copy_from_guest(&a, arg, 1) )
+            return -EFAULT;
+
+        rc = rcu_lock_target_domain_by_id(a.domid, &d);
+        if ( rc != 0 )
+            return rc;
+
+        rc = -EINVAL;
+        if ( !is_hvm_domain(d) )
+            goto param_fail6;
+
+        p2m = p2m_get_hostp2m(d);
+
+        if ( a.pfn == ~0ull )
+        {
+            a.hvmmem_access = memaccess[p2m->default_access];
+        }
+        else {
+            rc = -EINVAL;
+            if ( (a.pfn > domain_get_maximum_gpfn(d)) )
+                goto param_fail6;
+
+            rc = -ESRCH;
+            mfn = p2m->get_entry(p2m, a.pfn, &t, &ac, p2m_query);
+
+            if ( mfn_x(mfn) == INVALID_MFN )
+                goto param_fail6;
+
+            rc = -ERANGE;
+            if ( ac >= ARRAY_SIZE(memaccess) )
+                goto param_fail6;
+
+            a.hvmmem_access = memaccess[ac];
+        }
+
+        rc = copy_to_guest(arg, &a, 1) ? -EFAULT : 0;
+
+    param_fail6:
+        rcu_unlock_domain(d);
+        break;
+    }
+
     case HVMOP_pagetable_dying:
     {
         struct xen_hvm_pagetable_dying a;
@@ -3426,12 +3593,12 @@

         rc = -EINVAL;
         if ( !is_hvm_domain(d) || !paging_mode_shadow(d) )
-            goto param_fail5;
+            goto param_fail7;

         rc = 0;
         pagetable_dying(d, a.gpa);

-    param_fail5:
+    param_fail7:
         rcu_unlock_domain(d);
         break;
     }
diff -r 4e108cf56d07 xen/arch/x86/hvm/svm/svm.c
--- a/xen/arch/x86/hvm/svm/svm.c        Mon Dec 27 08:00:09 2010 +0000
+++ b/xen/arch/x86/hvm/svm/svm.c        Tue Dec 28 22:35:35 2010 -0800
@@ -979,7 +979,7 @@
         __trace_var(TRC_HVM_NPF, 0, sizeof(_d), &_d);
     }

-    if ( hvm_hap_nested_page_fault(gfn) )
+    if ( hvm_hap_nested_page_fault(gpa, 0, ~0ull, 0, 0, 0) )
         return;

     /* Everything else is an error. */
diff -r 4e108cf56d07 xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c        Mon Dec 27 08:00:09 2010 +0000
+++ b/xen/arch/x86/hvm/vmx/vmx.c        Tue Dec 28 22:35:35 2010 -0800
@@ -2079,7 +2079,13 @@
         __trace_var(TRC_HVM_NPF, 0, sizeof(_d), &_d);
     }

-    if ( hvm_hap_nested_page_fault(gfn) )
+    if ( hvm_hap_nested_page_fault(gpa,
+                                   qualification & EPT_GLA_VALID       ? 1 : 0,
+                                   qualification & EPT_GLA_VALID
+                                     ? __vmread(GUEST_LINEAR_ADDRESS) : ~0ull,
+                                   qualification & EPT_READ_VIOLATION  ? 1 : 0,
+                                   qualification & EPT_WRITE_VIOLATION ? 1 : 0,
+                                   qualification & EPT_EXEC_VIOLATION
 ? 1 : 0) )
         return;

     /* Everything else is an error. */
diff -r 4e108cf56d07 xen/arch/ia64/vmx/vmx_hypercall.c
--- a/xen/arch/ia64/vmx/vmx_hypercall.c Mon Dec 27 08:00:09 2010 +0000
+++ b/xen/arch/ia64/vmx/vmx_hypercall.c Tue Dec 28 22:38:13 2010 -0800
@@ -218,6 +218,9 @@
     }

     case HVMOP_set_mem_type:
+    case HVMOP_set_mem_access:
+    case HVMOP_get_mem_access:
+
         rc = -ENOSYS;
         break;

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.