Commit c8823e51 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

KVM: arm64: Fix MDCR_EL2.HPMN reset value



The MDCR_EL2 documentation indicates that the HPMN field has
the following behaviour:

"On a Warm reset, this field resets to the expression NUM_PMU_COUNTERS."

However, it appears we reset it to zero, which is not very useful.

Add a reset helper for MDCR_EL2, and handle the case where userspace
changes the target PMU, which may force us to change HPMN again.

Reported-by: default avatarJoey Gouly <joey.gouly@arm.com>
Reviewed-by: default avatarOliver Upton <oliver.upton@linux.dev>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent f12b54d7
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -1027,12 +1027,30 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
	return bitmap_weight(arm_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS);
}

static void kvm_arm_set_nr_counters(struct kvm *kvm, unsigned int nr)
{
	kvm->arch.nr_pmu_counters = nr;

	/* Reset MDCR_EL2.HPMN behind the vcpus' back... */
	if (test_bit(KVM_ARM_VCPU_HAS_EL2, kvm->arch.vcpu_features)) {
		struct kvm_vcpu *vcpu;
		unsigned long i;

		kvm_for_each_vcpu(i, vcpu, kvm) {
			u64 val = __vcpu_sys_reg(vcpu, MDCR_EL2);
			val &= ~MDCR_EL2_HPMN;
			val |= FIELD_PREP(MDCR_EL2_HPMN, kvm->arch.nr_pmu_counters);
			__vcpu_sys_reg(vcpu, MDCR_EL2) = val;
		}
	}
}

static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)
{
	lockdep_assert_held(&kvm->arch.config_lock);

	kvm->arch.arm_pmu = arm_pmu;
	kvm->arch.nr_pmu_counters = kvm_arm_pmu_get_max_counters(kvm);
	kvm_arm_set_nr_counters(kvm, kvm_arm_pmu_get_max_counters(kvm));
}

/**
+7 −1
Original line number Diff line number Diff line
@@ -2700,6 +2700,12 @@ static int set_imp_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
	.val = mask,					\
	}

static u64 reset_mdcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
{
	__vcpu_sys_reg(vcpu, r->reg) = vcpu->kvm->arch.nr_pmu_counters;
	return vcpu->kvm->arch.nr_pmu_counters;
}

/*
 * Architected system registers.
 * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
@@ -3243,7 +3249,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
	EL2_REG(SCTLR_EL2, access_rw, reset_val, SCTLR_EL2_RES1),
	EL2_REG(ACTLR_EL2, access_rw, reset_val, 0),
	EL2_REG_VNCR(HCR_EL2, reset_hcr, 0),
	EL2_REG(MDCR_EL2, access_mdcr, reset_val, 0),
	EL2_REG(MDCR_EL2, access_mdcr, reset_mdcr, 0),
	EL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1),
	EL2_REG_VNCR(HSTR_EL2, reset_val, 0),
	EL2_REG_VNCR(HFGRTR_EL2, reset_val, 0),