mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 04:33:26 +02:00
When panicking from the nVHE hyp and restoring the host context, x29 is
expected to hold a pointer to the host context. This wasn't being done
so fix it to make sure there's a valid pointer the host context being
used.
Rather than passing a boolean indicating whether or not the host context
should be restored, instead pass the pointer to the host context. NULL
is passed to indicate that no context should be restored.
Fixes: a2e102e20f
("KVM: arm64: nVHE: Handle hyp panics")
Cc: stable@vger.kernel.org
Signed-off-by: Andrew Scull <ascull@google.com>
[maz: partial rewrite to fit 5.12-rc1]
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20210219122406.1337626-1-ascull@google.com
Message-Id: <20210305185254.3730990-4-maz@kernel.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
232 lines
6.3 KiB
ArmAsm
232 lines
6.3 KiB
ArmAsm
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (C) 2020 - Google Inc
|
|
* Author: Andrew Scull <ascull@google.com>
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
|
|
#include <asm/assembler.h>
|
|
#include <asm/kvm_asm.h>
|
|
#include <asm/kvm_mmu.h>
|
|
|
|
.text
|
|
|
|
SYM_FUNC_START(__host_exit)
|
|
get_host_ctxt x0, x1
|
|
|
|
/* Store the host regs x2 and x3 */
|
|
stp x2, x3, [x0, #CPU_XREG_OFFSET(2)]
|
|
|
|
/* Retrieve the host regs x0-x1 from the stack */
|
|
ldp x2, x3, [sp], #16 // x0, x1
|
|
|
|
/* Store the host regs x0-x1 and x4-x17 */
|
|
stp x2, x3, [x0, #CPU_XREG_OFFSET(0)]
|
|
stp x4, x5, [x0, #CPU_XREG_OFFSET(4)]
|
|
stp x6, x7, [x0, #CPU_XREG_OFFSET(6)]
|
|
stp x8, x9, [x0, #CPU_XREG_OFFSET(8)]
|
|
stp x10, x11, [x0, #CPU_XREG_OFFSET(10)]
|
|
stp x12, x13, [x0, #CPU_XREG_OFFSET(12)]
|
|
stp x14, x15, [x0, #CPU_XREG_OFFSET(14)]
|
|
stp x16, x17, [x0, #CPU_XREG_OFFSET(16)]
|
|
|
|
/* Store the host regs x18-x29, lr */
|
|
save_callee_saved_regs x0
|
|
|
|
/* Save the host context pointer in x29 across the function call */
|
|
mov x29, x0
|
|
bl handle_trap
|
|
|
|
/* Restore host regs x0-x17 */
|
|
__host_enter_restore_full:
|
|
ldp x0, x1, [x29, #CPU_XREG_OFFSET(0)]
|
|
ldp x2, x3, [x29, #CPU_XREG_OFFSET(2)]
|
|
ldp x4, x5, [x29, #CPU_XREG_OFFSET(4)]
|
|
ldp x6, x7, [x29, #CPU_XREG_OFFSET(6)]
|
|
|
|
/* x0-7 are use for panic arguments */
|
|
__host_enter_for_panic:
|
|
ldp x8, x9, [x29, #CPU_XREG_OFFSET(8)]
|
|
ldp x10, x11, [x29, #CPU_XREG_OFFSET(10)]
|
|
ldp x12, x13, [x29, #CPU_XREG_OFFSET(12)]
|
|
ldp x14, x15, [x29, #CPU_XREG_OFFSET(14)]
|
|
ldp x16, x17, [x29, #CPU_XREG_OFFSET(16)]
|
|
|
|
/* Restore host regs x18-x29, lr */
|
|
restore_callee_saved_regs x29
|
|
|
|
/* Do not touch any register after this! */
|
|
__host_enter_without_restoring:
|
|
eret
|
|
sb
|
|
SYM_FUNC_END(__host_exit)
|
|
|
|
/*
|
|
* void __noreturn __host_enter(struct kvm_cpu_context *host_ctxt);
|
|
*/
|
|
SYM_FUNC_START(__host_enter)
|
|
mov x29, x0
|
|
b __host_enter_restore_full
|
|
SYM_FUNC_END(__host_enter)
|
|
|
|
/*
|
|
* void __noreturn __hyp_do_panic(struct kvm_cpu_context *host_ctxt, u64 spsr,
|
|
* u64 elr, u64 par);
|
|
*/
|
|
SYM_FUNC_START(__hyp_do_panic)
|
|
/* Prepare and exit to the host's panic funciton. */
|
|
mov lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
|
|
PSR_MODE_EL1h)
|
|
msr spsr_el2, lr
|
|
ldr lr, =panic
|
|
hyp_kimg_va lr, x6
|
|
msr elr_el2, lr
|
|
|
|
mov x29, x0
|
|
|
|
/* Load the format string into x0 and arguments into x1-7 */
|
|
ldr x0, =__hyp_panic_string
|
|
hyp_kimg_va x0, x6
|
|
|
|
/* Load the format arguments into x1-7. */
|
|
mov x6, x3
|
|
get_vcpu_ptr x7, x3
|
|
mrs x3, esr_el2
|
|
mrs x4, far_el2
|
|
mrs x5, hpfar_el2
|
|
|
|
/* Enter the host, conditionally restoring the host context. */
|
|
cbz x29, __host_enter_without_restoring
|
|
b __host_enter_for_panic
|
|
SYM_FUNC_END(__hyp_do_panic)
|
|
|
|
.macro host_el1_sync_vect
|
|
.align 7
|
|
.L__vect_start\@:
|
|
stp x0, x1, [sp, #-16]!
|
|
mrs x0, esr_el2
|
|
lsr x0, x0, #ESR_ELx_EC_SHIFT
|
|
cmp x0, #ESR_ELx_EC_HVC64
|
|
b.ne __host_exit
|
|
|
|
ldp x0, x1, [sp] // Don't fixup the stack yet
|
|
|
|
/* Check for a stub HVC call */
|
|
cmp x0, #HVC_STUB_HCALL_NR
|
|
b.hs __host_exit
|
|
|
|
add sp, sp, #16
|
|
/*
|
|
* Compute the idmap address of __kvm_handle_stub_hvc and
|
|
* jump there. Since we use kimage_voffset, do not use the
|
|
* HYP VA for __kvm_handle_stub_hvc, but the kernel VA instead
|
|
* (by loading it from the constant pool).
|
|
*
|
|
* Preserve x0-x4, which may contain stub parameters.
|
|
*/
|
|
ldr x5, =__kvm_handle_stub_hvc
|
|
hyp_pa x5, x6
|
|
br x5
|
|
.L__vect_end\@:
|
|
.if ((.L__vect_end\@ - .L__vect_start\@) > 0x80)
|
|
.error "host_el1_sync_vect larger than vector entry"
|
|
.endif
|
|
.endm
|
|
|
|
.macro invalid_host_el2_vect
|
|
.align 7
|
|
/* If a guest is loaded, panic out of it. */
|
|
stp x0, x1, [sp, #-16]!
|
|
get_loaded_vcpu x0, x1
|
|
cbnz x0, __guest_exit_panic
|
|
add sp, sp, #16
|
|
|
|
/*
|
|
* The panic may not be clean if the exception is taken before the host
|
|
* context has been saved by __host_exit or after the hyp context has
|
|
* been partially clobbered by __host_enter.
|
|
*/
|
|
b hyp_panic
|
|
.endm
|
|
|
|
.macro invalid_host_el1_vect
|
|
.align 7
|
|
mov x0, xzr /* restore_host = false */
|
|
mrs x1, spsr_el2
|
|
mrs x2, elr_el2
|
|
mrs x3, par_el1
|
|
b __hyp_do_panic
|
|
.endm
|
|
|
|
/*
|
|
* The host vector does not use an ESB instruction in order to avoid consuming
|
|
* SErrors that should only be consumed by the host. Guest entry is deferred by
|
|
* __guest_enter if there are any pending asynchronous exceptions so hyp will
|
|
* always return to the host without having consumerd host SErrors.
|
|
*
|
|
* CONFIG_KVM_INDIRECT_VECTORS is not applied to the host vectors because the
|
|
* host knows about the EL2 vectors already, and there is no point in hiding
|
|
* them.
|
|
*/
|
|
.align 11
|
|
SYM_CODE_START(__kvm_hyp_host_vector)
|
|
invalid_host_el2_vect // Synchronous EL2t
|
|
invalid_host_el2_vect // IRQ EL2t
|
|
invalid_host_el2_vect // FIQ EL2t
|
|
invalid_host_el2_vect // Error EL2t
|
|
|
|
invalid_host_el2_vect // Synchronous EL2h
|
|
invalid_host_el2_vect // IRQ EL2h
|
|
invalid_host_el2_vect // FIQ EL2h
|
|
invalid_host_el2_vect // Error EL2h
|
|
|
|
host_el1_sync_vect // Synchronous 64-bit EL1
|
|
invalid_host_el1_vect // IRQ 64-bit EL1
|
|
invalid_host_el1_vect // FIQ 64-bit EL1
|
|
invalid_host_el1_vect // Error 64-bit EL1
|
|
|
|
invalid_host_el1_vect // Synchronous 32-bit EL1
|
|
invalid_host_el1_vect // IRQ 32-bit EL1
|
|
invalid_host_el1_vect // FIQ 32-bit EL1
|
|
invalid_host_el1_vect // Error 32-bit EL1
|
|
SYM_CODE_END(__kvm_hyp_host_vector)
|
|
|
|
/*
|
|
* Forward SMC with arguments in struct kvm_cpu_context, and
|
|
* store the result into the same struct. Assumes SMCCC 1.2 or older.
|
|
*
|
|
* x0: struct kvm_cpu_context*
|
|
*/
|
|
SYM_CODE_START(__kvm_hyp_host_forward_smc)
|
|
/*
|
|
* Use x18 to keep the pointer to the host context because
|
|
* x18 is callee-saved in SMCCC but not in AAPCS64.
|
|
*/
|
|
mov x18, x0
|
|
|
|
ldp x0, x1, [x18, #CPU_XREG_OFFSET(0)]
|
|
ldp x2, x3, [x18, #CPU_XREG_OFFSET(2)]
|
|
ldp x4, x5, [x18, #CPU_XREG_OFFSET(4)]
|
|
ldp x6, x7, [x18, #CPU_XREG_OFFSET(6)]
|
|
ldp x8, x9, [x18, #CPU_XREG_OFFSET(8)]
|
|
ldp x10, x11, [x18, #CPU_XREG_OFFSET(10)]
|
|
ldp x12, x13, [x18, #CPU_XREG_OFFSET(12)]
|
|
ldp x14, x15, [x18, #CPU_XREG_OFFSET(14)]
|
|
ldp x16, x17, [x18, #CPU_XREG_OFFSET(16)]
|
|
|
|
smc #0
|
|
|
|
stp x0, x1, [x18, #CPU_XREG_OFFSET(0)]
|
|
stp x2, x3, [x18, #CPU_XREG_OFFSET(2)]
|
|
stp x4, x5, [x18, #CPU_XREG_OFFSET(4)]
|
|
stp x6, x7, [x18, #CPU_XREG_OFFSET(6)]
|
|
stp x8, x9, [x18, #CPU_XREG_OFFSET(8)]
|
|
stp x10, x11, [x18, #CPU_XREG_OFFSET(10)]
|
|
stp x12, x13, [x18, #CPU_XREG_OFFSET(12)]
|
|
stp x14, x15, [x18, #CPU_XREG_OFFSET(14)]
|
|
stp x16, x17, [x18, #CPU_XREG_OFFSET(16)]
|
|
|
|
ret
|
|
SYM_CODE_END(__kvm_hyp_host_forward_smc)
|