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

KVM: arm64: Allow userspace to limit the number of PMU counters for EL2 VMs



As long as we had purely EL1 VMs, we could easily update the number
of guest-visible counters by letting userspace write to PMCR_EL0.N.

With VMs started at EL2, PMCR_EL1.N only reflects MDCR_EL2.HPMN,
and we don't have a good way to limit it.

For this purpose, introduce a new PMUv3 attribute that allows
limiting the maximum number of counters. This requires the explicit
selection of a PMU.

Suggested-by: default avatarOliver Upton <oliver.upton@linux.dev>
Reviewed-by: default avatarOliver Upton <oliver.upton@linux.dev>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent 02243533
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -137,6 +137,30 @@ exit_reason = KVM_EXIT_FAIL_ENTRY and populate the fail_entry struct by setting
hardare_entry_failure_reason field to KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED and
the cpu field to the processor id.

1.5 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS
--------------------------------------------------

:Parameters: in kvm_device_attr.addr the address to an unsigned int
	     representing the maximum value taken by PMCR_EL0.N

:Returns:

	 =======  ====================================================
	 -EBUSY   PMUv3 already initialized, a VCPU has already run or
                  an event filter has already been set
	 -EFAULT  Error accessing the value pointed to by addr
	 -ENODEV  PMUv3 not supported or GIC not initialized
	 -EINVAL  No PMUv3 explicitly selected, or value of N out of
	 	  range
	 =======  ====================================================

Set the number of implemented event counters in the virtual PMU. This
mandates that a PMU has explicitly been selected via
KVM_ARM_VCPU_PMU_V3_SET_PMU, and will fail when no PMU has been
explicitly selected, or the number of counters is out of range for the
selected PMU. Selecting a new PMU cancels the effect of setting this
attribute.

2. GROUP: KVM_ARM_VCPU_TIMER_CTRL
=================================

+5 −4
Original line number Diff line number Diff line
@@ -435,6 +435,7 @@ enum {
#define   KVM_ARM_VCPU_PMU_V3_INIT		1
#define   KVM_ARM_VCPU_PMU_V3_FILTER		2
#define   KVM_ARM_VCPU_PMU_V3_SET_PMU		3
#define   KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS	4
#define KVM_ARM_VCPU_TIMER_CTRL		1
#define   KVM_ARM_VCPU_TIMER_IRQ_VTIMER		0
#define   KVM_ARM_VCPU_TIMER_IRQ_PTIMER		1
+24 −0
Original line number Diff line number Diff line
@@ -1104,6 +1104,20 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
	return ret;
}

static int kvm_arm_pmu_v3_set_nr_counters(struct kvm_vcpu *vcpu, unsigned int n)
{
	struct kvm *kvm = vcpu->kvm;

	if (!kvm->arch.arm_pmu)
		return -EINVAL;

	if (n > kvm_arm_pmu_get_max_counters(kvm))
		return -EINVAL;

	kvm_arm_set_nr_counters(kvm, n);
	return 0;
}

int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
{
	struct kvm *kvm = vcpu->kvm;
@@ -1200,6 +1214,15 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)

		return kvm_arm_pmu_v3_set_pmu(vcpu, pmu_id);
	}
	case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS: {
		unsigned int __user *uaddr = (unsigned int __user *)(long)attr->addr;
		unsigned int n;

		if (get_user(n, uaddr))
			return -EFAULT;

		return kvm_arm_pmu_v3_set_nr_counters(vcpu, n);
	}
	case KVM_ARM_VCPU_PMU_V3_INIT:
		return kvm_arm_pmu_v3_init(vcpu);
	}
@@ -1238,6 +1261,7 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
	case KVM_ARM_VCPU_PMU_V3_INIT:
	case KVM_ARM_VCPU_PMU_V3_FILTER:
	case KVM_ARM_VCPU_PMU_V3_SET_PMU:
	case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS:
		if (kvm_vcpu_has_pmu(vcpu))
			return 0;
	}