From mboxrd@z Thu Jan 1 00:00:00 1970 From: Razvan Cojocaru Subject: [PATCH RFC V10 5/5] xen: Handle resumed instruction based on previous mem_event reply Date: Thu, 28 Aug 2014 16:10:21 +0300 Message-ID: <1409231421-18444-5-git-send-email-rcojocaru@bitdefender.com> References: <1409231421-18444-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 1XMzTT-0002V8-Ex for xen-devel@lists.xenproject.org; Thu, 28 Aug 2014 13:10:47 +0000 In-Reply-To: <1409231421-18444-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, ian.campbell@citrix.com, Razvan Cojocaru , stefano.stabellini@eu.citrix.com, jun.nakajima@intel.com, andrew.cooper3@citrix.com, eddie.dong@intel.com, tim@xen.org, JBeulich@suse.com, ian.jackson@eu.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 --- Changes since V9: - Changed the EXIT_REASON_EPT_VIOLATION check into an ASSERT(). Changes since V8: - Make sure that the exit reason is EXIT_REASON_EPT_VIOLATION before checking for EPT_GLA_FAULT. Changes since V7: - Now calling hvm_mem_event_emulate_one() instead of hvm_emulate_one_full() (renamed in the first patch). Changes since V5: - Fixed indentation. - Re-ordered a set of if() conditions. Changes since V4: - Renamed "exited_by_pagefault" to "exited_by_nested_pagefault". - Folded two long lines. - Combined two "if" statements. - Moved "violation = 0;" to the XENMEM_access_rwx case. Changes since V3: - Collapsed verbose lines into a single "else if()". - Changed an int to bool_t. - Fixed a minor coding style issue. - Now computing the first parameter to hvm_emulate_one_full() (replaced an "if" with a single call). - Added code comments about eip and gla reset (clarity issue). - Removed duplicate code by Andrew Cooper (introduced in V2, since committed). 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." 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. --- xen/arch/x86/domain.c | 3 ++ xen/arch/x86/hvm/vmx/vmx.c | 19 +++++++++ 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, 124 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 493f4e0..c84ea16 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -1694,6 +1694,24 @@ 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; + +#ifndef NDEBUG + unsigned long exit_reason; + __vmread(VM_EXIT_REASON, &exit_reason); + ASSERT(exit_reason == EXIT_REASON_EPT_VIOLATION); +#endif + + __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, @@ -1753,6 +1771,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 41a316a..3ef3c81 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -1391,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 @@ -1443,6 +1444,36 @@ bool_t p2m_mem_access_check(paddr_t gpa, bool_t gla_valid, 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); @@ -1495,6 +1526,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 069ba39..8f0292c 100644 --- a/xen/include/asm-x86/hvm/hvm.h +++ b/xen/include/asm-x86/hvm/hvm.h @@ -207,6 +207,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 bbd1636..49f1b84 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