diff options
| -rw-r--r-- | arch/x86/kernel/kvm.c | 32 |
1 files changed, 29 insertions, 3 deletions
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 6b0a5861ccb8..d8dd2cadcbf7 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -265,11 +265,39 @@ noinstr u32 kvm_read_and_reset_apf_flags(void) } EXPORT_SYMBOL_FOR_KVM(kvm_read_and_reset_apf_flags); +static __always_inline bool kvm_apf_reason_pending(void) +{ + return __this_cpu_read(async_pf_enabled) && + __this_cpu_read(apf_reason.flags); +} + noinstr bool __kvm_handle_async_pf(struct pt_regs *regs, u32 token) { - u32 flags = kvm_read_and_reset_apf_flags(); irqentry_state_t state; + u32 flags; + + if (!kvm_apf_reason_pending()) + return false; + + /* + * The guest does not enable KVM_ASYNC_PF_SEND_ALWAYS, so the host + * delivers async #PF only to user mode (or as a #PF VM-exit, which KVM + * reads itself). A #PF from kernel mode can still arrive between that + * delivery and the read of the reason flags, inside an NMI that + * interrupted it, for instance perf reading user memory for a call + * chain. Such a fault is not async, so leave the async PF flags + * untouched for the outer handler. Outside NMI context, on the other + * hand, async PF flags on a kernel-mode fault mean the host is broken. + */ + if (!user_mode(regs)) { + if (likely(in_nmi())) + return false; + state = irqentry_enter(regs); + instrumentation_begin(); + panic("Host injected async #PF in kernel mode\n"); + } + flags = kvm_read_and_reset_apf_flags(); if (!flags) return false; @@ -285,8 +313,6 @@ noinstr bool __kvm_handle_async_pf(struct pt_regs *regs, u32 token) panic("Host injected async #PF in interrupt disabled region\n"); if (flags & KVM_PV_REASON_PAGE_NOT_PRESENT) { - if (unlikely(!(user_mode(regs)))) - panic("Host injected async #PF in kernel mode\n"); /* Page is swapped out by the host. */ kvm_async_pf_task_wait_schedule(token); } else { |
