diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 02ac324c3611..f9fc163a143b 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -324,6 +324,8 @@ extern u64 __kvm_get_mdcr_el2(void); __kvm_at_err; \ } ) +void vcpu_illegal_trap(struct kvm_vcpu *vcpu, u64 *exit_code); + void __noreturn hyp_panic(void); asmlinkage void kvm_unexpected_el2_exception(void); asmlinkage void __noreturn hyp_panic(void); diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h index 0e64fec398c4..956023650df2 100644 --- a/arch/arm64/include/asm/kvm_hypevents.h +++ b/arch/arm64/include/asm/kvm_hypevents.h @@ -105,6 +105,17 @@ HYP_EVENT(psci_mem_protect, HE_PRINTK("count=%llu was=%llu", __entry->count, __entry->was) ); +HYP_EVENT(vcpu_illegal_trap, + HE_PROTO(u64 esr), + HE_STRUCT( + he_field(u64, esr) + ), + HE_ASSIGN( + __entry->esr = esr; + ), + HE_PRINTK("esr_el2=%llx", __entry->esr) +); + #ifdef CONFIG_PROTECTED_NVHE_TESTING HYP_EVENT(selftest, HE_PROTO(void), diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index 08c0ff823a55..689e7f23963e 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -853,7 +853,7 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->exit_code = 0; } -static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu, u32 exit_reason) +static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code) { struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu; hyp_entry_exit_handler_fn ec_handler; @@ -871,7 +871,7 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu, u32 exit_reason) sync_hyp_vgic_state(hyp_vcpu); sync_hyp_timer_state(hyp_vcpu); - switch (ARM_EXCEPTION_CODE(exit_reason)) { + switch (ARM_EXCEPTION_CODE(*exit_code)) { case ARM_EXCEPTION_IRQ: case ARM_EXCEPTION_HYP_REQ: break; @@ -883,8 +883,16 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu, u32 exit_reason) else ec_handler = exit_hyp_vm_handlers[esr_ec]; - if (ec_handler) + if (ec_handler) { ec_handler(hyp_vcpu); + } else { + /* + * If we have no handler we should not be punting this + * trap to Host, as it will have no sync'ed context to + * handle (for example: ESR_EL2). + */ + vcpu_illegal_trap(&hyp_vcpu->vcpu, exit_code); + } break; case ARM_EXCEPTION_EL1_SERROR: case ARM_EXCEPTION_IL: @@ -898,7 +906,7 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu, u32 exit_reason) else host_vcpu->arch.iflags = hyp_vcpu->vcpu.arch.iflags; - hyp_vcpu->exit_code = exit_reason; + hyp_vcpu->exit_code = *exit_code; } static void fpsimd_host_restore(void) @@ -1041,7 +1049,7 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt) { struct pkvm_hyp_vcpu *hyp_vcpu; struct kvm_vcpu *host_vcpu; - int ret = ARM_EXCEPTION_IL; + u64 ret = ARM_EXCEPTION_IL; host_vcpu = get_host_hyp_vcpus(host_ctxt, 1, &hyp_vcpu); @@ -1068,7 +1076,7 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt) ret = __kvm_vcpu_run(&hyp_vcpu->vcpu); - sync_hyp_vcpu(hyp_vcpu, ret); + sync_hyp_vcpu(hyp_vcpu, &ret); /* Trap host fpsimd/sve if the guest has used fpsimd/sve. */ if (guest_owns_fp_regs()) diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index 94fe9365dee2..197fef06f88c 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -345,6 +345,21 @@ static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu) return hyp_exit_handlers; } +/* + * As we have caught the guest red-handed, decide that it isn't fit for + * purpose anymore by making the vcpu invalid. The VMM can try and fix it by + * re-initializing the vcpu with KVM_ARM_VCPU_INIT, however, this is likely + * not possible for protected VMs. + */ +void vcpu_illegal_trap(struct kvm_vcpu *vcpu, u64 *exit_code) +{ + trace_vcpu_illegal_trap(kvm_vcpu_get_esr(vcpu)); + + vcpu_clear_flag(vcpu, VCPU_INITIALIZED); + *exit_code &= BIT(ARM_EXIT_WITH_SERROR_BIT); + *exit_code |= ARM_EXCEPTION_IL; +} + /* * Some guests (e.g., protected VMs) are not be allowed to run in AArch32. * The ARMv8 architecture does not give the hypervisor a mechanism to prevent a @@ -352,24 +367,11 @@ static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu) * hypervisor spots a guest in such a state ensure it is handled, and don't * trust the host to spot or fix it. The check below is based on the one in * kvm_arch_vcpu_ioctl_run(). - * - * Returns false if the guest ran in AArch32 when it shouldn't have, and - * thus should exit to the host, or true if a the guest run loop can continue. */ static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code) { - if (unlikely(vcpu_is_protected(vcpu) && vcpu_mode_is_32bit(vcpu))) { - /* - * As we have caught the guest red-handed, decide that it isn't - * fit for purpose anymore by making the vcpu invalid. The VMM - * can try and fix it by re-initializing the vcpu with - * KVM_ARM_VCPU_INIT, however, this is likely not possible for - * protected VMs. - */ - vcpu_clear_flag(vcpu, VCPU_INITIALIZED); - *exit_code &= BIT(ARM_EXIT_WITH_SERROR_BIT); - *exit_code |= ARM_EXCEPTION_IL; - } + if (unlikely(vcpu_is_protected(vcpu) && vcpu_mode_is_32bit(vcpu))) + vcpu_illegal_trap(vcpu, exit_code); } /* Switch to the guest for legacy non-VHE systems */