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