[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 |