|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH RFC V3 5/5] xen: Handle resumed instruction based on previous mem_event reply
In a scenario where a page fault that triggered a mem_event occured,
p2m_mem_access_check() will now be able to either 1) emulate the
current instruction, or 2) emulate it, but don't allow it to perform
any writes.
Changes since V1:
- Removed the 'skip' code which required computing the current
instruction length.
- Removed the set_ad_bits() code that attempted to modify the
'accessed' and 'dirty' bits for instructions that the emulator
can't handle at the moment.
Changes since V2:
- Moved the __vmread(EXIT_QUALIFICATION, &exit_qualification); code
in vmx.c, accessible via hvm_funcs.
- Incorporated changes by Andrew Cooper ("[PATCH 1/2] Xen/mem_event:
Validate the response vcpu_id before acting on it."
Signed-off-by: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx>
---
xen/arch/x86/domain.c | 5 ++
xen/arch/x86/hvm/vmx/vmx.c | 13 +++++
xen/arch/x86/mm/mem_sharing.c | 11 +++-
xen/arch/x86/mm/p2m.c | 108 +++++++++++++++++++++++++++++++++++++++-
xen/include/asm-x86/domain.h | 9 ++++
xen/include/asm-x86/hvm/hvm.h | 2 +
xen/include/public/mem_event.h | 12 +++--
7 files changed, 152 insertions(+), 8 deletions(-)
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index e896210..5cd283b 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -407,6 +407,11 @@ int vcpu_initialise(struct vcpu *v)
v->arch.flags = TF_kernel_mode;
+ /* By default, do not emulate */
+ v->arch.mem_event.emulate_flags = 0;
+ v->arch.mem_event.gpa = 0;
+ v->arch.mem_event.eip = 0;
+
rc = mapcache_vcpu_init(v);
if ( rc )
return rc;
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 5ea3188..25d5663 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -1682,6 +1682,18 @@ void vmx_hypervisor_cpuid_leaf(uint32_t sub_idx,
*eax |= XEN_HVM_CPUID_X2APIC_VIRT;
}
+static bool_t vmx_exited_by_pagefault(void)
+{
+ unsigned long exit_qualification;
+
+ __vmread(EXIT_QUALIFICATION, &exit_qualification);
+
+ if ( (exit_qualification & EPT_GLA_FAULT) == 0 )
+ return 0;
+
+ return 1;
+}
+
static struct hvm_function_table __initdata vmx_function_table = {
.name = "VMX",
.cpu_up_prepare = vmx_cpu_up_prepare,
@@ -1740,6 +1752,7 @@ static struct hvm_function_table __initdata
vmx_function_table = {
.handle_eoi = vmx_handle_eoi,
.nhvm_hap_walk_L1_p2m = nvmx_hap_walk_L1_p2m,
.hypervisor_cpuid_leaf = vmx_hypervisor_cpuid_leaf,
+ .exited_by_pagefault = vmx_exited_by_pagefault,
};
const struct hvm_function_table * __init start_vmx(void)
diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c
index 7293f31..ec99266 100644
--- a/xen/arch/x86/mm/mem_sharing.c
+++ b/xen/arch/x86/mm/mem_sharing.c
@@ -596,11 +596,20 @@ int mem_sharing_sharing_resume(struct domain *d)
/* Get all requests off the ring */
while ( mem_event_get_response(d, &d->mem_event->share, &rsp) )
{
+ struct vcpu *v;
+
if ( rsp.flags & MEM_EVENT_FLAG_DUMMY )
continue;
+
+ /* Validate the vcpu_id in the response. */
+ if ( (rsp.vcpu_id >= d->max_vcpus) || !d->vcpu[rsp.vcpu_id] )
+ continue;
+
+ v = d->vcpu[rsp.vcpu_id];
+
/* Unpause domain/vcpu */
if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
- vcpu_unpause(d->vcpu[rsp.vcpu_id]);
+ vcpu_unpause(v);
}
return 0;
diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
index 6b52ab8..c51d27a 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -1290,8 +1290,17 @@ void p2m_mem_paging_resume(struct domain *d)
/* Pull all responses off the ring */
while( mem_event_get_response(d, &d->mem_event->paging, &rsp) )
{
+ struct vcpu *v;
+
if ( rsp.flags & MEM_EVENT_FLAG_DUMMY )
continue;
+
+ /* Validate the vcpu_id in the response. */
+ if ( (rsp.vcpu_id >= d->max_vcpus) || !d->vcpu[rsp.vcpu_id] )
+ continue;
+
+ v = d->vcpu[rsp.vcpu_id];
+
/* Fix p2m entry if the page was not dropped */
if ( !(rsp.flags & MEM_EVENT_FLAG_DROP_PAGE) )
{
@@ -1310,7 +1319,7 @@ void p2m_mem_paging_resume(struct domain *d)
}
/* Unpause domain */
if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
- vcpu_unpause(d->vcpu[rsp.vcpu_id]);
+ vcpu_unpause(v);
}
}
@@ -1382,6 +1391,7 @@ bool_t p2m_mem_access_check(paddr_t gpa, bool_t
gla_valid, unsigned long gla,
p2m_access_t p2ma;
mem_event_request_t *req;
int rc;
+ unsigned long eip = guest_cpu_user_regs()->eip;
/* First, handle rx2rw conversion automatically.
* These calls to p2m->set_entry() must succeed: we have the gfn
@@ -1434,6 +1444,37 @@ bool_t p2m_mem_access_check(paddr_t gpa, bool_t
gla_valid, unsigned long gla,
return 1;
}
}
+ else
+ {
+ /* There's a mem_event listener */
+ if ( hvm_funcs.exited_by_pagefault && !hvm_funcs.exited_by_pagefault()
) /* don't send a mem_event */
+ {
+ if ( v->arch.mem_event.emulate_flags == 0 )
+ {
+ v->arch.mem_event.emulate_flags = MEM_EVENT_FLAG_EMULATE;
+ v->arch.mem_event.gpa = gpa;
+ v->arch.mem_event.eip = eip;
+ }
+ }
+ }
+
+ if ( v->arch.mem_event.gpa != gpa || v->arch.mem_event.eip != eip )
+ {
+ v->arch.mem_event.emulate_flags = 0;
+ v->arch.mem_event.gpa = gpa;
+ v->arch.mem_event.eip = eip;
+ }
+
+ if ( v->arch.mem_event.emulate_flags )
+ {
+ if ( v->arch.mem_event.emulate_flags & MEM_EVENT_FLAG_EMULATE_NOWRITE )
+ hvm_emulate_one_full(1, TRAP_invalid_op,
HVM_DELIVER_NO_ERROR_CODE);
+ else
+ hvm_emulate_one_full(0, TRAP_invalid_op,
HVM_DELIVER_NO_ERROR_CODE);
+
+ v->arch.mem_event.emulate_flags = 0;
+ return 1;
+ }
*req_ptr = NULL;
req = xzalloc(mem_event_request_t);
@@ -1475,11 +1516,74 @@ void p2m_mem_access_resume(struct domain *d)
/* Pull all responses off the ring */
while( mem_event_get_response(d, &d->mem_event->access, &rsp) )
{
+ struct vcpu *v;
+
if ( rsp.flags & MEM_EVENT_FLAG_DUMMY )
continue;
+
+ /* Validate the vcpu_id in the response. */
+ if ( (rsp.vcpu_id >= d->max_vcpus) || !d->vcpu[rsp.vcpu_id] )
+ continue;
+
+ v = d->vcpu[rsp.vcpu_id];
+
+ /* Mark vcpu for skipping one instruction upon rescheduling */
+ if ( rsp.flags & MEM_EVENT_FLAG_EMULATE )
+ {
+ xenmem_access_t access;
+ int violation = 1;
+
+ v->arch.mem_event.emulate_flags = 0;
+
+ if ( p2m_get_mem_access(d, rsp.gfn, &access) == 0 )
+ {
+ violation = 0;
+
+ switch (access)
+ {
+ case XENMEM_access_n:
+ case XENMEM_access_n2rwx:
+ default:
+ violation = rsp.access_r || rsp.access_w || rsp.access_x;
+ break;
+
+ case XENMEM_access_r:
+ violation = rsp.access_w || rsp.access_x;
+ break;
+
+ case XENMEM_access_w:
+ violation = rsp.access_r || rsp.access_x;
+ break;
+
+ case XENMEM_access_x:
+ violation = rsp.access_r || rsp.access_w;
+ break;
+
+ case XENMEM_access_rx:
+ case XENMEM_access_rx2rw:
+ violation = rsp.access_w;
+ break;
+
+ case XENMEM_access_wx:
+ violation = rsp.access_r;
+ break;
+
+ case XENMEM_access_rw:
+ violation = rsp.access_x;
+ break;
+
+ case XENMEM_access_rwx:
+ break;
+ }
+ }
+
+ if ( violation )
+ v->arch.mem_event.emulate_flags = rsp.flags;
+ }
+
/* Unpause domain */
if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
- vcpu_unpause(d->vcpu[rsp.vcpu_id]);
+ vcpu_unpause(v);
}
}
diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h
index abf55fb..0fa4d3d 100644
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -446,6 +446,15 @@ struct arch_vcpu
/* A secondary copy of the vcpu time info. */
XEN_GUEST_HANDLE(vcpu_time_info_t) time_info_guest;
+
+ /* Should we emulate the next matching instruction on VCPU resume
+ * after a mem_event? */
+ struct {
+ uint32_t emulate_flags;
+ unsigned long gpa;
+ unsigned long eip;
+ } mem_event;
+
} __cacheline_aligned;
/* Shorthands to improve code legibility. */
diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
index 0ebd478..fecd3ef 100644
--- a/xen/include/asm-x86/hvm/hvm.h
+++ b/xen/include/asm-x86/hvm/hvm.h
@@ -205,6 +205,8 @@ struct hvm_function_table {
void (*hypervisor_cpuid_leaf)(uint32_t sub_idx,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx);
+
+ bool_t (*exited_by_pagefault)(void);
};
extern struct hvm_function_table hvm_funcs;
diff --git a/xen/include/public/mem_event.h b/xen/include/public/mem_event.h
index fbf2f07..582b427 100644
--- a/xen/include/public/mem_event.h
+++ b/xen/include/public/mem_event.h
@@ -31,11 +31,13 @@
#include "io/ring.h"
/* Memory event flags */
-#define MEM_EVENT_FLAG_VCPU_PAUSED (1 << 0)
-#define MEM_EVENT_FLAG_DROP_PAGE (1 << 1)
-#define MEM_EVENT_FLAG_EVICT_FAIL (1 << 2)
-#define MEM_EVENT_FLAG_FOREIGN (1 << 3)
-#define MEM_EVENT_FLAG_DUMMY (1 << 4)
+#define MEM_EVENT_FLAG_VCPU_PAUSED (1 << 0)
+#define MEM_EVENT_FLAG_DROP_PAGE (1 << 1)
+#define MEM_EVENT_FLAG_EVICT_FAIL (1 << 2)
+#define MEM_EVENT_FLAG_FOREIGN (1 << 3)
+#define MEM_EVENT_FLAG_DUMMY (1 << 4)
+#define MEM_EVENT_FLAG_EMULATE (1 << 5)
+#define MEM_EVENT_FLAG_EMULATE_NOWRITE (1 << 6)
/* Reasons for the memory event request */
#define MEM_EVENT_REASON_UNKNOWN 0 /* typical reason */
--
1.7.9.5
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |