Commit 5326303b authored by Oliver Upton's avatar Oliver Upton
Browse files

KVM: arm64: nv: Honor guest hypervisor's FP/SVE traps in CPTR_EL2



Start folding the guest hypervisor's FP/SVE traps into the value
programmed in hardware. Note that as of writing this is dead code, since
KVM does a full put() / load() for every nested exception boundary which
saves + flushes the FP/SVE state.

However, this will become useful when we can keep the guest's FP/SVE
state alive across a nested exception boundary and the host no longer
needs to conservatively program traps.

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


Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parent 0cfc85b8
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -67,6 +67,8 @@ static u64 __compute_hcr(struct kvm_vcpu *vcpu)

static void __activate_cptr_traps(struct kvm_vcpu *vcpu)
{
	u64 cptr;

	/*
	 * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
	 * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
@@ -85,6 +87,35 @@ static void __activate_cptr_traps(struct kvm_vcpu *vcpu)
		__activate_traps_fpsimd32(vcpu);
	}

	/*
	 * Layer the guest hypervisor's trap configuration on top of our own if
	 * we're in a nested context.
	 */
	if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu))
		goto write;

	cptr = vcpu_sanitised_cptr_el2(vcpu);

	/*
	 * Pay attention, there's some interesting detail here.
	 *
	 * The CPTR_EL2.xEN fields are 2 bits wide, although there are only two
	 * meaningful trap states when HCR_EL2.TGE = 0 (running a nested guest):
	 *
	 *  - CPTR_EL2.xEN = x0, traps are enabled
	 *  - CPTR_EL2.xEN = x1, traps are disabled
	 *
	 * In other words, bit[0] determines if guest accesses trap or not. In
	 * the interest of simplicity, clear the entire field if the guest
	 * hypervisor has traps enabled to dispel any illusion of something more
	 * complicated taking place.
	 */
	if (!(SYS_FIELD_GET(CPACR_ELx, FPEN, cptr) & BIT(0)))
		val &= ~CPACR_ELx_FPEN;
	if (!(SYS_FIELD_GET(CPACR_ELx, ZEN, cptr) & BIT(0)))
		val &= ~CPACR_ELx_ZEN;

write:
	write_sysreg(val, cpacr_el1);
}