18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012,2013 - ARM Ltd 48c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Derived from arch/arm/kvm/handle_exit.c: 78c2ecf20Sopenharmony_ci * Copyright (C) 2012 - Virtual Open Systems and Columbia University 88c2ecf20Sopenharmony_ci * Author: Christoffer Dall <c.dall@virtualopensystems.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kvm.h> 128c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/esr.h> 158c2ecf20Sopenharmony_ci#include <asm/exception.h> 168c2ecf20Sopenharmony_ci#include <asm/kvm_asm.h> 178c2ecf20Sopenharmony_ci#include <asm/kvm_coproc.h> 188c2ecf20Sopenharmony_ci#include <asm/kvm_emulate.h> 198c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h> 208c2ecf20Sopenharmony_ci#include <asm/debug-monitors.h> 218c2ecf20Sopenharmony_ci#include <asm/traps.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <kvm/arm_hypercalls.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 268c2ecf20Sopenharmony_ci#include "trace_handle_exit.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_citypedef int (*exit_handle_fn)(struct kvm_vcpu *); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void kvm_handle_guest_serror(struct kvm_vcpu *vcpu, u32 esr) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci if (!arm64_is_ras_serror(esr) || arm64_is_fatal_ras_serror(NULL, esr)) 338c2ecf20Sopenharmony_ci kvm_inject_vabt(vcpu); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int handle_hvc(struct kvm_vcpu *vcpu) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci int ret; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci trace_kvm_hvc_arm64(*vcpu_pc(vcpu), vcpu_get_reg(vcpu, 0), 418c2ecf20Sopenharmony_ci kvm_vcpu_hvc_get_imm(vcpu)); 428c2ecf20Sopenharmony_ci vcpu->stat.hvc_exit_stat++; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci ret = kvm_hvc_call_handler(vcpu); 458c2ecf20Sopenharmony_ci if (ret < 0) { 468c2ecf20Sopenharmony_ci vcpu_set_reg(vcpu, 0, ~0UL); 478c2ecf20Sopenharmony_ci return 1; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return ret; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int handle_smc(struct kvm_vcpu *vcpu) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci /* 568c2ecf20Sopenharmony_ci * "If an SMC instruction executed at Non-secure EL1 is 578c2ecf20Sopenharmony_ci * trapped to EL2 because HCR_EL2.TSC is 1, the exception is a 588c2ecf20Sopenharmony_ci * Trap exception, not a Secure Monitor Call exception [...]" 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * We need to advance the PC after the trap, as it would 618c2ecf20Sopenharmony_ci * otherwise return to the same address... 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci vcpu_set_reg(vcpu, 0, ~0UL); 648c2ecf20Sopenharmony_ci kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); 658c2ecf20Sopenharmony_ci return 1; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * Guest access to FP/ASIMD registers are routed to this handler only 708c2ecf20Sopenharmony_ci * when the system doesn't support FP/ASIMD. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic int handle_no_fpsimd(struct kvm_vcpu *vcpu) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci kvm_inject_undefined(vcpu); 758c2ecf20Sopenharmony_ci return 1; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/** 798c2ecf20Sopenharmony_ci * kvm_handle_wfx - handle a wait-for-interrupts or wait-for-event 808c2ecf20Sopenharmony_ci * instruction executed by a guest 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * @vcpu: the vcpu pointer 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * WFE: Yield the CPU and come back to this vcpu when the scheduler 858c2ecf20Sopenharmony_ci * decides to. 868c2ecf20Sopenharmony_ci * WFI: Simply call kvm_vcpu_block(), which will halt execution of 878c2ecf20Sopenharmony_ci * world-switches and schedule other host processes until there is an 888c2ecf20Sopenharmony_ci * incoming IRQ or FIQ to the VM. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_cistatic int kvm_handle_wfx(struct kvm_vcpu *vcpu) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci if (kvm_vcpu_get_esr(vcpu) & ESR_ELx_WFx_ISS_WFE) { 938c2ecf20Sopenharmony_ci trace_kvm_wfx_arm64(*vcpu_pc(vcpu), true); 948c2ecf20Sopenharmony_ci vcpu->stat.wfe_exit_stat++; 958c2ecf20Sopenharmony_ci kvm_vcpu_on_spin(vcpu, vcpu_mode_priv(vcpu)); 968c2ecf20Sopenharmony_ci } else { 978c2ecf20Sopenharmony_ci trace_kvm_wfx_arm64(*vcpu_pc(vcpu), false); 988c2ecf20Sopenharmony_ci vcpu->stat.wfi_exit_stat++; 998c2ecf20Sopenharmony_ci kvm_vcpu_block(vcpu); 1008c2ecf20Sopenharmony_ci kvm_clear_request(KVM_REQ_UNHALT, vcpu); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return 1; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/** 1098c2ecf20Sopenharmony_ci * kvm_handle_guest_debug - handle a debug exception instruction 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * @vcpu: the vcpu pointer 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * We route all debug exceptions through the same handler. If both the 1148c2ecf20Sopenharmony_ci * guest and host are using the same debug facilities it will be up to 1158c2ecf20Sopenharmony_ci * userspace to re-inject the correct exception for guest delivery. 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * @return: 0 (while setting vcpu->run->exit_reason), -1 for error 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic int kvm_handle_guest_debug(struct kvm_vcpu *vcpu) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 1228c2ecf20Sopenharmony_ci u32 esr = kvm_vcpu_get_esr(vcpu); 1238c2ecf20Sopenharmony_ci int ret = 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_DEBUG; 1268c2ecf20Sopenharmony_ci run->debug.arch.hsr = esr; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci switch (ESR_ELx_EC(esr)) { 1298c2ecf20Sopenharmony_ci case ESR_ELx_EC_WATCHPT_LOW: 1308c2ecf20Sopenharmony_ci run->debug.arch.far = vcpu->arch.fault.far_el2; 1318c2ecf20Sopenharmony_ci fallthrough; 1328c2ecf20Sopenharmony_ci case ESR_ELx_EC_SOFTSTP_LOW: 1338c2ecf20Sopenharmony_ci case ESR_ELx_EC_BREAKPT_LOW: 1348c2ecf20Sopenharmony_ci case ESR_ELx_EC_BKPT32: 1358c2ecf20Sopenharmony_ci case ESR_ELx_EC_BRK64: 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci default: 1388c2ecf20Sopenharmony_ci kvm_err("%s: un-handled case esr: %#08x\n", 1398c2ecf20Sopenharmony_ci __func__, (unsigned int) esr); 1408c2ecf20Sopenharmony_ci ret = -1; 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int kvm_handle_unknown_ec(struct kvm_vcpu *vcpu) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci u32 esr = kvm_vcpu_get_esr(vcpu); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci kvm_pr_unimpl("Unknown exception class: esr: %#08x -- %s\n", 1528c2ecf20Sopenharmony_ci esr, esr_get_class_string(esr)); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci kvm_inject_undefined(vcpu); 1558c2ecf20Sopenharmony_ci return 1; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int handle_sve(struct kvm_vcpu *vcpu) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci /* Until SVE is supported for guests: */ 1618c2ecf20Sopenharmony_ci kvm_inject_undefined(vcpu); 1628c2ecf20Sopenharmony_ci return 1; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci * Guest usage of a ptrauth instruction (which the guest EL1 did not turn into 1678c2ecf20Sopenharmony_ci * a NOP). If we get here, it is that we didn't fixup ptrauth on exit, and all 1688c2ecf20Sopenharmony_ci * that we can do is give the guest an UNDEF. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic int kvm_handle_ptrauth(struct kvm_vcpu *vcpu) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci kvm_inject_undefined(vcpu); 1738c2ecf20Sopenharmony_ci return 1; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic exit_handle_fn arm_exit_handlers[] = { 1778c2ecf20Sopenharmony_ci [0 ... ESR_ELx_EC_MAX] = kvm_handle_unknown_ec, 1788c2ecf20Sopenharmony_ci [ESR_ELx_EC_WFx] = kvm_handle_wfx, 1798c2ecf20Sopenharmony_ci [ESR_ELx_EC_CP15_32] = kvm_handle_cp15_32, 1808c2ecf20Sopenharmony_ci [ESR_ELx_EC_CP15_64] = kvm_handle_cp15_64, 1818c2ecf20Sopenharmony_ci [ESR_ELx_EC_CP14_MR] = kvm_handle_cp14_32, 1828c2ecf20Sopenharmony_ci [ESR_ELx_EC_CP14_LS] = kvm_handle_cp14_load_store, 1838c2ecf20Sopenharmony_ci [ESR_ELx_EC_CP14_64] = kvm_handle_cp14_64, 1848c2ecf20Sopenharmony_ci [ESR_ELx_EC_HVC32] = handle_hvc, 1858c2ecf20Sopenharmony_ci [ESR_ELx_EC_SMC32] = handle_smc, 1868c2ecf20Sopenharmony_ci [ESR_ELx_EC_HVC64] = handle_hvc, 1878c2ecf20Sopenharmony_ci [ESR_ELx_EC_SMC64] = handle_smc, 1888c2ecf20Sopenharmony_ci [ESR_ELx_EC_SYS64] = kvm_handle_sys_reg, 1898c2ecf20Sopenharmony_ci [ESR_ELx_EC_SVE] = handle_sve, 1908c2ecf20Sopenharmony_ci [ESR_ELx_EC_IABT_LOW] = kvm_handle_guest_abort, 1918c2ecf20Sopenharmony_ci [ESR_ELx_EC_DABT_LOW] = kvm_handle_guest_abort, 1928c2ecf20Sopenharmony_ci [ESR_ELx_EC_SOFTSTP_LOW]= kvm_handle_guest_debug, 1938c2ecf20Sopenharmony_ci [ESR_ELx_EC_WATCHPT_LOW]= kvm_handle_guest_debug, 1948c2ecf20Sopenharmony_ci [ESR_ELx_EC_BREAKPT_LOW]= kvm_handle_guest_debug, 1958c2ecf20Sopenharmony_ci [ESR_ELx_EC_BKPT32] = kvm_handle_guest_debug, 1968c2ecf20Sopenharmony_ci [ESR_ELx_EC_BRK64] = kvm_handle_guest_debug, 1978c2ecf20Sopenharmony_ci [ESR_ELx_EC_FP_ASIMD] = handle_no_fpsimd, 1988c2ecf20Sopenharmony_ci [ESR_ELx_EC_PAC] = kvm_handle_ptrauth, 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci u32 esr = kvm_vcpu_get_esr(vcpu); 2048c2ecf20Sopenharmony_ci u8 esr_ec = ESR_ELx_EC(esr); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return arm_exit_handlers[esr_ec]; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * We may be single-stepping an emulated instruction. If the emulation 2118c2ecf20Sopenharmony_ci * has been completed in the kernel, we can return to userspace with a 2128c2ecf20Sopenharmony_ci * KVM_EXIT_DEBUG, otherwise userspace needs to complete its 2138c2ecf20Sopenharmony_ci * emulation first. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistatic int handle_trap_exceptions(struct kvm_vcpu *vcpu) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci int handled; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* 2208c2ecf20Sopenharmony_ci * See ARM ARM B1.14.1: "Hyp traps on instructions 2218c2ecf20Sopenharmony_ci * that fail their condition code check" 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci if (!kvm_condition_valid(vcpu)) { 2248c2ecf20Sopenharmony_ci kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); 2258c2ecf20Sopenharmony_ci handled = 1; 2268c2ecf20Sopenharmony_ci } else { 2278c2ecf20Sopenharmony_ci exit_handle_fn exit_handler; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci exit_handler = kvm_get_exit_handler(vcpu); 2308c2ecf20Sopenharmony_ci handled = exit_handler(vcpu); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return handled; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on 2388c2ecf20Sopenharmony_ci * proper exit to userspace. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ciint handle_exit(struct kvm_vcpu *vcpu, int exception_index) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct kvm_run *run = vcpu->run; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (ARM_SERROR_PENDING(exception_index)) { 2458c2ecf20Sopenharmony_ci u8 esr_ec = ESR_ELx_EC(kvm_vcpu_get_esr(vcpu)); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * HVC/SMC already have an adjusted PC, which we need 2498c2ecf20Sopenharmony_ci * to correct in order to return to after having 2508c2ecf20Sopenharmony_ci * injected the SError. 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_ci if (esr_ec == ESR_ELx_EC_HVC32 || esr_ec == ESR_ELx_EC_HVC64 || 2538c2ecf20Sopenharmony_ci esr_ec == ESR_ELx_EC_SMC32 || esr_ec == ESR_ELx_EC_SMC64) { 2548c2ecf20Sopenharmony_ci u32 adj = kvm_vcpu_trap_il_is32bit(vcpu) ? 4 : 2; 2558c2ecf20Sopenharmony_ci *vcpu_pc(vcpu) -= adj; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return 1; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci exception_index = ARM_EXCEPTION_CODE(exception_index); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci switch (exception_index) { 2648c2ecf20Sopenharmony_ci case ARM_EXCEPTION_IRQ: 2658c2ecf20Sopenharmony_ci return 1; 2668c2ecf20Sopenharmony_ci case ARM_EXCEPTION_EL1_SERROR: 2678c2ecf20Sopenharmony_ci return 1; 2688c2ecf20Sopenharmony_ci case ARM_EXCEPTION_TRAP: 2698c2ecf20Sopenharmony_ci return handle_trap_exceptions(vcpu); 2708c2ecf20Sopenharmony_ci case ARM_EXCEPTION_HYP_GONE: 2718c2ecf20Sopenharmony_ci /* 2728c2ecf20Sopenharmony_ci * EL2 has been reset to the hyp-stub. This happens when a guest 2738c2ecf20Sopenharmony_ci * is pre-empted by kvm_reboot()'s shutdown call. 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_FAIL_ENTRY; 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci case ARM_EXCEPTION_IL: 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * We attempted an illegal exception return. Guest state must 2808c2ecf20Sopenharmony_ci * have been corrupted somehow. Give up. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_FAIL_ENTRY; 2838c2ecf20Sopenharmony_ci return -EINVAL; 2848c2ecf20Sopenharmony_ci default: 2858c2ecf20Sopenharmony_ci kvm_pr_unimpl("Unsupported exception type: %d", 2868c2ecf20Sopenharmony_ci exception_index); 2878c2ecf20Sopenharmony_ci run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* For exit types that need handling before we can be preempted */ 2938c2ecf20Sopenharmony_civoid handle_exit_early(struct kvm_vcpu *vcpu, int exception_index) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci if (ARM_SERROR_PENDING(exception_index)) { 2968c2ecf20Sopenharmony_ci if (this_cpu_has_cap(ARM64_HAS_RAS_EXTN)) { 2978c2ecf20Sopenharmony_ci u64 disr = kvm_vcpu_get_disr(vcpu); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci kvm_handle_guest_serror(vcpu, disr_to_esr(disr)); 3008c2ecf20Sopenharmony_ci } else { 3018c2ecf20Sopenharmony_ci kvm_inject_vabt(vcpu); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci exception_index = ARM_EXCEPTION_CODE(exception_index); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (exception_index == ARM_EXCEPTION_EL1_SERROR) 3108c2ecf20Sopenharmony_ci kvm_handle_guest_serror(vcpu, kvm_vcpu_get_esr(vcpu)); 3118c2ecf20Sopenharmony_ci} 312