From: Congkai Tan <congkai@amazon.com>
To: Oliver Upton <oupton@kernel.org>, <kvmarm@lists.linux.dev>,
<linux-arm-kernel@lists.infradead.org>
Cc: Congkai Tan <congkai@amazon.com>, Marc Zyngier <maz@kernel.org>,
"Joey Gouly" <joey.gouly@arm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Zenghui Yu <yuzenghui@huawei.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>,
Paolo Bonzini <pbonzini@redhat.com>,
"Jonathan Corbet" <corbet@lwn.net>,
Haris Okanovic <harisokn@amazon.com>,
Geoff Blake <blakgeof@amazon.com>,
Stanislav Spassov <stanspas@amazon.de>, <kvm@vger.kernel.org>,
<linux-doc@vger.kernel.org>, <linux-kselftest@vger.kernel.org>,
<linux-kernel@vger.kernel.org>
Subject: [PATCH v2 2/3] KVM: arm64: Expose PMMIR_EL1.SLOTS under strict PMUv3 UAPI
Date: Thu, 2 Jul 2026 19:04:20 +0000 [thread overview]
Message-ID: <20260702190421.420992-3-congkai@amazon.com> (raw)
In-Reply-To: <20260702190421.420992-1-congkai@amazon.com>
Introduce a new field pmmir_slots in struct kvm_arch to store
PMMIR_EL1.SLOTS. It only saves the actual hardware PMU value when
the VMM explicitly selects a PMU under KVM_ARM_VCPU_PMU_V3_STRICT.
Otherwise, it stays 0 after allocation.
Use this field to implement guest access, userspace get, and userspace
set for PMMIR_EL1:
- access_pmmir(): uses the value in kvm->arch.pmmir_slots directly. If
the VMM selected a PMU and KVM_ARM_VCPU_PMU_V3_STRICT is set, the guest
can correctly read the underlying core's SLOTS. Otherwise, it continues
to read 0 since the true SLOTS value can be nondeterministic.
- get_pmmir(): same as access_pmmir().
- set_pmmir(): only the SLOTS field is writable; a value setting any
other bit is rejected with -EINVAL, since get_pmmir() returns SLOTS
zero-extended. A value of 0 resets kvm->arch.pmmir_slots to 0 for
backward compatibility, as the register is RAZ in older KVM, a value
matching the current SLOTS is accepted as a no-op, and anything else is
rejected with -EINVAL. Once the VM has run PMMIR_EL1 is immutable, so a
mismatching write then returns -EBUSY.
The register is now exposed via KVM_GET_REG_LIST for PMUv3 vCPUs, so add
it to the get-reg-list selftest's PMU register list.
Signed-off-by: Congkai Tan <congkai@amazon.com>
Reviewed-by: Geoff Blake <blakgeof@amazon.com>
Reviewed-by: Haris Okanovic <harisokn@amazon.com>
Reviewed-by: Stanislav Spassov <stanspas@amazon.de>
---
arch/arm64/include/asm/kvm_host.h | 3 +
arch/arm64/kvm/pmu-emul.c | 11 ++++
arch/arm64/kvm/sys_regs.c | 63 ++++++++++++++++++-
.../selftests/kvm/arm64/get-reg-list.c | 1 +
4 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index a6e33aaf400d..b896d6eef822 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -387,6 +387,9 @@ struct kvm_arch {
/* Maximum number of counters for the guest */
u8 nr_pmu_counters;
+ /* PMMIR_EL1.SLOTS value exposed to the guest. */
+ u8 pmmir_slots;
+
/* Hypercall features firmware registers' descriptor */
struct kvm_smccc_features smccc_feat;
struct maple_tree smccc_filter;
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 1f24169505a9..9595bce6519f 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -1117,6 +1117,17 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
kvm_arm_set_pmu(kvm, arm_pmu);
cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
+
+ /*
+ * Since a specific PMU is explicitly selected,
+ * PMMIR_EL1.SLOTS is deterministic to the guest.
+ * If KVM_ARM_VCPU_PMU_V3_STRICT is set, snapshot
+ * the value to allow the guest to read it.
+ */
+ if (kvm_vcpu_has_pmuv3_strict(vcpu))
+ kvm->arch.pmmir_slots =
+ FIELD_GET(ARMV8_PMU_SLOTS,
+ arm_pmu->reg_pmmir);
ret = 0;
break;
}
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 148fc3400ea8..edfbb8de1528 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1370,6 +1370,64 @@ static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
return true;
}
+static bool access_pmmir(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ if (p->is_write)
+ return write_to_read_only(vcpu, p, r);
+
+ /*
+ * If KVM_ARM_VCPU_PMU_V3_STRICT is set and PMU was explicitly
+ * selected, the underlying hardware SLOTS value was read into this
+ * field. Otherwise, it stays 0. All other PMMIR_EL1 fields are RAZ.
+ */
+ p->regval = FIELD_PREP(ARMV8_PMU_SLOTS, vcpu->kvm->arch.pmmir_slots);
+ return true;
+}
+
+static int get_pmmir(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
+ u64 *val)
+{
+ *val = FIELD_PREP(ARMV8_PMU_SLOTS, vcpu->kvm->arch.pmmir_slots);
+ return 0;
+}
+
+static int set_pmmir(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
+ u64 val)
+{
+ struct kvm *kvm = vcpu->kvm;
+ u8 slots = FIELD_GET(ARMV8_PMU_SLOTS, val);
+
+ /*
+ * Only the SLOTS field is exposed (get_pmmir returns just that field),
+ * so reject a write that sets any other bit rather than silently
+ * masking it.
+ */
+ if (val & ~(u64)ARMV8_PMU_SLOTS)
+ return -EINVAL;
+
+ guard(mutex)(&kvm->arch.config_lock);
+
+ /*
+ * Once the VM has started PMMIR_EL1 is immutable. Reject any write
+ * that does not match the current value.
+ */
+ if (kvm_vm_has_ran_once(kvm))
+ return slots == kvm->arch.pmmir_slots ? 0 : -EBUSY;
+
+ /*
+ * Only SLOTS = 0 is honored for backwards compatibility with the
+ * old RAZ behavior. Reject any non-zero write that does not match
+ * the current value.
+ */
+ if (!slots)
+ kvm->arch.pmmir_slots = 0;
+ else if (slots != kvm->arch.pmmir_slots)
+ return -EINVAL;
+
+ return 0;
+}
+
static bool access_pmovs(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
@@ -3456,7 +3514,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
{ PMU_SYS_REG(PMINTENCLR_EL1),
.access = access_pminten, .reg = PMINTENSET_EL1,
.get_user = get_pmreg, .set_user = set_pmreg },
- { SYS_DESC(SYS_PMMIR_EL1), trap_raz_wi },
+ { PMU_SYS_REG(PMMIR_EL1), .access = access_pmmir, .reset = NULL,
+ .get_user = get_pmmir, .set_user = set_pmmir },
{ SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 },
{ SYS_DESC(SYS_PIRE0_EL1), NULL, reset_unknown, PIRE0_EL1,
@@ -4600,7 +4659,7 @@ static const struct sys_reg_desc cp15_regs[] = {
{ CP15_PMU_SYS_REG(HI, 0, 9, 14, 4), .access = access_pmceid },
{ CP15_PMU_SYS_REG(HI, 0, 9, 14, 5), .access = access_pmceid },
/* PMMIR */
- { CP15_PMU_SYS_REG(DIRECT, 0, 9, 14, 6), .access = trap_raz_wi },
+ { CP15_PMU_SYS_REG(DIRECT, 0, 9, 14, 6), .access = access_pmmir },
/* PRRR/MAIR0 */
{ AA32(LO), Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, MAIR_EL1 },
diff --git a/tools/testing/selftests/kvm/arm64/get-reg-list.c b/tools/testing/selftests/kvm/arm64/get-reg-list.c
index 0a3a94c4cca1..cfa99979d57c 100644
--- a/tools/testing/selftests/kvm/arm64/get-reg-list.c
+++ b/tools/testing/selftests/kvm/arm64/get-reg-list.c
@@ -532,6 +532,7 @@ static __u64 base_regs[] = {
static __u64 pmu_regs[] = {
ARM64_SYS_REG(3, 0, 9, 14, 1), /* PMINTENSET_EL1 */
ARM64_SYS_REG(3, 0, 9, 14, 2), /* PMINTENCLR_EL1 */
+ ARM64_SYS_REG(3, 0, 9, 14, 6), /* PMMIR_EL1 */
ARM64_SYS_REG(3, 3, 9, 12, 0), /* PMCR_EL0 */
ARM64_SYS_REG(3, 3, 9, 12, 1), /* PMCNTENSET_EL0 */
ARM64_SYS_REG(3, 3, 9, 12, 2), /* PMCNTENCLR_EL0 */
--
2.50.1
next prev parent reply other threads:[~2026-07-02 19:04 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-02 19:04 [PATCH v2 0/3] KVM: arm64: Expose PMMIR_EL1.SLOTS to guests Congkai Tan
2026-07-02 19:04 ` [PATCH v2 1/3] KVM: arm64: Add KVM_ARM_VCPU_PMU_V3_STRICT vCPU feature Congkai Tan
2026-07-02 19:04 ` Congkai Tan [this message]
2026-07-02 19:04 ` [PATCH v2 3/3] KVM: arm64: Advertise STALL_SLOT* in PMCEID1 under strict PMUv3 UAPI Congkai Tan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260702190421.420992-3-congkai@amazon.com \
--to=congkai@amazon.com \
--cc=blakgeof@amazon.com \
--cc=catalin.marinas@arm.com \
--cc=corbet@lwn.net \
--cc=harisokn@amazon.com \
--cc=joey.gouly@arm.com \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=maz@kernel.org \
--cc=oupton@kernel.org \
--cc=pbonzini@redhat.com \
--cc=stanspas@amazon.de \
--cc=suzuki.poulose@arm.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox