Commit 3290c6ff authored by James Morse's avatar James Morse Committed by Greg Kroah-Hartman

KVM: arm64: Add kvm_extable for vaxorcism code

commit e9ee186b 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.
Signed-off-by: default avatarJames Morse <james.morse@arm.com>
Reviewed-by: default avatarMarc Zyngier <maz@kernel.org>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Signed-off-by: default avatarAndre Przywara <andre.przywara@arm.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent af2cf2c5
...@@ -111,6 +111,21 @@ extern u32 __init_stage2_translation(void); ...@@ -111,6 +111,21 @@ extern u32 __init_stage2_translation(void);
kern_hyp_va \vcpu kern_hyp_va \vcpu
.endm .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
#endif /* __ARM_KVM_ASM_H__ */ #endif /* __ARM_KVM_ASM_H__ */
...@@ -24,6 +24,13 @@ ENTRY(_text) ...@@ -24,6 +24,13 @@ ENTRY(_text)
jiffies = jiffies_64; jiffies = jiffies_64;
#define HYPERVISOR_EXTABLE \
. = ALIGN(SZ_8); \
__start___kvm_ex_table = .; \
*(__kvm_ex_table) \
__stop___kvm_ex_table = .;
#define HYPERVISOR_TEXT \ #define HYPERVISOR_TEXT \
/* \ /* \
* Align to 4 KB so that \ * Align to 4 KB so that \
...@@ -39,6 +46,7 @@ jiffies = jiffies_64; ...@@ -39,6 +46,7 @@ jiffies = jiffies_64;
__hyp_idmap_text_end = .; \ __hyp_idmap_text_end = .; \
__hyp_text_start = .; \ __hyp_text_start = .; \
*(.hyp.text) \ *(.hyp.text) \
HYPERVISOR_EXTABLE \
__hyp_text_end = .; __hyp_text_end = .;
#define IDMAP_TEXT \ #define IDMAP_TEXT \
......
...@@ -148,18 +148,22 @@ alternative_endif ...@@ -148,18 +148,22 @@ alternative_endif
// This is our single instruction exception window. A pending // This is our single instruction exception window. A pending
// SError is guaranteed to occur at the earliest when we unmask // SError is guaranteed to occur at the earliest when we unmask
// it, and at the latest just after the ISB. // it, and at the latest just after the ISB.
.global abort_guest_exit_start
abort_guest_exit_start: abort_guest_exit_start:
isb isb
.global abort_guest_exit_end
abort_guest_exit_end: abort_guest_exit_end:
msr daifset, #4 // Mask aborts
ret
// If the exception took place, restore the EL1 exception _kvm_extable abort_guest_exit_start, 9997f
// context so that we can report some information. _kvm_extable abort_guest_exit_end, 9997f
// Merge the exception code with the SError pending bit. 9997:
tbz x0, #ARM_EXIT_WITH_SERROR_BIT, 1f 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 elr_el2, x2
msr esr_el2, x3 msr esr_el2, x3
msr spsr_el2, x4 msr spsr_el2, x4
......
...@@ -26,6 +26,30 @@ ...@@ -26,6 +26,30 @@
#include <asm/kvm_mmu.h> #include <asm/kvm_mmu.h>
#include <asm/mmu.h> #include <asm/mmu.h>
.macro save_caller_saved_regs_vect
/* x0 and x1 were saved in the vector entry */
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 .text
.pushsection .hyp.text, "ax" .pushsection .hyp.text, "ax"
...@@ -163,27 +187,14 @@ el1_error: ...@@ -163,27 +187,14 @@ el1_error:
b __guest_exit b __guest_exit
el2_error: el2_error:
ldp x0, x1, [sp], #16 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
/*
* 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)
eret eret
ENTRY(__hyp_do_panic) ENTRY(__hyp_do_panic)
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <kvm/arm_psci.h> #include <kvm/arm_psci.h>
#include <asm/cpufeature.h> #include <asm/cpufeature.h>
#include <asm/extable.h>
#include <asm/kprobes.h> #include <asm/kprobes.h>
#include <asm/kvm_asm.h> #include <asm/kvm_asm.h>
#include <asm/kvm_emulate.h> #include <asm/kvm_emulate.h>
...@@ -34,6 +35,9 @@ ...@@ -34,6 +35,9 @@
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/thread_info.h> #include <asm/thread_info.h>
extern struct exception_table_entry __start___kvm_ex_table;
extern struct exception_table_entry __stop___kvm_ex_table;
/* Check whether the FP regs were dirtied while in the host-side run loop: */ /* Check whether the FP regs were dirtied while in the host-side run loop: */
static bool __hyp_text update_fp_enabled(struct kvm_vcpu *vcpu) static bool __hyp_text update_fp_enabled(struct kvm_vcpu *vcpu)
{ {
...@@ -663,3 +667,30 @@ void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *host_ctxt) ...@@ -663,3 +667,30 @@ void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *host_ctxt)
unreachable(); 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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment