KVM: arm64: selftests: Check ID regs are immutable after a failed run

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: LLM
[Fixed for latest tag standard -- broonie]
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
Link: https://patch.msgid.link/20260805064740.3013538-1-fuad.tabba@linux.dev
Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Tested-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
Acked-by: Mark Brown <broonie@kernel.org>
Reviewed-by: Oliver Upton <oupton@kernel.org>
Link: https://patch.msgid.link/20260929-kvm-arm64-idreg-final-v4-3-75296dde994d@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
Fuad Tabba 2026-09-29 12:58:00 +01:00 committed by Marc Zyngier
parent 2ce80f6240
commit 00e3099338

View File

@ -13,6 +13,7 @@
#include "kvm_util.h" #include "kvm_util.h"
#include "processor.h" #include "processor.h"
#include "test_util.h" #include "test_util.h"
#include "vgic.h"
#include <linux/bitfield.h> #include <linux/bitfield.h>
enum ftr_type { 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__); 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) int main(void)
{ {
struct kvm_vcpu *vcpu; struct kvm_vcpu *vcpu;
@ -828,7 +930,7 @@ int main(void)
ksft_print_header(); 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 (i = 0; i < ARRAY_SIZE(test_regs); i++)
for (j = 0; test_regs[i].ftr_bits[j].type != FTR_END; j++) for (j = 0; test_regs[i].ftr_bits[j].type != FTR_END; j++)
test_cnt++; test_cnt++;
@ -847,5 +949,7 @@ int main(void)
kvm_vm_free(vm); kvm_vm_free(vm);
test_idreg_frozen_after_failed_run();
ksft_finished(); ksft_finished();
} }