From nobody Tue Apr 7 02:38:47 2026 Received: from www3579.sakura.ne.jp (www3579.sakura.ne.jp [49.212.243.89]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F10EE2D5935; Tue, 17 Mar 2026 06:38:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=49.212.243.89 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773729492; cv=none; b=J9jhdbv7W6BqPjtNsVnE+U2MrSFDZyZ+5/gJ9dK6TTxEWEVwFqrpGIQRrnkkm9S8Lw/iNGQcVmkBOExo2zQ8ZMTsbd+1NHtSHKWuKq3AiMQsWU7HkttW8FQ/vGYUlEoes8fnRMvBGDLzCORWxMlVz3jLL5EfqtLEe9dCNWrm7Ig= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773729492; c=relaxed/simple; bh=V7yGTOLIYvJ+y17btsf2NAXw0lQUB6xkJtS6Hal8JcM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MSW4R3ndS2qIGz87V50q71s/63S9B5Yais4TNMpd/weHq+JMjAGtJtjFsXu/wa5V8oV8K6ogsVRxR5pEwKgqeQJrD9w45bsQ719b8smFYYTP/DtOqfKtsln74emnrfTML89ICSzO5RPbEmuJh/oNXjCEMDmCjkYyQRvVrtULkVk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rsg.ci.i.u-tokyo.ac.jp; spf=pass smtp.mailfrom=rsg.ci.i.u-tokyo.ac.jp; dkim=fail (0-bit key) header.d=rsg.ci.i.u-tokyo.ac.jp header.i=@rsg.ci.i.u-tokyo.ac.jp header.b=KF33p9pM reason="key not found in DNS"; arc=none smtp.client-ip=49.212.243.89 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rsg.ci.i.u-tokyo.ac.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rsg.ci.i.u-tokyo.ac.jp Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="key not found in DNS" (0-bit key) header.d=rsg.ci.i.u-tokyo.ac.jp header.i=@rsg.ci.i.u-tokyo.ac.jp header.b="KF33p9pM" Received: from h205.csg.ci.i.u-tokyo.ac.jp (h205.csg.ci.i.u-tokyo.ac.jp [133.11.54.205]) (authenticated bits=0) by www3579.sakura.ne.jp (8.16.1/8.16.1) with ESMTPSA id 62H6aoNo004343 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Tue, 17 Mar 2026 15:37:01 +0900 (JST) (envelope-from odaki@rsg.ci.i.u-tokyo.ac.jp) DKIM-Signature: a=rsa-sha256; bh=ZtQ5dlfNVl2+jdFm1CbTKfe5Q89iVgHpFp8aH6VaLbI=; c=relaxed/relaxed; d=rsg.ci.i.u-tokyo.ac.jp; h=From:Message-Id:To:Subject:Date; s=rs20250326; t=1773729421; v=1; b=KF33p9pMgcgIfTvtGcPdqCI3NaQhGBjUMpZkmVE+JZ1EOqjHAzyaSoB54QlgvrU5 itk+CX4row7D+aR9jTbA9XRR/W74/iBeGlYlhs+d46pxqXHENGCZUY8mb+BflpJo LvHH9/jGhgiLNi3zopJAMVWsXvnk693ZcY3F2vB6fCjuBQlbteA0Hmrj86+91gjx oX+fawo7CkaMoNFRMs7BxVYyB633lPb0Dke5HA+HwxNTSKqlIp2fuYo4om70vfrC Zrg1mVQ28DXUiubZ8OSxPyCk13SkBVu1GyUAppGGOO7ZaiZm/YWJ6GZScsZtdx3a /HIeFW+uLCs/ay5V256nkQ== From: Akihiko Odaki Date: Tue, 17 Mar 2026 15:36:52 +0900 Subject: [PATCH v4 4/4] KVM: arm64: selftests: Test PMU_V3_FIXED_COUNTERS_ONLY Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260317-hybrid-v4-4-bd62bcd48644@rsg.ci.i.u-tokyo.ac.jp> References: <20260317-hybrid-v4-0-bd62bcd48644@rsg.ci.i.u-tokyo.ac.jp> In-Reply-To: <20260317-hybrid-v4-0-bd62bcd48644@rsg.ci.i.u-tokyo.ac.jp> To: Marc Zyngier , Oliver Upton , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Catalin Marinas , Will Deacon , Kees Cook , "Gustavo A. R. Silva" , Paolo Bonzini , Jonathan Corbet , Shuah Khan Cc: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org, linux-hardening@vger.kernel.org, devel@daynix.com, kvm@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, Akihiko Odaki X-Mailer: b4 0.15-dev-5ab4c Assert the following: - KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY is unset at initialization. - KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY can be set. - Setting KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY for the first time after setting an event filter results in EBUSY. - KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY can be set again even if an event filter has already been set. - Setting KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY after running a VCPU results in EBUSY. - The existing test cases pass with KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY set. Signed-off-by: Akihiko Odaki --- .../selftests/kvm/arm64/vpmu_counter_access.c | 148 +++++++++++++++++= ---- 1 file changed, 122 insertions(+), 26 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tool= s/testing/selftests/kvm/arm64/vpmu_counter_access.c index ae36325c022f..156bfa636923 100644 --- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c +++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c @@ -403,12 +403,7 @@ static void create_vpmu_vm(void *guest_code) { struct kvm_vcpu_init init; uint8_t pmuver, ec; - uint64_t dfr0, irq =3D 23; - struct kvm_device_attr irq_attr =3D { - .group =3D KVM_ARM_VCPU_PMU_V3_CTRL, - .attr =3D KVM_ARM_VCPU_PMU_V3_IRQ, - .addr =3D (uint64_t)&irq, - }; + uint64_t dfr0; =20 /* The test creates the vpmu_vm multiple times. Ensure a clean state */ memset(&vpmu_vm, 0, sizeof(vpmu_vm)); @@ -434,8 +429,6 @@ static void create_vpmu_vm(void *guest_code) TEST_ASSERT(pmuver !=3D ID_AA64DFR0_EL1_PMUVer_IMP_DEF && pmuver >=3D ID_AA64DFR0_EL1_PMUVer_IMP, "Unexpected PMUVER (0x%x) on the vCPU with PMUv3", pmuver); - - vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &irq_attr); } =20 static void destroy_vpmu_vm(void) @@ -461,15 +454,25 @@ static void run_vcpu(struct kvm_vcpu *vcpu, uint64_t = pmcr_n) } } =20 -static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters,= bool expect_fail) +static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, + bool fixed_counters_only, + bool expect_fail) { struct kvm_vcpu *vcpu; unsigned int prev; int ret; + uint64_t irq =3D 23; =20 create_vpmu_vm(guest_code); vcpu =3D vpmu_vm.vcpu; =20 + if (fixed_counters_only) + vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + + vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_IRQ, &irq); + prev =3D get_pmcr_n(vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0))); =20 ret =3D __vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, @@ -489,15 +492,15 @@ static void test_create_vpmu_vm_with_nr_counters(unsi= gned int nr_counters, bool * Create a guest with one vCPU, set the PMCR_EL0.N for the vCPU to @pmcr_= n, * and run the test. */ -static void run_access_test(uint64_t pmcr_n) +static void run_access_test(uint64_t pmcr_n, bool fixed_counters_only) { uint64_t sp; struct kvm_vcpu *vcpu; struct kvm_vcpu_init init; =20 - pr_debug("Test with pmcr_n %lu\n", pmcr_n); + pr_debug("Test with pmcr_n %lu, fixed_counters_only %d\n", pmcr_n, fixed_= counters_only); =20 - test_create_vpmu_vm_with_nr_counters(pmcr_n, false); + test_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, false); vcpu =3D vpmu_vm.vcpu; =20 /* Save the initial sp to restore them later to run the guest again */ @@ -531,14 +534,14 @@ static struct pmreg_sets validity_check_reg_sets[] = =3D { * Create a VM, and check if KVM handles the userspace accesses of * the PMU register sets in @validity_check_reg_sets[] correctly. */ -static void run_pmregs_validity_test(uint64_t pmcr_n) +static void run_pmregs_validity_test(uint64_t pmcr_n, bool fixed_counters_= only) { int i; struct kvm_vcpu *vcpu; uint64_t set_reg_id, clr_reg_id, reg_val; uint64_t valid_counters_mask, max_counters_mask; =20 - test_create_vpmu_vm_with_nr_counters(pmcr_n, false); + test_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, false); vcpu =3D vpmu_vm.vcpu; =20 valid_counters_mask =3D get_counters_mask(pmcr_n); @@ -588,11 +591,11 @@ static void run_pmregs_validity_test(uint64_t pmcr_n) * the vCPU to @pmcr_n, which is larger than the host value. * The attempt should fail as @pmcr_n is too big to set for the vCPU. */ -static void run_error_test(uint64_t pmcr_n) +static void run_error_test(uint64_t pmcr_n, bool fixed_counters_only) { pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n); =20 - test_create_vpmu_vm_with_nr_counters(pmcr_n, true); + test_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, true); destroy_vpmu_vm(); } =20 @@ -622,22 +625,115 @@ static bool kvm_supports_nr_counters_attr(void) return supported; } =20 -int main(void) +static void test_config(uint64_t pmcr_n, bool fixed_counters_only) { - 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()); + uint64_t i; =20 - pmcr_n =3D get_pmcr_n_limit(); for (i =3D 0; i <=3D pmcr_n; i++) { - run_access_test(i); - run_pmregs_validity_test(i); + run_access_test(i, fixed_counters_only); + run_pmregs_validity_test(i, fixed_counters_only); } =20 for (i =3D pmcr_n + 1; i < ARMV8_PMU_MAX_COUNTERS; i++) - run_error_test(i); + run_error_test(i, fixed_counters_only); +} + +static void test_fixed_counters_only(void) +{ + struct kvm_pmu_event_filter filter =3D { .nevents =3D 0 }; + struct kvm_vm *vm; + struct kvm_vcpu *running_vcpu; + struct kvm_vcpu *stopped_vcpu; + struct kvm_vcpu_init init; + int ret; + uint64_t irq =3D 23; + + create_vpmu_vm(guest_code); + ret =3D __vcpu_has_device_attr(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY); + if (ret) { + TEST_ASSERT(ret =3D=3D -1 && errno =3D=3D ENXIO, + KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); + destroy_vpmu_vm(); + return; + } + + /* Assert that FIXED_COUNTERS_ONLY is unset at initialization. */ + ret =3D __vcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + TEST_ASSERT(ret =3D=3D -1 && errno =3D=3D ENXIO, + KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); + + /* Assert that setting FIXED_COUNTERS_ONLY succeeds. */ + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + + /* Assert that getting FIXED_COUNTERS_ONLY succeeds. */ + vcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + + /* + * Assert that setting FIXED_COUNTERS_ONLY again succeeds even if an + * event filter has already been set. + */ + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FILTER, &filter); + + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + + destroy_vpmu_vm(); + + create_vpmu_vm(guest_code); + + /* + * Assert that setting FIXED_COUNTERS_ONLY results in EBUSY if an event + * filter has already been set while FIXED_COUNTERS_ONLY has not. + */ + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FILTER, &filter); + + ret =3D __vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + TEST_ASSERT(ret =3D=3D -1 && errno =3D=3D EBUSY, + KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); + + destroy_vpmu_vm(); + + /* + * Assert that setting FIXED_COUNTERS_ONLY after running a VCPU results + * in EBUSY. + */ + vm =3D vm_create(2); + vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init); + init.features[0] |=3D (1 << KVM_ARM_VCPU_PMU_V3); + running_vcpu =3D aarch64_vcpu_add(vm, 0, &init, guest_code); + stopped_vcpu =3D aarch64_vcpu_add(vm, 1, &init, guest_code); + kvm_arch_vm_finalize_vcpus(vm); + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_IRQ, &irq); + vcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_INIT, NULL); + vcpu_run(running_vcpu); + + ret =3D __vcpu_device_attr_set(stopped_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + TEST_ASSERT(ret =3D=3D -1 && errno =3D=3D EBUSY, + KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); + + kvm_vm_free(vm); + + test_config(0, true); +} + +int main(void) +{ + TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3)); + TEST_REQUIRE(kvm_supports_vgic_v3()); + TEST_REQUIRE(kvm_supports_nr_counters_attr()); + + test_config(get_pmcr_n_limit(), false); + test_fixed_counters_only(); =20 return 0; } --=20 2.53.0