|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v2 for-4.7 5/5] x86/hvm: Fix invalidation for emulated invlpg instructions
On 09/05/16 19:27, Andrew Cooper wrote:
> hap_invlpg() is reachable from the instruction emulator, which means
> introspection and tests using hvm_fep can end up here. As such, crashing the
> domain is not an appropriate action to take.
>
> Fixing this involves rearranging the callgraph.
>
> paging_invlpg() is now the central entry point. It first checks for the
> non-canonical NOP case, and calls ino the paging subsystem. If a real flush
> is needed, it will call the appropriate handler for the vcpu. This allows the
> PV callsites of paging_invlpg() to be simplified.
>
> The sole user of hvm_funcs.invlpg_intercept() is altered to use
> paging_invlpg() instead, allowing the .invlpg_intercept() hook to be removed.
>
> For both VMX and SVM, the existing $VENDOR_invlpg_intercept() is split in
> half. $VENDOR_invlpg_intercept() stays as the intercept handler only (which
> just calls paging_invlpg()), and new $VENDOR_invlpg() functions do the
> ASID/VPID management. These later functions are made available in hvm_funcs
> for paging_invlpg() to use.
>
> As a result, correct ASID/VPID management occurs for the hvmemul path, even if
> it did not originate from an real hardware intercept.
>
> Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
I haven't reviewed it in detail but it looks like a good idea; and based
on the other Reviewed-bys (and with or without Jan's suggested clean-up):
Acked-by: George Dunlap <george.dunlap@xxxxxxxxxx>
> ---
> CC: Jan Beulich <JBeulich@xxxxxxxx>
> CC: Paul Durrant <paul.durrant@xxxxxxxxxx>
> CC: George Dunlap <george.dunlap@xxxxxxxxxxxxx>
> CC: Tim Deegan <tim@xxxxxxx>
> CC: Jun Nakajima <jun.nakajima@xxxxxxxxx>
> CC: Kevin Tian <kevin.tian@xxxxxxxxx>
> CC: Boris Ostrovsky <boris.ostrovsky@xxxxxxxxxx>
> CC: Suravee Suthikulpanit <suravee.suthikulpanit@xxxxxxx>
> CC: Wei Liu <wei.liu2@xxxxxxxxxx>
>
> v2:
> * Split performance improvement for AMD into previous patch
> * Add vcpu parameter to new invlpg() function, and avoid assuming 'current'
> * Modify paging_invlpg() to be void, and issue the PV TLB flush as well
> ---
> xen/arch/x86/hvm/emulate.c | 4 ++--
> xen/arch/x86/hvm/svm/svm.c | 11 +++++++----
> xen/arch/x86/hvm/vmx/vmx.c | 14 +++++++++-----
> xen/arch/x86/mm.c | 24 ++++++++++++++++++------
> xen/arch/x86/mm/hap/hap.c | 23 ++++++++++-------------
> xen/include/asm-x86/hvm/hvm.h | 2 +-
> xen/include/asm-x86/paging.h | 11 ++---------
> 7 files changed, 49 insertions(+), 40 deletions(-)
>
> diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c
> index e6316be..b9cac8e 100644
> --- a/xen/arch/x86/hvm/emulate.c
> +++ b/xen/arch/x86/hvm/emulate.c
> @@ -1623,8 +1623,8 @@ static int hvmemul_invlpg(
> rc = X86EMUL_OKAY;
> }
>
> - if ( rc == X86EMUL_OKAY && is_canonical_address(addr) )
> - hvm_funcs.invlpg_intercept(addr);
> + if ( rc == X86EMUL_OKAY )
> + paging_invlpg(current, addr);
>
> return rc;
> }
> diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
> index 081a5d8..679e615 100644
> --- a/xen/arch/x86/hvm/svm/svm.c
> +++ b/xen/arch/x86/hvm/svm/svm.c
> @@ -2222,10 +2222,13 @@ static void svm_invlpga_intercept(
>
> static void svm_invlpg_intercept(unsigned long vaddr)
> {
> - struct vcpu *curr = current;
> HVMTRACE_LONG_2D(INVLPG, 0, TRC_PAR_LONG(vaddr));
> - if ( paging_invlpg(curr, vaddr) )
> - svm_asid_g_invlpg(curr, vaddr);
> + paging_invlpg(current, vaddr);
> +}
> +
> +static void svm_invlpg(struct vcpu *v, unsigned long vaddr)
> +{
> + svm_asid_g_invlpg(v, vaddr);
> }
>
> static struct hvm_function_table __initdata svm_function_table = {
> @@ -2258,12 +2261,12 @@ static struct hvm_function_table __initdata
> svm_function_table = {
> .inject_trap = svm_inject_trap,
> .init_hypercall_page = svm_init_hypercall_page,
> .event_pending = svm_event_pending,
> + .invlpg = svm_invlpg,
> .cpuid_intercept = svm_cpuid_intercept,
> .wbinvd_intercept = svm_wbinvd_intercept,
> .fpu_dirty_intercept = svm_fpu_dirty_intercept,
> .msr_read_intercept = svm_msr_read_intercept,
> .msr_write_intercept = svm_msr_write_intercept,
> - .invlpg_intercept = svm_invlpg_intercept,
> .set_rdtsc_exiting = svm_set_rdtsc_exiting,
> .get_insn_bytes = svm_get_insn_bytes,
>
> diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
> index bc4410f..3acf1ab 100644
> --- a/xen/arch/x86/hvm/vmx/vmx.c
> +++ b/xen/arch/x86/hvm/vmx/vmx.c
> @@ -81,7 +81,7 @@ static void vmx_wbinvd_intercept(void);
> static void vmx_fpu_dirty_intercept(void);
> static int vmx_msr_read_intercept(unsigned int msr, uint64_t *msr_content);
> static int vmx_msr_write_intercept(unsigned int msr, uint64_t msr_content);
> -static void vmx_invlpg_intercept(unsigned long vaddr);
> +static void vmx_invlpg(struct vcpu *v, unsigned long vaddr);
> static int vmx_vmfunc_intercept(struct cpu_user_regs *regs);
>
> struct vmx_pi_blocking_vcpu {
> @@ -2137,6 +2137,7 @@ static struct hvm_function_table __initdata
> vmx_function_table = {
> .inject_trap = vmx_inject_trap,
> .init_hypercall_page = vmx_init_hypercall_page,
> .event_pending = vmx_event_pending,
> + .invlpg = vmx_invlpg,
> .cpu_up = vmx_cpu_up,
> .cpu_down = vmx_cpu_down,
> .cpuid_intercept = vmx_cpuid_intercept,
> @@ -2144,7 +2145,6 @@ static struct hvm_function_table __initdata
> vmx_function_table = {
> .fpu_dirty_intercept = vmx_fpu_dirty_intercept,
> .msr_read_intercept = vmx_msr_read_intercept,
> .msr_write_intercept = vmx_msr_write_intercept,
> - .invlpg_intercept = vmx_invlpg_intercept,
> .vmfunc_intercept = vmx_vmfunc_intercept,
> .handle_cd = vmx_handle_cd,
> .set_info_guest = vmx_set_info_guest,
> @@ -2432,10 +2432,14 @@ static void vmx_dr_access(unsigned long
> exit_qualification,
>
> static void vmx_invlpg_intercept(unsigned long vaddr)
> {
> - struct vcpu *curr = current;
> HVMTRACE_LONG_2D(INVLPG, /*invlpga=*/ 0, TRC_PAR_LONG(vaddr));
> - if ( paging_invlpg(curr, vaddr) && cpu_has_vmx_vpid )
> - vpid_sync_vcpu_gva(curr, vaddr);
> + paging_invlpg(current, vaddr);
> +}
> +
> +static void vmx_invlpg(struct vcpu *v, unsigned long vaddr)
> +{
> + if ( cpu_has_vmx_vpid )
> + vpid_sync_vcpu_gva(v, vaddr);
> }
>
> static int vmx_vmfunc_intercept(struct cpu_user_regs *regs)
> diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
> index 2bb920b..03a4d5b 100644
> --- a/xen/arch/x86/mm.c
> +++ b/xen/arch/x86/mm.c
> @@ -3386,10 +3386,8 @@ long do_mmuext_op(
> case MMUEXT_INVLPG_LOCAL:
> if ( unlikely(d != pg_owner) )
> rc = -EPERM;
> - else if ( !paging_mode_enabled(d)
> - ? __addr_ok(op.arg1.linear_addr)
> - : paging_invlpg(curr, op.arg1.linear_addr) )
> - flush_tlb_one_local(op.arg1.linear_addr);
> + else
> + paging_invlpg(curr, op.arg1.linear_addr);
> break;
>
> case MMUEXT_TLB_FLUSH_MULTI:
> @@ -4510,8 +4508,7 @@ static int __do_update_va_mapping(
> switch ( (bmap_ptr = flags & ~UVMF_FLUSHTYPE_MASK) )
> {
> case UVMF_LOCAL:
> - if ( !paging_mode_enabled(d) || paging_invlpg(v, va) )
> - flush_tlb_one_local(va);
> + paging_invlpg(v, va);
> break;
> case UVMF_ALL:
> flush_tlb_one_mask(d->domain_dirty_cpumask, va);
> @@ -6478,6 +6475,21 @@ const unsigned long *__init
> get_platform_badpages(unsigned int *array_size)
> return bad_pages;
> }
>
> +void paging_invlpg(struct vcpu *v, unsigned long va)
> +{
> + if ( !is_canonical_address(va) )
> + return;
> +
> + if ( paging_mode_enabled(v->domain) &&
> + !paging_get_hostmode(v)->invlpg(v, va) )
> + return;
> +
> + if ( is_pv_vcpu(v) )
> + flush_tlb_one_local(va);
> + else
> + hvm_funcs.invlpg(v, va);
> +}
> +
> /*
> * Local variables:
> * mode: C
> diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c
> index 4ab99bb..9c2cd49 100644
> --- a/xen/arch/x86/mm/hap/hap.c
> +++ b/xen/arch/x86/mm/hap/hap.c
> @@ -680,23 +680,20 @@ static int hap_page_fault(struct vcpu *v, unsigned long
> va,
>
> /*
> * HAP guests can handle invlpg without needing any action from Xen, so
> - * should not be intercepting it.
> + * should not be intercepting it. However, we need to correctly handle
> + * getting here from instruction emulation.
> */
> static bool_t hap_invlpg(struct vcpu *v, unsigned long va)
> {
> - if (nestedhvm_enabled(v->domain)) {
> - /* Emulate INVLPGA:
> - * Must perform the flush right now or an other vcpu may
> - * use it when we use the next VMRUN emulation, otherwise.
> - */
> - if ( vcpu_nestedhvm(v).nv_p2m )
> - p2m_flush(v, vcpu_nestedhvm(v).nv_p2m);
> - return 1;
> - }
> + /*
> + * Emulate INVLPGA:
> + * Must perform the flush right now or an other vcpu may
> + * use it when we use the next VMRUN emulation, otherwise.
> + */
> + if ( nestedhvm_enabled(v->domain) && vcpu_nestedhvm(v).nv_p2m )
> + p2m_flush(v, vcpu_nestedhvm(v).nv_p2m);
>
> - HAP_ERROR("Intercepted a guest INVLPG (%pv) with HAP enabled\n", v);
> - domain_crash(v->domain);
> - return 0;
> + return 1;
> }
>
> static void hap_update_cr3(struct vcpu *v, int do_locking)
> diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
> index add6ee8..ddbcbe8 100644
> --- a/xen/include/asm-x86/hvm/hvm.h
> +++ b/xen/include/asm-x86/hvm/hvm.h
> @@ -154,6 +154,7 @@ struct hvm_function_table {
> void (*init_hypercall_page)(struct domain *d, void *hypercall_page);
>
> int (*event_pending)(struct vcpu *v);
> + void (*invlpg)(struct vcpu *v, unsigned long vaddr);
>
> int (*cpu_up_prepare)(unsigned int cpu);
> void (*cpu_dead)(unsigned int cpu);
> @@ -172,7 +173,6 @@ struct hvm_function_table {
> void (*fpu_dirty_intercept)(void);
> int (*msr_read_intercept)(unsigned int msr, uint64_t *msr_content);
> int (*msr_write_intercept)(unsigned int msr, uint64_t msr_content);
> - void (*invlpg_intercept)(unsigned long vaddr);
> int (*vmfunc_intercept)(struct cpu_user_regs *regs);
> void (*handle_cd)(struct vcpu *v, unsigned long value);
> void (*set_info_guest)(struct vcpu *v);
> diff --git a/xen/include/asm-x86/paging.h b/xen/include/asm-x86/paging.h
> index ab131cc..a1401ab 100644
> --- a/xen/include/asm-x86/paging.h
> +++ b/xen/include/asm-x86/paging.h
> @@ -237,15 +237,8 @@ paging_fault(unsigned long va, struct cpu_user_regs
> *regs)
> return paging_get_hostmode(v)->page_fault(v, va, regs);
> }
>
> -/* Handle invlpg requests on vcpus.
> - * Returns 1 if the invlpg instruction should be issued on the hardware,
> - * or 0 if it's safe not to do so. */
> -static inline bool_t paging_invlpg(struct vcpu *v, unsigned long va)
> -{
> - return (paging_mode_external(v->domain) ? is_canonical_address(va)
> - : __addr_ok(va)) &&
> - paging_get_hostmode(v)->invlpg(v, va);
> -}
> +/* Handle invlpg requests on vcpus. */
> +void paging_invlpg(struct vcpu *v, unsigned long va);
>
> /* Translate a guest virtual address to the frame number that the
> * *guest* pagetables would map it to. Returns INVALID_GFN if the guest
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |