From: Fuad Tabba <fuad.tabba@linux.dev>
To: Marc Zyngier <maz@kernel.org>, Oliver Upton <oupton@kernel.org>
Cc: Fuad Tabba <tabba@google.com>, Joey Gouly <joey.gouly@arm.com>,
Steffen Eiden <seiden@linux.ibm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Zenghui Yu <yuzenghui@huawei.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>,
Peter Maydell <peter.maydell@linaro.org>,
Mark Brown <broonie@kernel.org>,
linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
linux-kernel@vger.kernel.org
Subject: [PATCH v2] KVM: arm64: selftests: Check ID regs are immutable after a failed run
Date: Wed, 5 Aug 2026 07:47:40 +0100 [thread overview]
Message-ID: <20260805064740.3013538-1-fuad.tabba@linux.dev> (raw)
Add a set_id_regs case covering ID register immutability when a vCPU's
first KVM_RUN fails after finalization but before
KVM_ARCH_FLAG_HAS_RAN_ONCE is set. The test provokes such a failure with
a PMUv3-enabled vCPU whose PMU is left uninitialized, then checks that
KVM_SET_ONE_REG on the feature and implementation ID registers, and
KVM_CREATE_DEVICE for a vGIC, are all rejected with -EBUSY.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
Notes:
This exercises the bug fixed by the KVM/arm64 ID register finalisation
series and fails without it, so it should be applied on top of that
series:
https://lore.kernel.org/r/20260803-kvm-arm64-idreg-final-v2-0-d7d7e4efc640@kernel.org
Changes since v1:
- Cover the VM-wide implementation ID registers (MIDR_EL1, REVIDR_EL1,
AIDR_EL1) as well as the feature ID registers, per Mark's review.
- Check every feature ID field that could still be lowered before
finalization, rather than only the first one found.
- Report the skip under the same name as the pass, with the reason as a
ksft_print_msg() diagnostic, so automation can match results for this
test across runs.
.../testing/selftests/kvm/arm64/set_id_regs.c | 106 +++++++++++++++++-
1 file changed, 105 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c
index 7429a1055df56..10849d21c0dd2 100644
--- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
@@ -13,6 +13,7 @@
#include "kvm_util.h"
#include "processor.h"
#include "test_util.h"
+#include "vgic.h"
#include <linux/bitfield.h>
enum ftr_type {
@@ -803,6 +804,107 @@ static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
ksft_test_result_pass("%s\n", __func__);
}
+/*
+ * ID registers must stay immutable even when a vCPU's first KVM_RUN fails
+ * after finalization but before KVM_ARCH_FLAG_HAS_RAN_ONCE is set.
+ */
+static void test_idreg_frozen_after_failed_run(void)
+{
+ static const u32 imp_id_regs[] = {
+ SYS_MIDR_EL1,
+ SYS_REVIDR_EL1,
+ SYS_AIDR_EL1,
+ };
+ struct kvm_vcpu_init init;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ int r;
+
+ if (!kvm_has_cap(KVM_CAP_ARM_PMU_V3)) {
+ ksft_print_msg("PMUv3 unsupported, cannot fail the first run\n");
+ ksft_test_result_skip("%s\n", __func__);
+ return;
+ }
+
+ /* Skip the default vGIC so the KVM_CREATE_DEVICE gate is reachable. */
+ test_disable_default_vgic();
+
+ vm = vm_create(1);
+ vm_enable_cap(vm, KVM_CAP_ARM_WRITABLE_IMP_ID_REGS, 0);
+ kvm_get_default_vcpu_target(vm, &init);
+ init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3);
+ vcpu = aarch64_vcpu_add(vm, 0, &init, guest_code);
+ kvm_arch_vm_finalize_vcpus(vm);
+
+ /*
+ * A PMUv3 vCPU left without PMU init is rejected by
+ * kvm_arm_pmu_v3_enable(), which runs after sysreg finalization.
+ */
+ r = _vcpu_run(vcpu);
+ TEST_ASSERT(r < 0 && errno == EINVAL,
+ "first KVM_RUN should fail post-finalization: r=%d errno=%d",
+ r, errno);
+
+ /*
+ * Feature ID registers: use values that would have been accepted before
+ * finalization, so that a rejection means the registers are final
+ * rather than the value being invalid.
+ */
+ for (int i = 0; i < ARRAY_SIZE(test_regs); i++) {
+ const struct reg_ftr_bits *ftr_bits = test_regs[i].ftr_bits;
+ u64 reg = KVM_ARM64_SYS_REG(test_regs[i].reg);
+ u64 val = vcpu_get_reg(vcpu, reg);
+
+ for (int j = 0; ftr_bits[j].type != FTR_END; j++) {
+ u64 ftr = (val & ftr_bits[j].mask) >> ftr_bits[j].shift;
+ u64 safe = get_safe_value(&ftr_bits[j], ftr);
+ u64 new_val;
+
+ if (safe == ftr)
+ continue;
+
+ new_val = (val & ~ftr_bits[j].mask) |
+ (safe << ftr_bits[j].shift);
+
+ r = __vcpu_set_reg(vcpu, reg, new_val);
+ TEST_ASSERT(r < 0 && errno == EBUSY,
+ "%s write after failed first run: r=%d errno=%d",
+ ftr_bits[j].name, r, errno);
+ TEST_ASSERT_EQ(vcpu_get_reg(vcpu, reg), val);
+ }
+
+ /* A write matching the finalized value is still accepted. */
+ vcpu_set_reg(vcpu, reg, val);
+ }
+
+ /*
+ * The VM-wide implementation ID registers are gated separately. Bit 0
+ * is within the writable mask of all three, so flipping it is a change
+ * KVM would otherwise accept.
+ */
+ for (int i = 0; i < ARRAY_SIZE(imp_id_regs); i++) {
+ u64 reg = KVM_ARM64_SYS_REG(imp_id_regs[i]);
+ u64 val = vcpu_get_reg(vcpu, reg);
+
+ r = __vcpu_set_reg(vcpu, reg, val ^ 1);
+ TEST_ASSERT(r < 0 && errno == EBUSY,
+ "implementation ID reg write after failed first run: r=%d errno=%d",
+ r, errno);
+ TEST_ASSERT_EQ(vcpu_get_reg(vcpu, reg), val);
+ }
+
+ /* Creating an in-kernel irqchip would change the ID registers too. */
+ if (kvm_supports_vgic_v3()) {
+ r = __kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);
+ TEST_ASSERT(r < 0 && errno == EBUSY,
+ "vGIC creation after failed first run: r=%d errno=%d",
+ r, errno);
+ }
+
+ kvm_vm_free(vm);
+ ksft_test_result_pass("%s\n", __func__);
+}
+
int main(void)
{
struct kvm_vcpu *vcpu;
@@ -828,7 +930,7 @@ int main(void)
ksft_print_header();
- test_cnt = 3 + MPAM_IDREG_TEST + MTE_IDREG_TEST;
+ test_cnt = 4 + MPAM_IDREG_TEST + MTE_IDREG_TEST;
for (i = 0; i < ARRAY_SIZE(test_regs); i++)
for (j = 0; test_regs[i].ftr_bits[j].type != FTR_END; j++)
test_cnt++;
@@ -847,5 +949,7 @@ int main(void)
kvm_vm_free(vm);
+ test_idreg_frozen_after_failed_run();
+
ksft_finished();
}
--
2.39.5
next reply other threads:[~2026-08-05 6:48 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-05 6:47 Fuad Tabba [this message]
2026-08-05 14:15 ` [PATCH v2] KVM: arm64: selftests: Check ID regs are immutable after a failed run Mark Brown
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=20260805064740.3013538-1-fuad.tabba@linux.dev \
--to=fuad.tabba@linux.dev \
--cc=broonie@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=joey.gouly@arm.com \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maz@kernel.org \
--cc=oupton@kernel.org \
--cc=peter.maydell@linaro.org \
--cc=seiden@linux.ibm.com \
--cc=suzuki.poulose@arm.com \
--cc=tabba@google.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