Commit 7ae44d1c authored by Oliver Upton's avatar Oliver Upton Committed by Marc Zyngier
Browse files

KVM: arm64: selftests: Use the vCPU attr for setting nr of PMU counters



Configuring the number of implemented counters via PMCR_EL0.N was a bad
idea in retrospect as it interacts poorly with nested. Migrate the
selftest to use the vCPU attribute instead of the KVM_SET_ONE_REG
mechanism.

Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent 0910778e
Loading
Loading
Loading
Loading
+28 −31
Original line number Diff line number Diff line
@@ -44,11 +44,6 @@ static uint64_t get_pmcr_n(uint64_t pmcr)
	return FIELD_GET(ARMV8_PMU_PMCR_N, pmcr);
}

static void set_pmcr_n(uint64_t *pmcr, uint64_t pmcr_n)
{
	u64p_replace_bits((__u64 *) pmcr, pmcr_n, ARMV8_PMU_PMCR_N);
}

static uint64_t get_counters_mask(uint64_t n)
{
	uint64_t mask = BIT(ARMV8_PMU_CYCLE_IDX);
@@ -414,10 +409,6 @@ static void create_vpmu_vm(void *guest_code)
		.attr = KVM_ARM_VCPU_PMU_V3_IRQ,
		.addr = (uint64_t)&irq,
	};
	struct kvm_device_attr init_attr = {
		.group = KVM_ARM_VCPU_PMU_V3_CTRL,
		.attr = KVM_ARM_VCPU_PMU_V3_INIT,
	};

	/* The test creates the vpmu_vm multiple times. Ensure a clean state */
	memset(&vpmu_vm, 0, sizeof(vpmu_vm));
@@ -444,9 +435,7 @@ static void create_vpmu_vm(void *guest_code)
		    pmuver >= ID_AA64DFR0_EL1_PMUVer_IMP,
		    "Unexpected PMUVER (0x%x) on the vCPU with PMUv3", pmuver);

	/* Initialize vPMU */
	vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &irq_attr);
	vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &init_attr);
}

static void destroy_vpmu_vm(void)
@@ -472,33 +461,28 @@ static void run_vcpu(struct kvm_vcpu *vcpu, uint64_t pmcr_n)
	}
}

static void test_create_vpmu_vm_with_pmcr_n(uint64_t pmcr_n, bool expect_fail)
static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool expect_fail)
{
	struct kvm_vcpu *vcpu;
	uint64_t pmcr, pmcr_orig;
	unsigned int prev;
	int ret;

	create_vpmu_vm(guest_code);
	vcpu = vpmu_vm.vcpu;

	pmcr_orig = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0));
	pmcr = pmcr_orig;
	prev = get_pmcr_n(vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0)));

	/*
	 * Setting a larger value of PMCR.N should not modify the field, and
	 * return a success.
	 */
	set_pmcr_n(&pmcr, pmcr_n);
	vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0), pmcr);
	pmcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0));
	ret = __vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
				     KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS, &nr_counters);

	if (expect_fail)
		TEST_ASSERT(pmcr_orig == pmcr,
			    "PMCR.N modified by KVM to a larger value (PMCR: 0x%lx) for pmcr_n: 0x%lx",
			    pmcr, pmcr_n);
		TEST_ASSERT(ret && errno == EINVAL,
			    "Setting more PMU counters (%u) than available (%u) unexpectedly succeeded",
			    nr_counters, prev);
	else
		TEST_ASSERT(pmcr_n == get_pmcr_n(pmcr),
			    "Failed to update PMCR.N to %lu (received: %lu)",
			    pmcr_n, get_pmcr_n(pmcr));
		TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));

	vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, NULL);
}

/*
@@ -513,7 +497,7 @@ static void run_access_test(uint64_t pmcr_n)

	pr_debug("Test with pmcr_n %lu\n", pmcr_n);

	test_create_vpmu_vm_with_pmcr_n(pmcr_n, false);
	test_create_vpmu_vm_with_nr_counters(pmcr_n, false);
	vcpu = vpmu_vm.vcpu;

	/* Save the initial sp to restore them later to run the guest again */
@@ -554,7 +538,7 @@ static void run_pmregs_validity_test(uint64_t pmcr_n)
	uint64_t set_reg_id, clr_reg_id, reg_val;
	uint64_t valid_counters_mask, max_counters_mask;

	test_create_vpmu_vm_with_pmcr_n(pmcr_n, false);
	test_create_vpmu_vm_with_nr_counters(pmcr_n, false);
	vcpu = vpmu_vm.vcpu;

	valid_counters_mask = get_counters_mask(pmcr_n);
@@ -608,7 +592,7 @@ static void run_error_test(uint64_t pmcr_n)
{
	pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n);

	test_create_vpmu_vm_with_pmcr_n(pmcr_n, true);
	test_create_vpmu_vm_with_nr_counters(pmcr_n, true);
	destroy_vpmu_vm();
}

@@ -626,12 +610,25 @@ static uint64_t get_pmcr_n_limit(void)
	return get_pmcr_n(pmcr);
}

static bool kvm_supports_nr_counters_attr(void)
{
	bool supported;

	create_vpmu_vm(NULL);
	supported = !__vcpu_has_device_attr(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
					    KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS);
	destroy_vpmu_vm();

	return supported;
}

int main(void)
{
	uint64_t i, pmcr_n;

	TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3));
	TEST_REQUIRE(kvm_supports_vgic_v3());
	TEST_REQUIRE(kvm_supports_nr_counters_attr());

	pmcr_n = get_pmcr_n_limit();
	for (i = 0; i <= pmcr_n; i++) {