KVM: arm64: Add kvm_extable for vaxorcism code
authorJames Morse <james.morse@arm.com>
Fri, 4 Sep 2020 11:28:57 +0000 (12:28 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 12 Sep 2020 09:47:37 +0000 (11:47 +0200)
commit e9ee186bb735bfc17fa81dbc9aebf268aee5b41e upstream.

KVM has a one instruction window where it will allow an SError exception
to be consumed by the hypervisor without treating it as a hypervisor bug.
This is used to consume asynchronous external abort that were caused by
the guest.

As we are about to add another location that survives unexpected exceptions,
generalise this code to make it behave like the host's extable.

KVM's version has to be mapped to EL2 to be accessible on nVHE systems.

The SError vaxorcism code is a one instruction window, so has two entries
in the extable. Because the KVM code is copied for VHE and nVHE, we end up
with four entries, half of which correspond with code that isn't mapped.

Cc: stable@vger.kernel.org # v4.9
Signed-off-by: James Morse <james.morse@arm.com>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/arm64/include/asm/kvm_asm.h
arch/arm64/kernel/vmlinux.lds.S
arch/arm64/kvm/hyp/entry.S
arch/arm64/kvm/hyp/hyp-entry.S
arch/arm64/kvm/hyp/switch.c

index 8f5cf83b23396d91bc9e6c56ce6a688503813dbb..f6a22f07fa84acb16909baaf44e993d4240a1427 100644 (file)
@@ -106,6 +106,21 @@ extern u32 __init_stage2_translation(void);
        kern_hyp_va     \vcpu
 .endm
 
+/*
+ * KVM extable for unexpected exceptions.
+ * In the same format _asm_extable, but output to a different section so that
+ * it can be mapped to EL2. The KVM version is not sorted. The caller must
+ * ensure:
+ * x18 has the hypervisor value to allow any Shadow-Call-Stack instrumented
+ * code to write to it, and that SPSR_EL2 and ELR_EL2 are restored by the fixup.
+ */
+.macro _kvm_extable, from, to
+       .pushsection    __kvm_ex_table, "a"
+       .align          3
+       .long           (\from - .), (\to - .)
+       .popsection
+.endm
+
 #endif
 
 #endif /* __ARM_KVM_ASM_H__ */
index 6a584558b29d2317dee20c28f62e69d8e0331044..fa3ffad50a61cbf5ebb6c7d396446741ce5f0aa4 100644 (file)
@@ -23,6 +23,13 @@ ENTRY(_text)
 
 jiffies = jiffies_64;
 
+
+#define HYPERVISOR_EXTABLE                                     \
+       . = ALIGN(SZ_8);                                        \
+       VMLINUX_SYMBOL(__start___kvm_ex_table) = .;             \
+       *(__kvm_ex_table)                                       \
+       VMLINUX_SYMBOL(__stop___kvm_ex_table) = .;
+
 #define HYPERVISOR_TEXT                                        \
        /*                                              \
         * Align to 4 KB so that                        \
@@ -38,6 +45,7 @@ jiffies = jiffies_64;
        VMLINUX_SYMBOL(__hyp_idmap_text_end) = .;       \
        VMLINUX_SYMBOL(__hyp_text_start) = .;           \
        *(.hyp.text)                                    \
+       HYPERVISOR_EXTABLE                              \
        VMLINUX_SYMBOL(__hyp_text_end) = .;
 
 #define IDMAP_TEXT                                     \
index a360ac6e89e9d777fd9818c2f550c03813608b16..76545f95f7045194201f52d72a2e63f0c7ac1a6e 100644 (file)
@@ -135,18 +135,22 @@ ENTRY(__guest_exit)
        // This is our single instruction exception window. A pending
        // SError is guaranteed to occur at the earliest when we unmask
        // it, and at the latest just after the ISB.
-       .global abort_guest_exit_start
 abort_guest_exit_start:
 
        isb
 
-       .global abort_guest_exit_end
 abort_guest_exit_end:
+       msr     daifset, #4     // Mask aborts
+       ret
 
-       // If the exception took place, restore the EL1 exception
-       // context so that we can report some information.
-       // Merge the exception code with the SError pending bit.
-       tbz     x0, #ARM_EXIT_WITH_SERROR_BIT, 1f
+       _kvm_extable    abort_guest_exit_start, 9997f
+       _kvm_extable    abort_guest_exit_end, 9997f
+9997:
+       msr     daifset, #4     // Mask aborts
+       mov     x0, #(1 << ARM_EXIT_WITH_SERROR_BIT)
+
+       // restore the EL1 exception context so that we can report some
+       // information. Merge the exception code with the SError pending bit.
        msr     elr_el2, x2
        msr     esr_el2, x3
        msr     spsr_el2, x4
index bf4988f9dae8f014de832c5e70b9fa4453f0e687..ee2a1b4b4e3991f69a557dc70e0cfe1e2ed33ec6 100644 (file)
 #include <asm/kvm_asm.h>
 #include <asm/kvm_mmu.h>
 
+.macro save_caller_saved_regs_vect
+       stp     x0, x1,   [sp, #-16]!
+       stp     x2, x3,   [sp, #-16]!
+       stp     x4, x5,   [sp, #-16]!
+       stp     x6, x7,   [sp, #-16]!
+       stp     x8, x9,   [sp, #-16]!
+       stp     x10, x11, [sp, #-16]!
+       stp     x12, x13, [sp, #-16]!
+       stp     x14, x15, [sp, #-16]!
+       stp     x16, x17, [sp, #-16]!
+.endm
+
+.macro restore_caller_saved_regs_vect
+       ldp     x16, x17, [sp], #16
+       ldp     x14, x15, [sp], #16
+       ldp     x12, x13, [sp], #16
+       ldp     x10, x11, [sp], #16
+       ldp     x8, x9,   [sp], #16
+       ldp     x6, x7,   [sp], #16
+       ldp     x4, x5,   [sp], #16
+       ldp     x2, x3,   [sp], #16
+       ldp     x0, x1,   [sp], #16
+.endm
+
        .text
        .pushsection    .hyp.text, "ax"
 
@@ -178,25 +202,14 @@ el1_error:
        b       __guest_exit
 
 el2_error:
-       /*
-        * Only two possibilities:
-        * 1) Either we come from the exit path, having just unmasked
-        *    PSTATE.A: change the return code to an EL2 fault, and
-        *    carry on, as we're already in a sane state to handle it.
-        * 2) Or we come from anywhere else, and that's a bug: we panic.
-        *
-        * For (1), x0 contains the original return code and x1 doesn't
-        * contain anything meaningful at that stage. We can reuse them
-        * as temp registers.
-        * For (2), who cares?
-        */
-       mrs     x0, elr_el2
-       adr     x1, abort_guest_exit_start
-       cmp     x0, x1
-       adr     x1, abort_guest_exit_end
-       ccmp    x0, x1, #4, ne
-       b.ne    __hyp_panic
-       mov     x0, #(1 << ARM_EXIT_WITH_SERROR_BIT)
+       save_caller_saved_regs_vect
+       stp     x29, x30, [sp, #-16]!
+
+       bl      kvm_unexpected_el2_exception
+
+       ldp     x29, x30, [sp], #16
+       restore_caller_saved_regs_vect
+
        eret
 
 ENTRY(__hyp_do_panic)
index ed7e3a288b4e52f93f6c5b2e6a3ac233a2b21f98..0d3d66cc38413d6153421305bce680d5514214b3 100644 (file)
 #include <asm/kvm_asm.h>
 #include <asm/kvm_emulate.h>
 #include <asm/kvm_hyp.h>
+#include <asm/uaccess.h>
+
+extern struct exception_table_entry __start___kvm_ex_table;
+extern struct exception_table_entry __stop___kvm_ex_table;
 
 static bool __hyp_text __fpsimd_enabled_nvhe(void)
 {
@@ -454,3 +458,30 @@ void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *host_ctxt)
 
        unreachable();
 }
+
+asmlinkage void __hyp_text kvm_unexpected_el2_exception(void)
+{
+       unsigned long addr, fixup;
+       struct kvm_cpu_context *host_ctxt;
+       struct exception_table_entry *entry, *end;
+       unsigned long elr_el2 = read_sysreg(elr_el2);
+
+       entry = hyp_symbol_addr(__start___kvm_ex_table);
+       end = hyp_symbol_addr(__stop___kvm_ex_table);
+       host_ctxt = __hyp_this_cpu_ptr(kvm_host_cpu_state);
+
+       while (entry < end) {
+               addr = (unsigned long)&entry->insn + entry->insn;
+               fixup = (unsigned long)&entry->fixup + entry->fixup;
+
+               if (addr != elr_el2) {
+                       entry++;
+                       continue;
+               }
+
+               write_sysreg(fixup, elr_el2);
+               return;
+       }
+
+       hyp_panic(host_ctxt);
+}