Commit 9aba641b authored by Oliver Upton's avatar Oliver Upton
Browse files

KVM: arm64: nv: Respect exception routing rules for SEAs



Synchronous external aborts are taken to EL2 if ELIsInHost() or
HCR_EL2.TEA=1. Rework the SEA injection plumbing to respect the imposed
routing of the guest hypervisor and opportunistically rephrase things to
make their function a bit more obvious.

Reviewed-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250708172532.1699409-6-oliver.upton@linux.dev


Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parent aae35f4f
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -46,15 +46,25 @@ void kvm_skip_instr32(struct kvm_vcpu *vcpu);

void kvm_inject_undefined(struct kvm_vcpu *vcpu);
void kvm_inject_vabt(struct kvm_vcpu *vcpu);
void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);
int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
void kvm_inject_size_fault(struct kvm_vcpu *vcpu);

static inline int kvm_inject_sea_dabt(struct kvm_vcpu *vcpu, u64 addr)
{
	return kvm_inject_sea(vcpu, false, addr);
}

static inline int kvm_inject_sea_iabt(struct kvm_vcpu *vcpu, u64 addr)
{
	return kvm_inject_sea(vcpu, true, addr);
}

void kvm_vcpu_wfi(struct kvm_vcpu *vcpu);

void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu);
int kvm_inject_nested_sync(struct kvm_vcpu *vcpu, u64 esr_el2);
int kvm_inject_nested_irq(struct kvm_vcpu *vcpu);
int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);

static inline void kvm_inject_nested_sve_trap(struct kvm_vcpu *vcpu)
{
+10 −0
Original line number Diff line number Diff line
@@ -2811,3 +2811,13 @@ int kvm_inject_nested_irq(struct kvm_vcpu *vcpu)
	/* esr_el2 value doesn't matter for exits due to irqs. */
	return kvm_inject_nested(vcpu, 0, except_type_irq);
}

int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
{
	u64 esr = FIELD_PREP(ESR_ELx_EC_MASK,
			     iabt ? ESR_ELx_EC_IABT_LOW : ESR_ELx_EC_DABT_LOW);
	esr |= ESR_ELx_FSC_EXTABT | ESR_ELx_IL;

	vcpu_write_sys_reg(vcpu, FAR_EL2, addr);
	return kvm_inject_nested_sync(vcpu, esr);
}
+3 −2
Original line number Diff line number Diff line
@@ -839,6 +839,7 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
	bool serror_pending = events->exception.serror_pending;
	bool has_esr = events->exception.serror_has_esr;
	bool ext_dabt_pending = events->exception.ext_dabt_pending;
	int ret = 0;

	if (serror_pending && has_esr) {
		if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
@@ -853,9 +854,9 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
	}

	if (ext_dabt_pending)
		kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
		ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));

	return 0;
	return (ret < 0) ? ret : 0;
}

u32 __attribute_const__ kvm_target_cpu(void)
+17 −28
Original line number Diff line number Diff line
@@ -155,36 +155,28 @@ static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, u32 addr)
	vcpu_write_sys_reg(vcpu, far, FAR_EL1);
}

/**
 * kvm_inject_dabt - inject a data abort into the guest
 * @vcpu: The VCPU to receive the data abort
 * @addr: The address to report in the DFAR
 *
 * It is assumed that this code is called from the VCPU thread and that the
 * VCPU therefore is not currently executing guest code.
 */
void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr)
static void __kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
{
	if (vcpu_el1_is_32bit(vcpu))
		inject_abt32(vcpu, false, addr);
		inject_abt32(vcpu, iabt, addr);
	else
		inject_abt64(vcpu, false, addr);
		inject_abt64(vcpu, iabt, addr);
}

/**
 * kvm_inject_pabt - inject a prefetch abort into the guest
 * @vcpu: The VCPU to receive the prefetch abort
 * @addr: The address to report in the DFAR
 *
 * It is assumed that this code is called from the VCPU thread and that the
 * VCPU therefore is not currently executing guest code.
 */
void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr)
static bool kvm_sea_target_is_el2(struct kvm_vcpu *vcpu)
{
	if (vcpu_el1_is_32bit(vcpu))
		inject_abt32(vcpu, true, addr);
	else
		inject_abt64(vcpu, true, addr);
	return __vcpu_sys_reg(vcpu, HCR_EL2) & (HCR_TGE | HCR_TEA);
}

int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
{
	lockdep_assert_held(&vcpu->mutex);

	if (is_nested_ctxt(vcpu) && kvm_sea_target_is_el2(vcpu))
		return kvm_inject_nested_sea(vcpu, iabt, addr);

	__kvm_inject_sea(vcpu, iabt, addr);
	return 1;
}

void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
@@ -194,10 +186,7 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
	addr  = kvm_vcpu_get_fault_ipa(vcpu);
	addr |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);

	if (kvm_vcpu_trap_is_iabt(vcpu))
		kvm_inject_pabt(vcpu, addr);
	else
		kvm_inject_dabt(vcpu, addr);
	__kvm_inject_sea(vcpu, kvm_vcpu_trap_is_iabt(vcpu), addr);

	/*
	 * If AArch64 or LPAE, set FSC to 0 to indicate an Address
+2 −4
Original line number Diff line number Diff line
@@ -169,10 +169,8 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
		trace_kvm_mmio_nisv(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu),
				    kvm_vcpu_get_hfar(vcpu), fault_ipa);

		if (vcpu_is_protected(vcpu)) {
			kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
			return 1;
		}
		if (vcpu_is_protected(vcpu))
			return kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));

		if (test_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,
			     &vcpu->kvm->arch.flags)) {
Loading