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
|