Commit b3d29a82 authored by Oliver Upton's avatar Oliver Upton
Browse files

KVM: arm64: nv: Handle ZCR_EL2 traps



Unlike other SVE-related registers, ZCR_EL2 takes a sysreg trap to EL2
when HCR_EL2.NV = 1. KVM still needs to honor the guest hypervisor's
trap configuration, which expects an SVE trap (i.e. ESR_EL2.EC = 0x19)
when CPTR traps are enabled for the vCPU's current context.

Otherwise, if the guest hypervisor has traps disabled, emulate the
access by mapping the requested VL into ZCR_EL1.

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


Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parent 399debfc
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -56,6 +56,14 @@ 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);

static inline void kvm_inject_nested_sve_trap(struct kvm_vcpu *vcpu)
{
	u64 esr = FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SVE) |
		  ESR_ELx_IL;

	kvm_inject_nested_sync(vcpu, esr);
}

#if defined(__KVM_VHE_HYPERVISOR__) || defined(__KVM_NVHE_HYPERVISOR__)
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
{
+3 −0
Original line number Diff line number Diff line
@@ -423,6 +423,7 @@ enum vcpu_sysreg {
	MDCR_EL2,	/* Monitor Debug Configuration Register (EL2) */
	CPTR_EL2,	/* Architectural Feature Trap Register (EL2) */
	HACR_EL2,	/* Hypervisor Auxiliary Control Register */
	ZCR_EL2,	/* SVE Control Register (EL2) */
	TTBR0_EL2,	/* Translation Table Base Register 0 (EL2) */
	TTBR1_EL2,	/* Translation Table Base Register 1 (EL2) */
	TCR_EL2,	/* Translation Control Register (EL2) */
@@ -991,6 +992,7 @@ static inline bool __vcpu_read_sys_reg_from_cpu(int reg, u64 *val)
	case DACR32_EL2:	*val = read_sysreg_s(SYS_DACR32_EL2);	break;
	case IFSR32_EL2:	*val = read_sysreg_s(SYS_IFSR32_EL2);	break;
	case DBGVCR32_EL2:	*val = read_sysreg_s(SYS_DBGVCR32_EL2);	break;
	case ZCR_EL1:		*val = read_sysreg_s(SYS_ZCR_EL12);	break;
	default:		return false;
	}

@@ -1036,6 +1038,7 @@ static inline bool __vcpu_write_sys_reg_to_cpu(u64 val, int reg)
	case DACR32_EL2:	write_sysreg_s(val, SYS_DACR32_EL2);	break;
	case IFSR32_EL2:	write_sysreg_s(val, SYS_IFSR32_EL2);	break;
	case DBGVCR32_EL2:	write_sysreg_s(val, SYS_DBGVCR32_EL2);	break;
	case ZCR_EL1:		write_sysreg_s(val, SYS_ZCR_EL12);	break;
	default:		return false;
	}

+38 −0
Original line number Diff line number Diff line
@@ -121,6 +121,7 @@ static bool get_el2_to_el1_mapping(unsigned int reg,
		MAPPED_EL2_SYSREG(AMAIR_EL2,   AMAIR_EL1,   NULL	     );
		MAPPED_EL2_SYSREG(ELR_EL2,     ELR_EL1,	    NULL	     );
		MAPPED_EL2_SYSREG(SPSR_EL2,    SPSR_EL1,    NULL	     );
		MAPPED_EL2_SYSREG(ZCR_EL2,     ZCR_EL1,     NULL	     );
	default:
		return false;
	}
@@ -2199,6 +2200,40 @@ static u64 reset_hcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
	return __vcpu_sys_reg(vcpu, r->reg) = val;
}

static unsigned int sve_el2_visibility(const struct kvm_vcpu *vcpu,
				       const struct sys_reg_desc *rd)
{
	unsigned int r;

	r = el2_visibility(vcpu, rd);
	if (r)
		return r;

	return sve_visibility(vcpu, rd);
}

static bool access_zcr_el2(struct kvm_vcpu *vcpu,
			   struct sys_reg_params *p,
			   const struct sys_reg_desc *r)
{
	unsigned int vq;

	if (guest_hyp_sve_traps_enabled(vcpu)) {
		kvm_inject_nested_sve_trap(vcpu);
		return true;
	}

	if (!p->is_write) {
		p->regval = vcpu_read_sys_reg(vcpu, ZCR_EL2);
		return true;
	}

	vq = SYS_FIELD_GET(ZCR_ELx, LEN, p->regval) + 1;
	vq = min(vq, vcpu_sve_max_vq(vcpu));
	vcpu_write_sys_reg(vcpu, vq - 1, ZCR_EL2);
	return true;
}

/*
 * Architected system registers.
 * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
@@ -2688,6 +2723,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {
	EL2_REG_VNCR(HFGITR_EL2, reset_val, 0),
	EL2_REG_VNCR(HACR_EL2, reset_val, 0),

	{ SYS_DESC(SYS_ZCR_EL2), .access = access_zcr_el2, .reset = reset_val,
	  .visibility = sve_el2_visibility, .reg = ZCR_EL2 },

	EL2_REG_VNCR(HCRX_EL2, reset_val, 0),

	EL2_REG(TTBR0_EL2, access_rw, reset_val, 0),