From nobody Mon Jun 15 15:26: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 990C132FA2B; Sat, 11 Apr 2026 10:21:56 +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=1775902918; cv=none; b=MGJF0jzR/YlV/JTuuWjkU4rfeuTitkKsxMwXQW1zA5QYFuX8F4b2GxZ0k4Mocru87Ljca7YeXi3d4epeJxzLHEp6ICP4akgYin0284vfvZt+L0gN808CrmXjfuh/vu0rOTSI6lyG6RiOMckFd8Av4VoP/9ClfqqMzrA+gEZd6ac= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775902918; c=relaxed/simple; bh=6pigEJb6MjU5WOzv3cHoV2MRbx3gnmposFMAjl091HA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=h/gkpZhtR9jfbBcd6HZCZkNBJaLOeYB/4xuH2R0eZXuazmA28IahC8yu/VVH5Ih003iQSaMeYjQPF/GsksJ2A+iHQNcJALivlPZhcB8XKKFuMQY5vrV7przaiIoY6vAZ3EcEDDwrcQ75nLQ368m0Yxp6buQvHXcrSVGT48W+nuI= 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=c75er+X1 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="c75er+X1" 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 63BAKqRm021769 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Sat, 11 Apr 2026 19:20:54 +0900 (JST) (envelope-from odaki@rsg.ci.i.u-tokyo.ac.jp) DKIM-Signature: a=rsa-sha256; bh=Vpu164nWx4WQH4ej/oGZKppmfZaoPsbjbattGAiYgSg=; c=relaxed/relaxed; d=rsg.ci.i.u-tokyo.ac.jp; h=From:Message-Id:To:Subject:Date; s=rs20250326; t=1775902854; v=1; b=c75er+X1PN0maGfvFJV0Xzne9R8sgCDVB0XeimikaL1sSEvbBwGutr8clOtXdlAw TkMfT7vUMiCqIH4Ml2EPI7QtxHCI+kRWwfOM+wsS5flxsVEpEZ2u6gYzRq0LMoNT NE9AK78FRA9d5xYYlX2MJRhFiS6MYFLizs31r2pFn843NybjsU5xFD8N6Oslw79v OhPPndiX+4msj5LN/9Fj6Z+2xrleK4WTVn3zCF3hx0kN06MGA/yqas1ACAFW5gk8 d3OHYNBlRazm5mXALqW8P38m15oW2eyOzlQl0WtWKY6weehLBfxvNwdsngZu423P 6NPflQcOqaPRQ71k0aLzoA== From: Akihiko Odaki Date: Sat, 11 Apr 2026 19:20:31 +0900 Subject: [PATCH v5 1/4] KVM: arm64: PMU: Add kvm_pmu_enabled_counter_mask() 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: <20260411-hybrid-v5-1-b043b4d9f49e@rsg.ci.i.u-tokyo.ac.jp> References: <20260411-hybrid-v5-0-b043b4d9f49e@rsg.ci.i.u-tokyo.ac.jp> In-Reply-To: <20260411-hybrid-v5-0-b043b4d9f49e@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 This function will be useful to enumerate enabled counters. Signed-off-by: Akihiko Odaki --- arch/arm64/kvm/pmu-emul.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index b03dbda7f1ab..59ec96e09321 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -619,18 +619,24 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 v= al) } } =20 -static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc) +static u64 kvm_pmu_enabled_counter_mask(struct kvm_vcpu *vcpu) { - struct kvm_vcpu *vcpu =3D kvm_pmc_to_vcpu(pmc); - unsigned int mdcr =3D __vcpu_sys_reg(vcpu, MDCR_EL2); + u64 mask =3D 0; =20 - if (!(__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & BIT(pmc->idx))) - return false; + if (__vcpu_sys_reg(vcpu, MDCR_EL2) & MDCR_EL2_HPME) + mask |=3D kvm_pmu_hyp_counter_mask(vcpu); =20 - if (kvm_pmu_counter_is_hyp(vcpu, pmc->idx)) - return mdcr & MDCR_EL2_HPME; + if (kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E) + mask |=3D ~kvm_pmu_hyp_counter_mask(vcpu); + + return __vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & mask; +} + +static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc) +{ + struct kvm_vcpu *vcpu =3D kvm_pmc_to_vcpu(pmc); =20 - return kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E; + return kvm_pmu_enabled_counter_mask(vcpu) & BIT(pmc->idx); } =20 static bool kvm_pmc_counts_at_el0(struct kvm_pmc *pmc) --=20 2.53.0 From nobody Mon Jun 15 15:26: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 89E102FE05C; Sat, 11 Apr 2026 10:21:56 +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=1775902917; cv=none; b=A1TT1hFKmWO/ve/fSCbToFc39B8F2i4hUjhEUhwVXlnofBupKavI50Q1QK2er259gcSjuoUskxbVo9AcDoJ0E1TWD7yiEq/kwm2sXAO+OfjUTAAC4u57xeKmGJLPFh/7eSfQSVU096ulHLVJ9rIVhw7sl2Uw0CTXk2P2CjnyeoM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775902917; c=relaxed/simple; bh=iJr5toA1/CsmmyI6M1AbODpx/FMJj4ZMpGVZAMCOEe4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EUNiuPjKWPe/viHj84+NHrw/asztVtBddpFXhHgk4fo2BeBKc79zY3rxVijuajY1hgghSNMBvxpjTmAwwwRmg3TOaqDa0yTGUswL6JlUTWkMw8DlwbbWNWzXVzV66SWk9F3/LmCSPPLUzVecyxPMwyWu9JknTd7XjEqQEg7HPes= 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=eBwZ9bmS 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="eBwZ9bmS" 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 63BAKqRn021769 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Sat, 11 Apr 2026 19:20:54 +0900 (JST) (envelope-from odaki@rsg.ci.i.u-tokyo.ac.jp) DKIM-Signature: a=rsa-sha256; bh=y+sZrQAT5sl0rgEx/XygAhgEwYefEMRbED57gjaRUBQ=; c=relaxed/relaxed; d=rsg.ci.i.u-tokyo.ac.jp; h=From:Message-Id:To:Subject:Date; s=rs20250326; t=1775902854; v=1; b=eBwZ9bmSEJ3hssZAy5qEznlB8PZOsrIghCYflCCIVJeFSkgGZhCI41obXXYM5/jN 9BkfkJPjYdoB2c5U2tO8HXiTcirs6bdmLTs07SMw/gWa2CKNAJALQDzOkOvGf5s+ hLdKwsfSjiLJ1okOaNnfEK/DsGe6ZokKoGhbq19CP4QFZ8jQHT7gxr7b1ngFCYCe HyForcf6D1f8EtbvTPDX8csAeClgtjPn7OgNQFzG3Ok5So8QAdQ8gP5wO25JmTXz eD56K1fp5cBBcbo9/h95qiMN/Pl6a8h7JNGTYdEvRo77pmYwF/blNjx+15y1Vu4e Tsl+6ytd04/ye4TRlmPpAQ== From: Akihiko Odaki Date: Sat, 11 Apr 2026 19:20:32 +0900 Subject: [PATCH v5 2/4] KVM: arm64: PMU: Protect the list of PMUs with RCU 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: <20260411-hybrid-v5-2-b043b4d9f49e@rsg.ci.i.u-tokyo.ac.jp> References: <20260411-hybrid-v5-0-b043b4d9f49e@rsg.ci.i.u-tokyo.ac.jp> In-Reply-To: <20260411-hybrid-v5-0-b043b4d9f49e@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 Convert the list of PMUs to a RCU-protected list that has primitives to avoid read-side contention. Signed-off-by: Akihiko Odaki --- arch/arm64/kvm/pmu-emul.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index 59ec96e09321..ef5140bbfe28 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -7,9 +7,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -26,7 +26,6 @@ static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pm= c); =20 bool kvm_supports_guest_pmuv3(void) { - guard(mutex)(&arm_pmus_lock); return !list_empty(&arm_pmus); } =20 @@ -808,7 +807,7 @@ void kvm_host_pmu_init(struct arm_pmu *pmu) return; =20 entry->arm_pmu =3D pmu; - list_add_tail(&entry->entry, &arm_pmus); + list_add_tail_rcu(&entry->entry, &arm_pmus); } =20 static struct arm_pmu *kvm_pmu_probe_armpmu(void) @@ -817,7 +816,7 @@ static struct arm_pmu *kvm_pmu_probe_armpmu(void) struct arm_pmu *pmu; int cpu; =20 - guard(mutex)(&arm_pmus_lock); + guard(rcu)(); =20 /* * It is safe to use a stale cpu to iterate the list of PMUs so long as @@ -837,7 +836,7 @@ static struct arm_pmu *kvm_pmu_probe_armpmu(void) * carried here. */ cpu =3D raw_smp_processor_id(); - list_for_each_entry(entry, &arm_pmus, entry) { + list_for_each_entry_rcu(entry, &arm_pmus, entry) { pmu =3D entry->arm_pmu; =20 if (cpumask_test_cpu(cpu, &pmu->supported_cpus)) @@ -1088,9 +1087,9 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vc= pu, int pmu_id) int ret =3D -ENXIO; =20 lockdep_assert_held(&kvm->arch.config_lock); - mutex_lock(&arm_pmus_lock); + guard(rcu)(); =20 - list_for_each_entry(entry, &arm_pmus, entry) { + list_for_each_entry_rcu(entry, &arm_pmus, entry) { arm_pmu =3D entry->arm_pmu; if (arm_pmu->pmu.type =3D=3D pmu_id) { if (kvm_vm_has_ran_once(kvm) || @@ -1106,7 +1105,6 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vc= pu, int pmu_id) } } =20 - mutex_unlock(&arm_pmus_lock); return ret; } =20 --=20 2.53.0 From nobody Mon Jun 15 15:26: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 4E5F733030F; Sat, 11 Apr 2026 10:21:57 +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=1775902919; cv=none; b=TFvk7kaNatCheSqebRy+CMrUYivKCr/Em8QlxCoSdRkEfrYkutiLhLS6xfX4pi/RolqV1vZ00tgRChQzZx59flB3ULHGCZSS4aXzq7BTC/ud7/HsZentbjLjHAInOb7I1xQDQF5tUOQuDaqUq8Sox0wTpMgCfLGR4Dxv2Sl2q1k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775902919; c=relaxed/simple; bh=QsQBe4TSNg/OtIC2BHy8I327OL4X7vYE1pUw82WLWz4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Hgkqy5FX5A4uDlEAjRGPXQx+zK95cO4k5YZZ5vv1asYtl/8HWIUrgOlHK4nvOQxuwmxMw42xCbePxdt5LV5XRa8ksNWkmzd4XCG6IWOsws9RFyHplAwd1ihVAPj9BqfEHzgDTl/nPAFXY5Z/kfnyHXkgaPsFBIkMkyJEKQqUnPo= 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=aixtJHo2 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="aixtJHo2" 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 63BAKqRo021769 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Sat, 11 Apr 2026 19:20:55 +0900 (JST) (envelope-from odaki@rsg.ci.i.u-tokyo.ac.jp) DKIM-Signature: a=rsa-sha256; bh=kMYvdVvO0jaqgmBGYVkOXzM7RPnRmdpyNMgtU0GumNE=; c=relaxed/relaxed; d=rsg.ci.i.u-tokyo.ac.jp; h=From:Message-Id:To:Subject:Date; s=rs20250326; t=1775902855; v=1; b=aixtJHo2ypJaFaxPb33yKEVRw/QL4RtY2bvmdV4T75W79243941xPtcKg6yfS5eG 5XxH1RtMR+u9bxIyV251dESb7SF2vfVz4koRaNA1zuWwe7mFKP7kQP5u9bNGxjSG O1v0VKJynle5VkNHsjUFwiqyXFlwP+sTIe17CiW6OS8QNNOAGql+ajKuCOFit3U+ +/oQX7mV2qvTM3DuybJKDloWXRg7VUELX3yztH0PnPZLA7DDxWXkx6Cz2cstNwVE bMjX4MTStBGV0ejdZ2DNUcR6G03oLnsr31crRGM6o1f9MUTqoDBm2as2NX0UItJT w2mQ8fqKTFmWgkANkW/Z1w== From: Akihiko Odaki Date: Sat, 11 Apr 2026 19:20:33 +0900 Subject: [PATCH v5 3/4] KVM: arm64: PMU: Introduce 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: <20260411-hybrid-v5-3-b043b4d9f49e@rsg.ci.i.u-tokyo.ac.jp> References: <20260411-hybrid-v5-0-b043b4d9f49e@rsg.ci.i.u-tokyo.ac.jp> In-Reply-To: <20260411-hybrid-v5-0-b043b4d9f49e@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 On a heterogeneous arm64 system, KVM's PMU emulation is based on the features of a single host PMU instance. When a vCPU is migrated to a pCPU with an incompatible PMU, counters such as PMCCNTR_EL0 stop incrementing. Although this behavior is permitted by the architecture, Windows does not handle it gracefully and may crash with a division-by-zero error. The current workaround requires VMMs to pin vCPUs to a set of pCPUs that share a compatible PMU. This is difficult to implement correctly in QEMU/libvirt, where pinning occurs after vCPU initialization, and it also restricts the guest to a subset of available pCPUs. Introduce the KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY attribute to create a "fixed-counters-only" PMU. When set, KVM exposes a PMU that is compatible with all pCPUs but that does not support programmable event counters which may have different feature sets on different PMUs. This allows Windows guests to run reliably on heterogeneous systems without crashing, even without vCPU pinning, and enables VMMs to schedule vCPUs across all available pCPUs, making full use of the host hardware. Much like KVM_ARM_VCPU_PMU_V3_IRQ and other read-write attributes, this attribute provides a getter that facilitates kernel and userspace debugging/testing. Signed-off-by: Akihiko Odaki --- Documentation/virt/kvm/devices/vcpu.rst | 29 ++++++ arch/arm64/include/asm/kvm_host.h | 2 + arch/arm64/include/uapi/asm/kvm.h | 1 + arch/arm64/kvm/arm.c | 1 + arch/arm64/kvm/pmu-emul.c | 156 +++++++++++++++++++++++-----= ---- include/kvm/arm_pmu.h | 2 + 6 files changed, 148 insertions(+), 43 deletions(-) diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/k= vm/devices/vcpu.rst index 60bf205cb373..e0aeb1897d77 100644 --- a/Documentation/virt/kvm/devices/vcpu.rst +++ b/Documentation/virt/kvm/devices/vcpu.rst @@ -161,6 +161,35 @@ 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. =20 +1.6 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY +------------------------------------------------------ + +:Parameters: no additional parameter in kvm_device_attr.addr + +:Returns: + + =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + -EBUSY Attempted to set after initializing PMUv3 or running + VCPU, or attempted to set for the first time after + setting an event filter + -ENXIO Attempted to get before setting + -ENODEV Attempted to set while PMUv3 not supported + =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +If set, PMUv3 will be emulated without programmable event counters. The VC= PU +will use any compatible hardware PMU. This attribute is particularly usefu= l on +heterogeneous systems where different hardware PMUs cover different physic= al +CPUs. The compatibility of hardware PMUs can be checked with +KVM_ARM_VCPU_PMU_V3_SET_PMU. All VCPUs in a VM share this attribute. It is= n't +possible to set it for the first time if a PMU event filter is already pre= sent. + +Note that KVM will not make any attempts to run the VCPU on the physical C= PUs +with compatible hardware PMUs. This is entirely left to userspace. However, +attempting to run the VCPU on an unsupported CPU will fail and KVM_RUN will +return with exit_reason =3D KVM_EXIT_FAIL_ENTRY and populate the fail_entr= y struct +by setting hardware_entry_failure_reason field to +KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED and the cpu field to the processor id. + 2. GROUP: KVM_ARM_VCPU_TIMER_CTRL =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D =20 diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index 59f25b85be2b..b59e0182472c 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -353,6 +353,8 @@ struct kvm_arch { #define KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS 10 /* Unhandled SEAs are taken to userspace */ #define KVM_ARCH_FLAG_EXIT_SEA 11 + /* PMUv3 is emulated without progammable event counters */ +#define KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY 12 unsigned long flags; =20 /* VM-wide vCPU feature set */ diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/as= m/kvm.h index a792a599b9d6..474c84fa757f 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -436,6 +436,7 @@ enum { #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_PMU_V3_FIXED_COUNTERS_ONLY 5 #define KVM_ARM_VCPU_TIMER_CTRL 1 #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 620a465248d1..dca16ca26d32 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -634,6 +634,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) if (has_vhe()) kvm_vcpu_load_vhe(vcpu); kvm_arch_vcpu_load_fp(vcpu); + kvm_vcpu_load_pmu(vcpu); kvm_vcpu_pmu_restore_guest(vcpu); if (kvm_arm_is_pvtime_enabled(&vcpu->arch)) kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu); diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index ef5140bbfe28..c827e66af0a2 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -326,7 +326,10 @@ u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *= vcpu) =20 static void kvm_pmc_enable_perf_event(struct kvm_pmc *pmc) { - if (!pmc->perf_event) { + struct kvm_vcpu *vcpu =3D kvm_pmc_to_vcpu(pmc); + + if (!pmc->perf_event || + !cpumask_test_cpu(vcpu->cpu, &to_arm_pmu(pmc->perf_event->pmu)->suppo= rted_cpus)) { kvm_pmu_create_perf_event(pmc); return; } @@ -667,10 +670,8 @@ static bool kvm_pmc_counts_at_el2(struct kvm_pmc *pmc) return kvm_pmc_read_evtreg(pmc) & ARMV8_PMU_INCLUDE_EL2; } =20 -static int kvm_map_pmu_event(struct kvm *kvm, unsigned int eventsel) +static int kvm_map_pmu_event(struct arm_pmu *pmu, unsigned int eventsel) { - struct arm_pmu *pmu =3D kvm->arch.arm_pmu; - /* * The CPU PMU likely isn't PMUv3; let the driver provide a mapping * for the guest's PMUv3 event ID. @@ -681,6 +682,23 @@ static int kvm_map_pmu_event(struct kvm *kvm, unsigned= int eventsel) return eventsel; } =20 +static struct arm_pmu *kvm_pmu_probe_armpmu(int cpu) +{ + struct arm_pmu_entry *entry; + struct arm_pmu *pmu; + + guard(rcu)(); + + list_for_each_entry_rcu(entry, &arm_pmus, entry) { + pmu =3D entry->arm_pmu; + + if (cpumask_test_cpu(cpu, &pmu->supported_cpus)) + return pmu; + } + + return NULL; +} + /** * kvm_pmu_create_perf_event - create a perf event for a counter * @pmc: Counter context @@ -694,6 +712,12 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *= pmc) int eventsel; u64 evtreg; =20 + if (test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &vcpu->kvm->arch.f= lags)) { + arm_pmu =3D kvm_pmu_probe_armpmu(vcpu->cpu); + if (WARN_ON_ONCE(!arm_pmu)) + return; + } + evtreg =3D kvm_pmc_read_evtreg(pmc); =20 kvm_pmu_stop_counter(pmc); @@ -722,7 +746,7 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *p= mc) * Don't create an event if we're running on hardware that requires * PMUv3 event translation and we couldn't find a valid mapping. */ - eventsel =3D kvm_map_pmu_event(vcpu->kvm, eventsel); + eventsel =3D kvm_map_pmu_event(arm_pmu, eventsel); if (eventsel < 0) return; =20 @@ -810,42 +834,6 @@ void kvm_host_pmu_init(struct arm_pmu *pmu) list_add_tail_rcu(&entry->entry, &arm_pmus); } =20 -static struct arm_pmu *kvm_pmu_probe_armpmu(void) -{ - struct arm_pmu_entry *entry; - struct arm_pmu *pmu; - int cpu; - - guard(rcu)(); - - /* - * It is safe to use a stale cpu to iterate the list of PMUs so long as - * the same value is used for the entirety of the loop. Given this, and - * the fact that no percpu data is used for the lookup there is no need - * to disable preemption. - * - * It is still necessary to get a valid cpu, though, to probe for the - * default PMU instance as userspace is not required to specify a PMU - * type. In order to uphold the preexisting behavior KVM selects the - * PMU instance for the core during vcpu init. A dependent use - * case would be a user with disdain of all things big.LITTLE that - * affines the VMM to a particular cluster of cores. - * - * In any case, userspace should just do the sane thing and use the UAPI - * to select a PMU type directly. But, be wary of the baggage being - * carried here. - */ - cpu =3D raw_smp_processor_id(); - list_for_each_entry_rcu(entry, &arm_pmus, entry) { - pmu =3D entry->arm_pmu; - - if (cpumask_test_cpu(cpu, &pmu->supported_cpus)) - return pmu; - } - - return NULL; -} - static u64 __compute_pmceid(struct arm_pmu *pmu, bool pmceid1) { u32 hi[2], lo[2]; @@ -888,6 +876,9 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmce= id1) u64 val, mask =3D 0; int base, i, nr_events; =20 + if (test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &vcpu->kvm->arch.f= lags)) + return 0; + if (!pmceid1) { val =3D compute_pmceid0(cpu_pmu); base =3D 0; @@ -915,6 +906,26 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmc= eid1) return val & mask; } =20 +void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu) +{ + unsigned long mask =3D kvm_pmu_enabled_counter_mask(vcpu); + struct kvm_pmc *pmc; + struct arm_pmu *cpu_pmu; + int i; + + for_each_set_bit(i, &mask, 32) { + pmc =3D kvm_vcpu_idx_to_pmc(vcpu, i); + if (!pmc->perf_event) + continue; + + cpu_pmu =3D to_arm_pmu(pmc->perf_event->pmu); + if (!cpumask_test_cpu(vcpu->cpu, &cpu_pmu->supported_cpus)) { + kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu); + break; + } + } +} + void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) { u64 mask =3D kvm_pmu_implemented_counter_mask(vcpu); @@ -1016,6 +1027,9 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm) { struct arm_pmu *arm_pmu =3D kvm->arch.arm_pmu; =20 + if (test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &kvm->arch.flags)) + return 0; + /* * PMUv3 requires that all event counters are capable of counting any * event, though the same may not be true of non-PMUv3 hardware. @@ -1070,7 +1084,24 @@ static void kvm_arm_set_pmu(struct kvm *kvm, struct = arm_pmu *arm_pmu) */ int kvm_arm_set_default_pmu(struct kvm *kvm) { - struct arm_pmu *arm_pmu =3D kvm_pmu_probe_armpmu(); + /* + * It is safe to use a stale cpu to iterate the list of PMUs so long as + * the same value is used for the entirety of the loop. Given this, and + * the fact that no percpu data is used for the lookup there is no need + * to disable preemption. + * + * It is still necessary to get a valid cpu, though, to probe for the + * default PMU instance as userspace is not required to specify a PMU + * type. In order to uphold the preexisting behavior KVM selects the + * PMU instance for the core during vcpu init. A dependent use + * case would be a user with disdain of all things big.LITTLE that + * affines the VMM to a particular cluster of cores. + * + * In any case, userspace should just do the sane thing and use the UAPI + * to select a PMU type directly. But, be wary of the baggage being + * carried here. + */ + struct arm_pmu *arm_pmu =3D kvm_pmu_probe_armpmu(raw_smp_processor_id()); =20 if (!arm_pmu) return -ENODEV; @@ -1098,6 +1129,7 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vc= pu, int pmu_id) break; } =20 + clear_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &kvm->arch.flags); kvm_arm_set_pmu(kvm, arm_pmu); cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus); ret =3D 0; @@ -1108,11 +1140,42 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *= vcpu, int pmu_id) return ret; } =20 +static int kvm_arm_pmu_v3_set_pmu_fixed_counters_only(struct kvm_vcpu *vcp= u) +{ + struct kvm *kvm =3D vcpu->kvm; + struct arm_pmu_entry *entry; + struct arm_pmu *arm_pmu; + struct cpumask *supported_cpus =3D kvm->arch.supported_cpus; + + lockdep_assert_held(&kvm->arch.config_lock); + + if (kvm_vm_has_ran_once(kvm) || + (kvm->arch.pmu_filter && + !test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &kvm->arch.flags= ))) + return -EBUSY; + + set_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &kvm->arch.flags); + kvm_arm_set_nr_counters(kvm, 0); + cpumask_clear(supported_cpus); + + guard(rcu)(); + + list_for_each_entry_rcu(entry, &arm_pmus, entry) { + arm_pmu =3D entry->arm_pmu; + cpumask_or(supported_cpus, supported_cpus, &arm_pmu->supported_cpus); + } + + return 0; +} + static int kvm_arm_pmu_v3_set_nr_counters(struct kvm_vcpu *vcpu, unsigned = int n) { struct kvm *kvm =3D vcpu->kvm; =20 - if (!kvm->arch.arm_pmu) + lockdep_assert_held(&kvm->arch.config_lock); + + if (!kvm->arch.arm_pmu && + !test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &kvm->arch.flags)) return -EINVAL; =20 if (n > kvm_arm_pmu_get_max_counters(kvm)) @@ -1227,6 +1290,8 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, st= ruct kvm_device_attr *attr) =20 return kvm_arm_pmu_v3_set_nr_counters(vcpu, n); } + case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY: + return kvm_arm_pmu_v3_set_pmu_fixed_counters_only(vcpu); case KVM_ARM_VCPU_PMU_V3_INIT: return kvm_arm_pmu_v3_init(vcpu); } @@ -1253,6 +1318,10 @@ int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, s= truct kvm_device_attr *attr) irq =3D vcpu->arch.pmu.irq_num; return put_user(irq, uaddr); } + case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY: + lockdep_assert_held(&vcpu->kvm->arch.config_lock); + if (test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &vcpu->kvm->arch.= flags)) + return 0; } =20 return -ENXIO; @@ -1266,6 +1335,7 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, st= ruct kvm_device_attr *attr) case KVM_ARM_VCPU_PMU_V3_FILTER: case KVM_ARM_VCPU_PMU_V3_SET_PMU: case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS: + case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY: if (kvm_vcpu_has_pmu(vcpu)) return 0; } diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 96754b51b411..1375cbaf97b2 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -56,6 +56,7 @@ void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u6= 4 val); void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val); void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data, u64 select_idx); +void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu); void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu); int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr); @@ -161,6 +162,7 @@ static inline u64 kvm_pmu_get_pmceid(struct kvm_vcpu *v= cpu, bool pmceid1) static inline void kvm_pmu_update_vcpu_events(struct kvm_vcpu *vcpu) {} static inline void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) {} static inline void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) {} +static inline void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu) {} static inline void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) {} static inline u8 kvm_arm_pmu_get_pmuver_limit(void) { --=20 2.53.0 From nobody Mon Jun 15 15:26: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 89D952E542C; Sat, 11 Apr 2026 10:21:56 +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=1775902918; cv=none; b=b+XICFMyjtdWODBvWAxOxkX/c0piI+jaBq+4TRH1QagnWOPHUuDYP7qE92VOeDPOmEBg8oEEvqXwBlIM7Rn8TUAAMS7/y2JNluKdUW3KxtoOQm+42CLrNp/8D+a2MF9BA+FBmGiKP0TDo44e5nMXx3FBZcDFkTOoRXDe1t8ZlEs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775902918; c=relaxed/simple; bh=UeLeJJYv4MrHqdVFeHaI77Z9WC9EDg0U1CEhgi0gzQ8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YHdO/pRu41jGb828DD6MAnHZERLD+ZnsWjRQ6rftc6nUzErWfQu7Tfu/ADwFZXXGthj/123+TnPzINNtvlhbwFLXUXP+cDCAOcO3M6DTdtY+aty1EQpsUmwKzVxY0aIgoroFo0tmUd0IUALCAZlVD7P48wNHO0xZdvSqJRP+wzM= 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=HXMlrDXF 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="HXMlrDXF" 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 63BAKqRp021769 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Sat, 11 Apr 2026 19:20:55 +0900 (JST) (envelope-from odaki@rsg.ci.i.u-tokyo.ac.jp) DKIM-Signature: a=rsa-sha256; bh=291jTJXEz1ehfeP+LRZyKOCmsrXtX0eb9mRinpxbDvA=; c=relaxed/relaxed; d=rsg.ci.i.u-tokyo.ac.jp; h=From:Message-Id:To:Subject:Date; s=rs20250326; t=1775902855; v=1; b=HXMlrDXFLN8Pr3Khujc4ktEPI4No10Sktig1+J0gbx1sz6oIgtAQVBphXSO7uQo5 OqJWK4KYw8il6xPCbnKQrm52srjOFs4YunQIHqpXqI4KMIgP5/o1s5Z4x3EwShlV LkzbGfxn/KkYMSp9WixUlKUEQtTWwMrxh70lZiMuxnS+Ar84Lj64oxNB330qUxjP qvL0jScpyIbR98rflz9UVLDJX3LRJz/FIlNbFxFlSk4RTxcYtQi1D+nfVulQ+zxe z4t43zFSpDUsJfWACV3tXQSynDi6qqaTwExwvYB0/sFgngDCvit/hpOru3k4Fy7w W71BdBgJh2IqlyVU2bk1+w== From: Akihiko Odaki Date: Sat, 11 Apr 2026 19:20:34 +0900 Subject: [PATCH v5 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: <20260411-hybrid-v5-4-b043b4d9f49e@rsg.ci.i.u-tokyo.ac.jp> References: <20260411-hybrid-v5-0-b043b4d9f49e@rsg.ci.i.u-tokyo.ac.jp> In-Reply-To: <20260411-hybrid-v5-0-b043b4d9f49e@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..50189fb56374 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(running_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