From nobody Sun Oct 5 10:47:20 2025 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 3DED21F948; Wed, 6 Aug 2025 09:11:13 +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=1754471476; cv=none; b=I2OqkG7OAdW+xuRJLRfIaItl8EUrZ87K7udPF7fT7z8VqMAvpXH2SNLEBVh+LlmcWo6BzMgBt14sv3YAaNqO0OXqMTSJdPiEIZpNhglZW21oFFPe1kcSbpDeCjabvxm/RK4uovGo82+yLHHTa1WdZt5ga/EQRAxb4JRzSQs+NOw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754471476; c=relaxed/simple; bh=GHrQqSC1UtWlG4pv4bxpFM9GNMvL/w8HFNVLh9OzojE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=KnV0KixMsSmUENulrWfoKmMJoBdDIPyKXdCHF7XfIMQGCL2h3JQ2BB3ov86rGEUpFpjpedXTw0tuip9ht+4/j+RKfkG/9WZKL0GqtLykqNZ/Y0vpvhTeCA3PgDShlddwbrLnB11KCZaspWiHdsUPIbDgJU6fuSuUH+/k6q7nKxk= 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=ILovQubZ 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="ILovQubZ" 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 5769A4Q5005818 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Wed, 6 Aug 2025 18:10:13 +0900 (JST) (envelope-from odaki@rsg.ci.i.u-tokyo.ac.jp) DKIM-Signature: a=rsa-sha256; bh=cNChbfwbJxu1p5jME8+k/YzZo2FzwjCcltFsF+wNolE=; c=relaxed/relaxed; d=rsg.ci.i.u-tokyo.ac.jp; h=From:Date:Subject:Message-Id:To; s=rs20250326; t=1754471413; v=1; b=ILovQubZMvdmX4JupMk0+o5hS9d1oHFD/LOTYDhCE9Rx1fIWftCeY+n5oeAPTDYg Eqe74MSJsTncKg9JzaZL0QyvCPj7JSZZa2UiLlIl7jlL+uOSgFkVy7uXaPfZ6M3c J8x/BLWE36onnKZWuL9r/H/RR9Dlkpvari6SP8pmN2kV8Fi69UNiMRWWonyQl6sX /nwbnxU4dYSURcWsDjE6AYKC+eQuZ3eCWXfVca8QcDRuDzZOVw2FT0+AIeVweHoI CiWev08DGiq7qgq+19v+7R3R0zABjK5m6+pOau/HIL1Odsj8SwCTXfxoddYAILOO zs6v7mRu1+ZJs3BowaDHYA== From: Akihiko Odaki Date: Wed, 06 Aug 2025 18:09:55 +0900 Subject: [PATCH RFC v2 2/2] KVM: arm64: selftests: Test guest PMUv3 composition 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: <20250806-hybrid-v2-2-0661aec3af8c@rsg.ci.i.u-tokyo.ac.jp> References: <20250806-hybrid-v2-0-0661aec3af8c@rsg.ci.i.u-tokyo.ac.jp> In-Reply-To: <20250806-hybrid-v2-0-0661aec3af8c@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.14.2 Test the following: - KVM_ARM_VCPU_PMU_V3_COMPOSITION is unset at initialization. - KVM_ARM_VCPU_PMU_V3_COMPOSITION can be set. - Setting KVM_ARM_VCPU_PMU_V3_COMPOSITION for the first after setting an event filter results in EBUSY. - KVM_ARM_VCPU_PMU_V3_COMPOSITION can be set again even if an event filter has already been set. - Setting KVM_ARM_VCPU_PMU_V3_COMPOSITION after running a VCPU results in EBUSY. - The composite guest PMUv3 behaves as a PMUv3 whose PMCR.N is 0. Signed-off-by: Akihiko Odaki --- .../selftests/kvm/arm64/vpmu_counter_access.c | 148 ++++++++++++++++-= ---- 1 file changed, 118 insertions(+), 30 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 f16b3b27e32ed7ca57481f27d689d47783aa0345..8d91d8017aea6a190be4a11e5ab= edb3324d93989 100644 --- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c +++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c @@ -409,16 +409,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, - }; - struct kvm_device_attr init_attr =3D { - .group =3D KVM_ARM_VCPU_PMU_V3_CTRL, - .attr =3D KVM_ARM_VCPU_PMU_V3_INIT, - }; + uint64_t dfr0; =20 /* The test creates the vpmu_vm multiple times. Ensure a clean state */ memset(&vpmu_vm, 0, sizeof(vpmu_vm)); @@ -445,10 +436,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); - - /* Initialize vPMU */ - vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &irq_attr); - vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &init_attr); } =20 static void destroy_vpmu_vm(void) @@ -475,14 +462,26 @@ static void run_vcpu(struct kvm_vcpu *vcpu, uint64_t = pmcr_n) } } =20 -static void test_create_vpmu_vm_with_pmcr_n(uint64_t pmcr_n, bool expect_f= ail) +static void test_init_vpmu_vm_with_pmcr_n(uint64_t pmcr_n, bool compositio= n, + bool expect_fail) { struct kvm_vcpu *vcpu; uint64_t pmcr, pmcr_orig; + uint64_t irq =3D 23; =20 create_vpmu_vm(guest_code); vcpu =3D vpmu_vm.vcpu; =20 + if (composition) + vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_COMPOSITION, NULL); + + vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_IRQ, &irq); + + vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_INIT, NULL); + pmcr_orig =3D vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0)); pmcr =3D pmcr_orig; =20 @@ -508,15 +507,15 @@ static void test_create_vpmu_vm_with_pmcr_n(uint64_t = pmcr_n, bool expect_fail) * 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 composition) { 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, composition %d\n", pmcr_n, composition); =20 - test_create_vpmu_vm_with_pmcr_n(pmcr_n, false); + test_init_vpmu_vm_with_pmcr_n(pmcr_n, composition, false); vcpu =3D vpmu_vm.vcpu; =20 /* Save the initial sp to restore them later to run the guest again */ @@ -550,14 +549,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 composition) { 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_pmcr_n(pmcr_n, false); + test_init_vpmu_vm_with_pmcr_n(pmcr_n, composition, false); vcpu =3D vpmu_vm.vcpu; =20 valid_counters_mask =3D get_counters_mask(pmcr_n); @@ -607,11 +606,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 composition) { pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n); =20 - test_create_vpmu_vm_with_pmcr_n(pmcr_n, true); + test_init_vpmu_vm_with_pmcr_n(pmcr_n, composition, true); destroy_vpmu_vm(); } =20 @@ -629,20 +628,109 @@ static uint64_t get_pmcr_n_limit(void) return get_pmcr_n(pmcr); } =20 -int main(void) +static void test_config(uint64_t pmcr_n, bool composition) { - uint64_t i, pmcr_n; - - TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3)); + 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, composition); + run_pmregs_validity_test(i, composition); } =20 for (i =3D pmcr_n + 1; i < ARMV8_PMU_MAX_COUNTERS; i++) - run_error_test(i); + run_error_test(i, composition); +} + +static void test_composition(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; + + 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_COMPOSITION); + 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 composition is unset at initialization. */ + ret =3D __vcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_COMPOSITION, NULL); + TEST_ASSERT(ret =3D=3D -1 && errno =3D=3D ENXIO, + KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); + + /* Assert that setting composition succeeds. */ + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_COMPOSITION, NULL); + + /* Assert that getting composition succeeds. */ + vcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_COMPOSITION, NULL); + + /* + * Assert that setting composition 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_COMPOSITION, NULL); + + destroy_vpmu_vm(); + + create_vpmu_vm(guest_code); + + /* + * Assert that setting composition results in EBUSY if an event filter + * has already been set while composition 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_COMPOSITION, 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 composition 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); + 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_COMPOSITION, 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_config(get_pmcr_n_limit(), false); + test_composition(); =20 return 0; } --=20 2.50.1