]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: Fix clobbered ELR in sync abort/SError
authorPierre-Clément Tosi <ptosi@google.com>
Mon, 10 Jun 2024 06:32:30 +0000 (07:32 +0100)
committerOliver Upton <oliver.upton@linux.dev>
Thu, 20 Jun 2024 17:40:53 +0000 (17:40 +0000)
When the hypervisor receives a SError or synchronous exception (EL2h)
while running with the __kvm_hyp_vector and if ELR_EL2 doesn't point to
an extable entry, it panics indirectly by overwriting ELR with the
address of a panic handler in order for the asm routine it returns to to
ERET into the handler.

However, this clobbers ELR_EL2 for the handler itself. As a result,
hyp_panic(), when retrieving what it believes to be the PC where the
exception happened, actually ends up reading the address of the panic
handler that called it! This results in an erroneous and confusing panic
message where the source of any synchronous exception (e.g. BUG() or
kCFI) appears to be __guest_exit_panic, making it hard to locate the
actual BRK instruction.

Therefore, store the original ELR_EL2 in the per-CPU kvm_hyp_ctxt and
point the sysreg to a routine that first restores it to its previous
value before running __guest_exit_panic.

Fixes: 7db21530479f ("KVM: arm64: Restore hyp when panicking in guest context")
Signed-off-by: Pierre-Clément Tosi <ptosi@google.com>
Acked-by: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20240610063244.2828978-2-ptosi@google.com
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kernel/asm-offsets.c
arch/arm64/kvm/hyp/entry.S
arch/arm64/kvm/hyp/include/hyp/switch.h

index 81496083c0413576bf8cf0cbc534516ef4c311fc..27de1dddb0abee1f6e9d760676a0ffdd0c9a2603 100644 (file)
@@ -128,6 +128,7 @@ int main(void)
   DEFINE(VCPU_FAULT_DISR,      offsetof(struct kvm_vcpu, arch.fault.disr_el1));
   DEFINE(VCPU_HCR_EL2,         offsetof(struct kvm_vcpu, arch.hcr_el2));
   DEFINE(CPU_USER_PT_REGS,     offsetof(struct kvm_cpu_context, regs));
+  DEFINE(CPU_ELR_EL2,          offsetof(struct kvm_cpu_context, sys_regs[ELR_EL2]));
   DEFINE(CPU_RGSR_EL1,         offsetof(struct kvm_cpu_context, sys_regs[RGSR_EL1]));
   DEFINE(CPU_GCR_EL1,          offsetof(struct kvm_cpu_context, sys_regs[GCR_EL1]));
   DEFINE(CPU_APIAKEYLO_EL1,    offsetof(struct kvm_cpu_context, sys_regs[APIAKEYLO_EL1]));
index f3aa7738b477d6a83e41db42cc161fc6f9aa6291..4433a234aa9ba242f43b943d22011b5ddacd8af7 100644 (file)
@@ -83,6 +83,14 @@ alternative_else_nop_endif
        eret
        sb
 
+SYM_INNER_LABEL(__guest_exit_restore_elr_and_panic, SYM_L_GLOBAL)
+       // x2-x29,lr: vcpu regs
+       // vcpu x0-x1 on the stack
+
+       adr_this_cpu x0, kvm_hyp_ctxt, x1
+       ldr     x0, [x0, #CPU_ELR_EL2]
+       msr     elr_el2, x0
+
 SYM_INNER_LABEL(__guest_exit_panic, SYM_L_GLOBAL)
        // x2-x29,lr: vcpu regs
        // vcpu x0-x1 on the stack
index 0c4de44534b7a8bb183cfffa1a414c9dc6814687..1f4b87a73445f24c49042d33a08de9d8c0c67bce 100644 (file)
@@ -693,7 +693,7 @@ guest:
 
 static inline void __kvm_unexpected_el2_exception(void)
 {
-       extern char __guest_exit_panic[];
+       extern char __guest_exit_restore_elr_and_panic[];
        unsigned long addr, fixup;
        struct kvm_exception_table_entry *entry, *end;
        unsigned long elr_el2 = read_sysreg(elr_el2);
@@ -715,7 +715,8 @@ static inline void __kvm_unexpected_el2_exception(void)
        }
 
        /* Trigger a panic after restoring the hyp context. */
-       write_sysreg(__guest_exit_panic, elr_el2);
+       this_cpu_ptr(&kvm_hyp_ctxt)->sys_regs[ELR_EL2] = elr_el2;
+       write_sysreg(__guest_exit_restore_elr_and_panic, elr_el2);
 }
 
 #endif /* __ARM64_KVM_HYP_SWITCH_H__ */