From mboxrd@z Thu Jan 1 00:00:00 1970 From: Razvan Cojocaru Subject: [PATCH V4 5/5] xen: Handle resumed instruction based on previous mem_event reply Date: Fri, 5 Sep 2014 13:01:37 +0300 Message-ID: <1409911297-3360-6-git-send-email-rcojocaru@bitdefender.com> References: <1409911297-3360-1-git-send-email-rcojocaru@bitdefender.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: Received: from mail6.bemta4.messagelabs.com ([85.158.143.247]) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1XPqL3-0001Mm-Og for xen-devel@lists.xenproject.org; Fri, 05 Sep 2014 10:01:53 +0000 Received: from smtp01.buh.bitdefender.com (smtp.bitdefender.biz [10.17.80.75]) by mx-sr.buh.bitdefender.com (Postfix) with ESMTP id 3C6167FCC3 for ; Fri, 5 Sep 2014 13:01:44 +0300 (EEST) In-Reply-To: <1409911297-3360-1-git-send-email-rcojocaru@bitdefender.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: xen-devel@lists.xenproject.org Cc: kevin.tian@intel.com, keir@xen.org, ian.campbell@citrix.com, Razvan Cojocaru , stefano.stabellini@eu.citrix.com, eddie.dong@intel.com, ian.jackson@eu.citrix.com, tim@xen.org, jbeulich@suse.com, jun.nakajima@intel.com, andrew.cooper3@citrix.com List-Id: xen-devel@lists.xenproject.org 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. Signed-off-by: Razvan Cojocaru Acked-by: Kevin Tian --- xen/arch/x86/domain.c | 3 ++ xen/arch/x86/hvm/vmx/vmx.c | 16 ++++++++ xen/arch/x86/mm/p2m.c | 84 ++++++++++++++++++++++++++++++++++++++++ xen/include/asm-x86/domain.h | 9 +++++ xen/include/asm-x86/hvm/hvm.h | 2 + xen/include/public/mem_event.h | 12 +++--- 6 files changed, 121 insertions(+), 5 deletions(-) diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index f7e0e78..7b1dfe6 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -415,6 +415,9 @@ int vcpu_initialise(struct vcpu *v) v->arch.flags = TF_kernel_mode; + /* By default, do not emulate */ + v->arch.mem_event.emulate_flags = 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 b08664e..cc317a4 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -1695,6 +1695,21 @@ static void vmx_enable_msr_exit_interception(struct domain *d) MSR_TYPE_W); } +static bool_t vmx_exited_by_nested_pagefault(void) +{ + unsigned long exit_qualification, exit_reason; + + __vmread(VM_EXIT_REASON, &exit_reason); + ASSERT(exit_reason == EXIT_REASON_EPT_VIOLATION); + + __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, @@ -1754,6 +1769,7 @@ static struct hvm_function_table __initdata vmx_function_table = { .nhvm_hap_walk_L1_p2m = nvmx_hap_walk_L1_p2m, .hypervisor_cpuid_leaf = vmx_hypervisor_cpuid_leaf, .enable_msr_exit_interception = vmx_enable_msr_exit_interception, + .exited_by_nested_pagefault = vmx_exited_by_nested_pagefault, }; const struct hvm_function_table * __init start_vmx(void) diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index d0962aa..616ebb4 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -1395,6 +1395,7 @@ bool_t p2m_mem_access_check(paddr_t gpa, 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 @@ -1447,6 +1448,36 @@ bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla, return 1; } } + else if ( v->arch.mem_event.emulate_flags == 0 && + hvm_funcs.exited_by_nested_pagefault && + !hvm_funcs.exited_by_nested_pagefault() ) /* don't send a mem_event */ + { + v->arch.mem_event.emulate_flags = MEM_EVENT_FLAG_EMULATE; + v->arch.mem_event.gpa = gpa; + v->arch.mem_event.eip = eip; + } + + /* The previous mem_event reply does not match the current state. */ + if ( v->arch.mem_event.gpa != gpa || v->arch.mem_event.eip != eip ) + { + /* Don't emulate the current instruction, send a new mem_event. */ + v->arch.mem_event.emulate_flags = 0; + + /* Make sure to mark the current state to match it again against + * the new mem_event about to be sent. */ + v->arch.mem_event.gpa = gpa; + v->arch.mem_event.eip = eip; + } + + if ( v->arch.mem_event.emulate_flags ) + { + hvm_mem_event_emulate_one((v->arch.mem_event.emulate_flags & + MEM_EVENT_FLAG_EMULATE_NOWRITE) != 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); @@ -1502,6 +1533,59 @@ void p2m_mem_access_resume(struct domain *d) 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; + bool_t violation = 1; + + v->arch.mem_event.emulate_flags = 0; + + if ( p2m_get_mem_access(d, rsp.gfn, &access) == 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: + violation = 0; + break; + } + } + + if ( violation ) + v->arch.mem_event.emulate_flags = rsp.flags; + } + /* Unpause domain */ if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED ) mem_event_vcpu_unpause(v); diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h index 83329ed..440aa81 100644 --- a/xen/include/asm-x86/domain.h +++ b/xen/include/asm-x86/domain.h @@ -458,6 +458,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; smap_check_policy_t smap_policy_change(struct vcpu *v, diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h index 3b0bde9..9ab126c 100644 --- a/xen/include/asm-x86/hvm/hvm.h +++ b/xen/include/asm-x86/hvm/hvm.h @@ -208,6 +208,8 @@ struct hvm_function_table { uint32_t *ecx, uint32_t *edx); void (*enable_msr_exit_interception)(struct domain *d); + + bool_t (*exited_by_nested_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 d3dd9c6..92c063c 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