/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2015-2018 - ARM Ltd * Author: Marc Zyngier */ #include #include #include #include #include #include #include #include .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 el1_sync: // Guest trapped into EL2 mrs x0, esr_el2 ubfx x0, x0, #ESR_ELx_EC_SHIFT, #ESR_ELx_EC_WIDTH cmp x0, #ESR_ELx_EC_HVC64 ccmp x0, #ESR_ELx_EC_HVC32, #4, ne b.ne el1_trap /* * Fastest possible path for ARM_SMCCC_ARCH_WORKAROUND_1. * The workaround has already been applied on the host, * so let's quickly get back to the guest. We don't bother * restoring x1, as it can be clobbered anyway. */ ldr x1, [sp] // Guest's x0 eor w1, w1, #ARM_SMCCC_ARCH_WORKAROUND_1 cbz w1, wa_epilogue /* ARM_SMCCC_ARCH_WORKAROUND_2 handling */ eor w1, w1, #(ARM_SMCCC_ARCH_WORKAROUND_1 ^ \ ARM_SMCCC_ARCH_WORKAROUND_2) cbz w1, wa_epilogue eor w1, w1, #(ARM_SMCCC_ARCH_WORKAROUND_2 ^ \ ARM_SMCCC_ARCH_WORKAROUND_3) cbnz w1, el1_trap wa_epilogue: mov x0, xzr add sp, sp, #16 eret sb el1_trap: get_vcpu_ptr x1, x0 mov x0, #ARM_EXCEPTION_TRAP b __guest_exit el1_irq: get_vcpu_ptr x1, x0 mov x0, #ARM_EXCEPTION_IRQ b __guest_exit el1_error: get_vcpu_ptr x1, x0 mov x0, #ARM_EXCEPTION_EL1_SERROR b __guest_exit el2_sync: /* Check for illegal exception return */ mrs x0, spsr_el2 tbnz x0, #20, 1f 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 1: /* Let's attempt a recovery from the illegal exception return */ get_vcpu_ptr x1, x0 mov x0, #ARM_EXCEPTION_IL b __guest_exit el2_error: 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 sb .macro invalid_vector label, target = __guest_exit_panic .align 2 SYM_CODE_START(\label) b \target SYM_CODE_END(\label) .endm /* None of these should ever happen */ invalid_vector el2t_sync_invalid invalid_vector el2t_irq_invalid invalid_vector el2t_fiq_invalid invalid_vector el2t_error_invalid invalid_vector el2h_irq_invalid invalid_vector el2h_fiq_invalid invalid_vector el1_fiq_invalid .ltorg .align 11 .macro check_preamble_length start, end /* kvm_patch_vector_branch() generates code that jumps over the preamble. */ .if ((\end-\start) != KVM_VECTOR_PREAMBLE) .error "KVM vector preamble length mismatch" .endif .endm .macro valid_vect target .align 7 661: esb stp x0, x1, [sp, #-16]! 662: b \target check_preamble_length 661b, 662b .endm .macro invalid_vect target .align 7 661: nop stp x0, x1, [sp, #-16]! 662: b \target check_preamble_length 661b, 662b .endm SYM_CODE_START(__kvm_hyp_vector) invalid_vect el2t_sync_invalid // Synchronous EL2t invalid_vect el2t_irq_invalid // IRQ EL2t invalid_vect el2t_fiq_invalid // FIQ EL2t invalid_vect el2t_error_invalid // Error EL2t valid_vect el2_sync // Synchronous EL2h invalid_vect el2h_irq_invalid // IRQ EL2h invalid_vect el2h_fiq_invalid // FIQ EL2h valid_vect el2_error // Error EL2h valid_vect el1_sync // Synchronous 64-bit EL1 valid_vect el1_irq // IRQ 64-bit EL1 invalid_vect el1_fiq_invalid // FIQ 64-bit EL1 valid_vect el1_error // Error 64-bit EL1 valid_vect el1_sync // Synchronous 32-bit EL1 valid_vect el1_irq // IRQ 32-bit EL1 invalid_vect el1_fiq_invalid // FIQ 32-bit EL1 valid_vect el1_error // Error 32-bit EL1 SYM_CODE_END(__kvm_hyp_vector) .macro hyp_ventry .align 7 1: esb .rept 26 nop .endr /* * The default sequence is to directly branch to the KVM vectors, * using the computed offset. This applies for VHE as well as * !ARM64_HARDEN_EL2_VECTORS. The first vector must always run the preamble. * * For ARM64_HARDEN_EL2_VECTORS configurations, this gets replaced * with: * * stp x0, x1, [sp, #-16]! * movz x0, #(addr & 0xffff) * movk x0, #((addr >> 16) & 0xffff), lsl #16 * movk x0, #((addr >> 32) & 0xffff), lsl #32 * br x0 * * Where: * addr = kern_hyp_va(__kvm_hyp_vector) + vector-offset + KVM_VECTOR_PREAMBLE. * See kvm_patch_vector_branch for details. */ alternative_cb kvm_patch_vector_branch stp x0, x1, [sp, #-16]! b __kvm_hyp_vector + (1b - 0b + KVM_VECTOR_PREAMBLE) nop nop nop alternative_cb_end .endm .macro generate_vectors 0: .rept 16 hyp_ventry .endr .org 0b + SZ_2K // Safety measure .endm .align 11 SYM_CODE_START(__bp_harden_hyp_vecs) .rept BP_HARDEN_EL2_SLOTS generate_vectors .endr 1: .org __bp_harden_hyp_vecs + __BP_HARDEN_HYP_VECS_SZ .org 1b SYM_CODE_END(__bp_harden_hyp_vecs)