162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Fault injection for both 32 and 64bit guests. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012,2013 - ARM Ltd 662306a36Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on arch/arm/kvm/emulate.c 962306a36Sopenharmony_ci * Copyright (C) 2012 - Virtual Open Systems and Columbia University 1062306a36Sopenharmony_ci * Author: Christoffer Dall <c.dall@virtualopensystems.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kvm_host.h> 1462306a36Sopenharmony_ci#include <asm/kvm_emulate.h> 1562306a36Sopenharmony_ci#include <asm/kvm_nested.h> 1662306a36Sopenharmony_ci#include <asm/esr.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic void pend_sync_exception(struct kvm_vcpu *vcpu) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci /* If not nesting, EL1 is the only possible exception target */ 2162306a36Sopenharmony_ci if (likely(!vcpu_has_nv(vcpu))) { 2262306a36Sopenharmony_ci kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); 2362306a36Sopenharmony_ci return; 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* 2762306a36Sopenharmony_ci * With NV, we need to pick between EL1 and EL2. Note that we 2862306a36Sopenharmony_ci * never deal with a nesting exception here, hence never 2962306a36Sopenharmony_ci * changing context, and the exception itself can be delayed 3062306a36Sopenharmony_ci * until the next entry. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci switch(*vcpu_cpsr(vcpu) & PSR_MODE_MASK) { 3362306a36Sopenharmony_ci case PSR_MODE_EL2h: 3462306a36Sopenharmony_ci case PSR_MODE_EL2t: 3562306a36Sopenharmony_ci kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SYNC); 3662306a36Sopenharmony_ci break; 3762306a36Sopenharmony_ci case PSR_MODE_EL1h: 3862306a36Sopenharmony_ci case PSR_MODE_EL1t: 3962306a36Sopenharmony_ci kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci case PSR_MODE_EL0t: 4262306a36Sopenharmony_ci if (vcpu_el2_tge_is_set(vcpu)) 4362306a36Sopenharmony_ci kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SYNC); 4462306a36Sopenharmony_ci else 4562306a36Sopenharmony_ci kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci default: 4862306a36Sopenharmony_ci BUG(); 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic bool match_target_el(struct kvm_vcpu *vcpu, unsigned long target) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci return (vcpu_get_flag(vcpu, EXCEPT_MASK) == target); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci unsigned long cpsr = *vcpu_cpsr(vcpu); 6062306a36Sopenharmony_ci bool is_aarch32 = vcpu_mode_is_32bit(vcpu); 6162306a36Sopenharmony_ci u64 esr = 0; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci pend_sync_exception(vcpu); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * Build an {i,d}abort, depending on the level and the 6762306a36Sopenharmony_ci * instruction set. Report an external synchronous abort. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci if (kvm_vcpu_trap_il_is32bit(vcpu)) 7062306a36Sopenharmony_ci esr |= ESR_ELx_IL; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* 7362306a36Sopenharmony_ci * Here, the guest runs in AArch64 mode when in EL1. If we get 7462306a36Sopenharmony_ci * an AArch32 fault, it means we managed to trap an EL0 fault. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci if (is_aarch32 || (cpsr & PSR_MODE_MASK) == PSR_MODE_EL0t) 7762306a36Sopenharmony_ci esr |= (ESR_ELx_EC_IABT_LOW << ESR_ELx_EC_SHIFT); 7862306a36Sopenharmony_ci else 7962306a36Sopenharmony_ci esr |= (ESR_ELx_EC_IABT_CUR << ESR_ELx_EC_SHIFT); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (!is_iabt) 8262306a36Sopenharmony_ci esr |= ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci esr |= ESR_ELx_FSC_EXTABT; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (match_target_el(vcpu, unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC))) { 8762306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, addr, FAR_EL1); 8862306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, esr, ESR_EL1); 8962306a36Sopenharmony_ci } else { 9062306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, addr, FAR_EL2); 9162306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, esr, ESR_EL2); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void inject_undef64(struct kvm_vcpu *vcpu) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci u64 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci pend_sync_exception(vcpu); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * Build an unknown exception, depending on the instruction 10362306a36Sopenharmony_ci * set. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci if (kvm_vcpu_trap_il_is32bit(vcpu)) 10662306a36Sopenharmony_ci esr |= ESR_ELx_IL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (match_target_el(vcpu, unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC))) 10962306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, esr, ESR_EL1); 11062306a36Sopenharmony_ci else 11162306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, esr, ESR_EL2); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define DFSR_FSC_EXTABT_LPAE 0x10 11562306a36Sopenharmony_ci#define DFSR_FSC_EXTABT_nLPAE 0x08 11662306a36Sopenharmony_ci#define DFSR_LPAE BIT(9) 11762306a36Sopenharmony_ci#define TTBCR_EAE BIT(31) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void inject_undef32(struct kvm_vcpu *vcpu) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci kvm_pend_exception(vcpu, EXCEPT_AA32_UND); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * Modelled after TakeDataAbortException() and TakePrefetchAbortException 12662306a36Sopenharmony_ci * pseudocode. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistatic void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, u32 addr) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci u64 far; 13162306a36Sopenharmony_ci u32 fsr; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Give the guest an IMPLEMENTATION DEFINED exception */ 13462306a36Sopenharmony_ci if (vcpu_read_sys_reg(vcpu, TCR_EL1) & TTBCR_EAE) { 13562306a36Sopenharmony_ci fsr = DFSR_LPAE | DFSR_FSC_EXTABT_LPAE; 13662306a36Sopenharmony_ci } else { 13762306a36Sopenharmony_ci /* no need to shuffle FS[4] into DFSR[10] as its 0 */ 13862306a36Sopenharmony_ci fsr = DFSR_FSC_EXTABT_nLPAE; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci far = vcpu_read_sys_reg(vcpu, FAR_EL1); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (is_pabt) { 14462306a36Sopenharmony_ci kvm_pend_exception(vcpu, EXCEPT_AA32_IABT); 14562306a36Sopenharmony_ci far &= GENMASK(31, 0); 14662306a36Sopenharmony_ci far |= (u64)addr << 32; 14762306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, fsr, IFSR32_EL2); 14862306a36Sopenharmony_ci } else { /* !iabt */ 14962306a36Sopenharmony_ci kvm_pend_exception(vcpu, EXCEPT_AA32_DABT); 15062306a36Sopenharmony_ci far &= GENMASK(63, 32); 15162306a36Sopenharmony_ci far |= addr; 15262306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, fsr, ESR_EL1); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, far, FAR_EL1); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/** 15962306a36Sopenharmony_ci * kvm_inject_dabt - inject a data abort into the guest 16062306a36Sopenharmony_ci * @vcpu: The VCPU to receive the data abort 16162306a36Sopenharmony_ci * @addr: The address to report in the DFAR 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * It is assumed that this code is called from the VCPU thread and that the 16462306a36Sopenharmony_ci * VCPU therefore is not currently executing guest code. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_civoid kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci if (vcpu_el1_is_32bit(vcpu)) 16962306a36Sopenharmony_ci inject_abt32(vcpu, false, addr); 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci inject_abt64(vcpu, false, addr); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * kvm_inject_pabt - inject a prefetch abort into the guest 17662306a36Sopenharmony_ci * @vcpu: The VCPU to receive the prefetch abort 17762306a36Sopenharmony_ci * @addr: The address to report in the DFAR 17862306a36Sopenharmony_ci * 17962306a36Sopenharmony_ci * It is assumed that this code is called from the VCPU thread and that the 18062306a36Sopenharmony_ci * VCPU therefore is not currently executing guest code. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_civoid kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci if (vcpu_el1_is_32bit(vcpu)) 18562306a36Sopenharmony_ci inject_abt32(vcpu, true, addr); 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci inject_abt64(vcpu, true, addr); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_civoid kvm_inject_size_fault(struct kvm_vcpu *vcpu) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci unsigned long addr, esr; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci addr = kvm_vcpu_get_fault_ipa(vcpu); 19562306a36Sopenharmony_ci addr |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (kvm_vcpu_trap_is_iabt(vcpu)) 19862306a36Sopenharmony_ci kvm_inject_pabt(vcpu, addr); 19962306a36Sopenharmony_ci else 20062306a36Sopenharmony_ci kvm_inject_dabt(vcpu, addr); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * If AArch64 or LPAE, set FSC to 0 to indicate an Address 20462306a36Sopenharmony_ci * Size Fault at level 0, as if exceeding PARange. 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * Non-LPAE guests will only get the external abort, as there 20762306a36Sopenharmony_ci * is no way to describe the ASF. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci if (vcpu_el1_is_32bit(vcpu) && 21062306a36Sopenharmony_ci !(vcpu_read_sys_reg(vcpu, TCR_EL1) & TTBCR_EAE)) 21162306a36Sopenharmony_ci return; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci esr = vcpu_read_sys_reg(vcpu, ESR_EL1); 21462306a36Sopenharmony_ci esr &= ~GENMASK_ULL(5, 0); 21562306a36Sopenharmony_ci vcpu_write_sys_reg(vcpu, esr, ESR_EL1); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/** 21962306a36Sopenharmony_ci * kvm_inject_undefined - inject an undefined instruction into the guest 22062306a36Sopenharmony_ci * @vcpu: The vCPU in which to inject the exception 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * It is assumed that this code is called from the VCPU thread and that the 22362306a36Sopenharmony_ci * VCPU therefore is not currently executing guest code. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_civoid kvm_inject_undefined(struct kvm_vcpu *vcpu) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci if (vcpu_el1_is_32bit(vcpu)) 22862306a36Sopenharmony_ci inject_undef32(vcpu); 22962306a36Sopenharmony_ci else 23062306a36Sopenharmony_ci inject_undef64(vcpu); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_civoid kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 esr) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci vcpu_set_vsesr(vcpu, esr & ESR_ELx_ISS_MASK); 23662306a36Sopenharmony_ci *vcpu_hcr(vcpu) |= HCR_VSE; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/** 24062306a36Sopenharmony_ci * kvm_inject_vabt - inject an async abort / SError into the guest 24162306a36Sopenharmony_ci * @vcpu: The VCPU to receive the exception 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * It is assumed that this code is called from the VCPU thread and that the 24462306a36Sopenharmony_ci * VCPU therefore is not currently executing guest code. 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * Systems with the RAS Extensions specify an imp-def ESR (ISV/IDS = 1) with 24762306a36Sopenharmony_ci * the remaining ISS all-zeros so that this error is not interpreted as an 24862306a36Sopenharmony_ci * uncategorized RAS error. Without the RAS Extensions we can't specify an ESR 24962306a36Sopenharmony_ci * value, so the CPU generates an imp-def value. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_civoid kvm_inject_vabt(struct kvm_vcpu *vcpu) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci kvm_set_sei_esr(vcpu, ESR_ELx_ISV); 25462306a36Sopenharmony_ci} 255