* [PATCH v3 1/4] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests
2025-12-19 19:28 [PATCH v3 0/4] KVM: selftests: arm64: Improve diagnostics from set_id_regs Mark Brown
@ 2025-12-19 19:28 ` Mark Brown
2026-01-02 14:40 ` Ben Horgan
2025-12-19 19:28 ` [PATCH v3 2/4] KVM: selftests: arm64: Report register reset tests individually Mark Brown
` (2 subsequent siblings)
3 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2025-12-19 19:28 UTC (permalink / raw)
To: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
Shuah Khan, Oliver Upton
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel,
Mark Brown
Currently when we run guest code to validate that the values we wrote to
the registers are seen by the guest we assert that these values match using
a KVM selftests level assert, resulting in unclear diagnostics if the test
fails. Replace this assert with reporting a kselftest test per register.
In order to support getting the names of the registers we repaint the array
of ID_ registers to store the names and open code the rest.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/kvm/arm64/set_id_regs.c | 74 +++++++++++++++++++------
1 file changed, 57 insertions(+), 17 deletions(-)
diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c
index c4815d365816..84e9484a4899 100644
--- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
@@ -40,6 +40,7 @@ struct reg_ftr_bits {
};
struct test_feature_reg {
+ const char *name;
uint32_t reg;
const struct reg_ftr_bits *ftr_bits;
};
@@ -218,24 +219,25 @@ static const struct reg_ftr_bits ftr_id_aa64zfr0_el1[] = {
#define TEST_REG(id, table) \
{ \
- .reg = id, \
+ .name = #id, \
+ .reg = SYS_ ## id, \
.ftr_bits = &((table)[0]), \
}
static struct test_feature_reg test_regs[] = {
- TEST_REG(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_el1),
- TEST_REG(SYS_ID_DFR0_EL1, ftr_id_dfr0_el1),
- TEST_REG(SYS_ID_AA64ISAR0_EL1, ftr_id_aa64isar0_el1),
- TEST_REG(SYS_ID_AA64ISAR1_EL1, ftr_id_aa64isar1_el1),
- TEST_REG(SYS_ID_AA64ISAR2_EL1, ftr_id_aa64isar2_el1),
- TEST_REG(SYS_ID_AA64ISAR3_EL1, ftr_id_aa64isar3_el1),
- TEST_REG(SYS_ID_AA64PFR0_EL1, ftr_id_aa64pfr0_el1),
- TEST_REG(SYS_ID_AA64PFR1_EL1, ftr_id_aa64pfr1_el1),
- TEST_REG(SYS_ID_AA64MMFR0_EL1, ftr_id_aa64mmfr0_el1),
- TEST_REG(SYS_ID_AA64MMFR1_EL1, ftr_id_aa64mmfr1_el1),
- TEST_REG(SYS_ID_AA64MMFR2_EL1, ftr_id_aa64mmfr2_el1),
- TEST_REG(SYS_ID_AA64MMFR3_EL1, ftr_id_aa64mmfr3_el1),
- TEST_REG(SYS_ID_AA64ZFR0_EL1, ftr_id_aa64zfr0_el1),
+ TEST_REG(ID_AA64DFR0_EL1, ftr_id_aa64dfr0_el1),
+ TEST_REG(ID_DFR0_EL1, ftr_id_dfr0_el1),
+ TEST_REG(ID_AA64ISAR0_EL1, ftr_id_aa64isar0_el1),
+ TEST_REG(ID_AA64ISAR1_EL1, ftr_id_aa64isar1_el1),
+ TEST_REG(ID_AA64ISAR2_EL1, ftr_id_aa64isar2_el1),
+ TEST_REG(ID_AA64ISAR3_EL1, ftr_id_aa64isar3_el1),
+ TEST_REG(ID_AA64PFR0_EL1, ftr_id_aa64pfr0_el1),
+ TEST_REG(ID_AA64PFR1_EL1, ftr_id_aa64pfr1_el1),
+ TEST_REG(ID_AA64MMFR0_EL1, ftr_id_aa64mmfr0_el1),
+ TEST_REG(ID_AA64MMFR1_EL1, ftr_id_aa64mmfr1_el1),
+ TEST_REG(ID_AA64MMFR2_EL1, ftr_id_aa64mmfr2_el1),
+ TEST_REG(ID_AA64MMFR3_EL1, ftr_id_aa64mmfr3_el1),
+ TEST_REG(ID_AA64ZFR0_EL1, ftr_id_aa64zfr0_el1),
};
#define GUEST_REG_SYNC(id) GUEST_SYNC_ARGS(0, id, read_sysreg_s(id), 0, 0);
@@ -265,6 +267,34 @@ static void guest_code(void)
GUEST_DONE();
}
+#define GUEST_READ_TEST (ARRAY_SIZE(test_regs) + 6)
+
+static const char *get_reg_name(u64 id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(test_regs); i++)
+ if (test_regs[i].reg == id)
+ return test_regs[i].name;
+
+ switch (id) {
+ case SYS_MPIDR_EL1:
+ return "MPIDR_EL1";
+ case SYS_CLIDR_EL1:
+ return "CLIDR_EL1";
+ case SYS_CTR_EL0:
+ return "CTR_EL0";
+ case SYS_MIDR_EL1:
+ return "MIDR_EL1";
+ case SYS_REVIDR_EL1:
+ return "REVIDR_EL1";
+ case SYS_AIDR_EL1:
+ return "AIDR_EL1";
+ default:
+ TEST_FAIL("Unknown register");
+ }
+}
+
/* Return a safe value to a given ftr_bits an ftr value */
uint64_t get_safe_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr)
{
@@ -639,6 +669,8 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
{
bool done = false;
struct ucall uc;
+ uint64_t reg_id, expected_val, guest_val;
+ bool match;
while (!done) {
vcpu_run(vcpu);
@@ -649,8 +681,16 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
break;
case UCALL_SYNC:
/* Make sure the written values are seen by guest */
- TEST_ASSERT_EQ(test_reg_vals[encoding_to_range_idx(uc.args[2])],
- uc.args[3]);
+ reg_id = uc.args[2];
+ guest_val = uc.args[3];
+ expected_val = test_reg_vals[encoding_to_range_idx(reg_id)];
+ match = expected_val == guest_val;
+ if (!match)
+ ksft_print_msg("%lx != %lx\n",
+ expected_val, guest_val);
+ ksft_test_result(match,
+ "%s value seen in guest\n",
+ get_reg_name(reg_id));
break;
case UCALL_DONE:
done = true;
@@ -790,7 +830,7 @@ int main(void)
ksft_print_header();
- test_cnt = 3 + MPAM_IDREG_TEST + MTE_IDREG_TEST;
+ test_cnt = 3 + MPAM_IDREG_TEST + MTE_IDREG_TEST + GUEST_READ_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++;
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 1/4] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests
2025-12-19 19:28 ` [PATCH v3 1/4] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests Mark Brown
@ 2026-01-02 14:40 ` Ben Horgan
0 siblings, 0 replies; 12+ messages in thread
From: Ben Horgan @ 2026-01-02 14:40 UTC (permalink / raw)
To: Mark Brown, Marc Zyngier, Joey Gouly, Suzuki K Poulose,
Paolo Bonzini, Shuah Khan, Oliver Upton
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel
Hi Mark,
On 12/19/25 19:28, Mark Brown wrote:
> Currently when we run guest code to validate that the values we wrote to
> the registers are seen by the guest we assert that these values match using
> a KVM selftests level assert, resulting in unclear diagnostics if the test
> fails. Replace this assert with reporting a kselftest test per register.
>
> In order to support getting the names of the registers we repaint the array
> of ID_ registers to store the names and open code the rest.
>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
> tools/testing/selftests/kvm/arm64/set_id_regs.c | 74 +++++++++++++++++++------
> 1 file changed, 57 insertions(+), 17 deletions(-)
>
Looks like an improvement to me.
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Thanks,
Ben
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 2/4] KVM: selftests: arm64: Report register reset tests individually
2025-12-19 19:28 [PATCH v3 0/4] KVM: selftests: arm64: Improve diagnostics from set_id_regs Mark Brown
2025-12-19 19:28 ` [PATCH v3 1/4] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests Mark Brown
@ 2025-12-19 19:28 ` Mark Brown
2026-01-02 14:42 ` Ben Horgan
2025-12-19 19:28 ` [PATCH v3 3/4] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal Mark Brown
2025-12-19 19:28 ` [PATCH v3 4/4] KVM: selftests: arm64: Skip all 32 bit IDs when set_id_regs is aarch64 only Mark Brown
3 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2025-12-19 19:28 UTC (permalink / raw)
To: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
Shuah Khan, Oliver Upton
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel,
Mark Brown
set_id_regs tests that registers have their values preserved over reset.
Currently it reports all registers in a single test with an instantly fatal
assert which isn't great for diagnostics, it's hard to tell which register
failed or if it's just one register. Change this to report each register as
a separate test so that it's clear from the program output which registers
have problems.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/kvm/arm64/set_id_regs.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c
index 84e9484a4899..b61942895808 100644
--- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
@@ -779,11 +779,18 @@ static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encodin
{
size_t idx = encoding_to_range_idx(encoding);
uint64_t observed;
+ bool pass;
observed = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(encoding));
- TEST_ASSERT_EQ(test_reg_vals[idx], observed);
+ pass = test_reg_vals[idx] == observed;
+ if (!pass)
+ ksft_print_msg("%lx != %lx\n", test_reg_vals[idx], observed);
+ ksft_test_result(pass, "%s unchanged by reset\n",
+ get_reg_name(encoding));
}
+#define ID_REG_RESET_UNCHANGED_TEST (ARRAY_SIZE(test_regs) + 6)
+
static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
{
/*
@@ -801,8 +808,6 @@ static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
test_assert_id_reg_unchanged(vcpu, SYS_MIDR_EL1);
test_assert_id_reg_unchanged(vcpu, SYS_REVIDR_EL1);
test_assert_id_reg_unchanged(vcpu, SYS_AIDR_EL1);
-
- ksft_test_result_pass("%s\n", __func__);
}
int main(void)
@@ -830,7 +835,8 @@ int main(void)
ksft_print_header();
- test_cnt = 3 + MPAM_IDREG_TEST + MTE_IDREG_TEST + GUEST_READ_TEST;
+ test_cnt = 2 + MPAM_IDREG_TEST + MTE_IDREG_TEST + GUEST_READ_TEST +
+ ID_REG_RESET_UNCHANGED_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++;
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 2/4] KVM: selftests: arm64: Report register reset tests individually
2025-12-19 19:28 ` [PATCH v3 2/4] KVM: selftests: arm64: Report register reset tests individually Mark Brown
@ 2026-01-02 14:42 ` Ben Horgan
0 siblings, 0 replies; 12+ messages in thread
From: Ben Horgan @ 2026-01-02 14:42 UTC (permalink / raw)
To: Mark Brown, Marc Zyngier, Joey Gouly, Suzuki K Poulose,
Paolo Bonzini, Shuah Khan, Oliver Upton
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel
Hi Mark,
On 12/19/25 19:28, Mark Brown wrote:
> set_id_regs tests that registers have their values preserved over reset.
> Currently it reports all registers in a single test with an instantly fatal
> assert which isn't great for diagnostics, it's hard to tell which register
> failed or if it's just one register. Change this to report each register as
> a separate test so that it's clear from the program output which registers
> have problems.
>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
> tools/testing/selftests/kvm/arm64/set_id_regs.c | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Thanks,
Ben
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 3/4] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal
2025-12-19 19:28 [PATCH v3 0/4] KVM: selftests: arm64: Improve diagnostics from set_id_regs Mark Brown
2025-12-19 19:28 ` [PATCH v3 1/4] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests Mark Brown
2025-12-19 19:28 ` [PATCH v3 2/4] KVM: selftests: arm64: Report register reset tests individually Mark Brown
@ 2025-12-19 19:28 ` Mark Brown
2026-01-02 14:45 ` Ben Horgan
2025-12-19 19:28 ` [PATCH v3 4/4] KVM: selftests: arm64: Skip all 32 bit IDs when set_id_regs is aarch64 only Mark Brown
3 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2025-12-19 19:28 UTC (permalink / raw)
To: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
Shuah Khan, Oliver Upton
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel,
Mark Brown
Currently when set_id_regs encounters a problem checking validation of
writes to feature registers it uses an immediately fatal assert to report
the problem. This is not idiomatic for kselftest, and it is also not great
for usability. The affected bitfield is not clearly reported and further
tests do not have their results reported.
Switch to using standard kselftest result reporting for the two asserts
we do, these are non-fatal asserts so allow the program to continue and the
test names include the affected field.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/kvm/arm64/set_id_regs.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c
index b61942895808..5837da63e9b9 100644
--- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
@@ -409,6 +409,7 @@ static uint64_t test_reg_set_success(struct kvm_vcpu *vcpu, uint64_t reg,
uint8_t shift = ftr_bits->shift;
uint64_t mask = ftr_bits->mask;
uint64_t val, new_val, ftr;
+ bool match;
val = vcpu_get_reg(vcpu, reg);
ftr = (val & mask) >> shift;
@@ -421,7 +422,10 @@ static uint64_t test_reg_set_success(struct kvm_vcpu *vcpu, uint64_t reg,
vcpu_set_reg(vcpu, reg, val);
new_val = vcpu_get_reg(vcpu, reg);
- TEST_ASSERT_EQ(new_val, val);
+ match = new_val == val;
+ if (!match)
+ ksft_print_msg("%lx != %lx\n", new_val, val);
+ ksft_test_result(match, "%s valid write succeeded\n", ftr_bits->name);
return new_val;
}
@@ -433,6 +437,7 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu, uint64_t reg,
uint64_t mask = ftr_bits->mask;
uint64_t val, old_val, ftr;
int r;
+ bool match;
val = vcpu_get_reg(vcpu, reg);
ftr = (val & mask) >> shift;
@@ -449,7 +454,10 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu, uint64_t reg,
"Unexpected KVM_SET_ONE_REG error: r=%d, errno=%d", r, errno);
val = vcpu_get_reg(vcpu, reg);
- TEST_ASSERT_EQ(val, old_val);
+ match = val == old_val;
+ if (!match)
+ ksft_print_msg("%lx != %lx\n", val, old_val);
+ ksft_test_result(match, "%s invalid write rejected\n", ftr_bits->name);
}
static uint64_t test_reg_vals[KVM_ARM_FEATURE_ID_RANGE_SIZE];
@@ -489,7 +497,11 @@ static void test_vm_ftr_id_regs(struct kvm_vcpu *vcpu, bool aarch64_only)
for (int j = 0; ftr_bits[j].type != FTR_END; j++) {
/* Skip aarch32 reg on aarch64 only system, since they are RAZ/WI. */
if (aarch64_only && sys_reg_CRm(reg_id) < 4) {
- ksft_test_result_skip("%s on AARCH64 only system\n",
+ ksft_print_msg("%s on AARCH64 only system\n",
+ ftr_bits[j].name);
+ ksft_test_result_skip("%s invalid write rejected\n",
+ ftr_bits[j].name);
+ ksft_test_result_skip("%s valid write succeeded\n",
ftr_bits[j].name);
continue;
}
@@ -501,8 +513,6 @@ static void test_vm_ftr_id_regs(struct kvm_vcpu *vcpu, bool aarch64_only)
test_reg_vals[idx] = test_reg_set_success(vcpu, reg,
&ftr_bits[j]);
-
- ksft_test_result_pass("%s\n", ftr_bits[j].name);
}
}
}
@@ -839,7 +849,7 @@ int main(void)
ID_REG_RESET_UNCHANGED_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++;
+ test_cnt += 2;
ksft_set_plan(test_cnt);
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 3/4] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal
2025-12-19 19:28 ` [PATCH v3 3/4] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal Mark Brown
@ 2026-01-02 14:45 ` Ben Horgan
2026-01-05 12:15 ` Mark Brown
0 siblings, 1 reply; 12+ messages in thread
From: Ben Horgan @ 2026-01-02 14:45 UTC (permalink / raw)
To: Mark Brown, Marc Zyngier, Joey Gouly, Suzuki K Poulose,
Paolo Bonzini, Shuah Khan, Oliver Upton
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel
Hi Mark,
On 12/19/25 19:28, Mark Brown wrote:
> Currently when set_id_regs encounters a problem checking validation of
> writes to feature registers it uses an immediately fatal assert to report
> the problem. This is not idiomatic for kselftest, and it is also not great
> for usability. The affected bitfield is not clearly reported and further
> tests do not have their results reported.
>
> Switch to using standard kselftest result reporting for the two asserts
> we do, these are non-fatal asserts so allow the program to continue and the
> test names include the affected field.
>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
> tools/testing/selftests/kvm/arm64/set_id_regs.c | 22 ++++++++++++++++------
> 1 file changed, 16 insertions(+), 6 deletions(-)
This one also looks good to me. I'm not aware of why the asserts have
been favoured previously though.
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Thanks,
Ben
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/4] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal
2026-01-02 14:45 ` Ben Horgan
@ 2026-01-05 12:15 ` Mark Brown
0 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-01-05 12:15 UTC (permalink / raw)
To: Ben Horgan
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
Shuah Khan, Oliver Upton, linux-arm-kernel, kvmarm, kvm,
linux-kselftest, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 905 bytes --]
On Fri, Jan 02, 2026 at 02:45:04PM +0000, Ben Horgan wrote:
> On 12/19/25 19:28, Mark Brown wrote:
> > Currently when set_id_regs encounters a problem checking validation of
> > writes to feature registers it uses an immediately fatal assert to report
> > the problem. This is not idiomatic for kselftest, and it is also not great
> This one also looks good to me. I'm not aware of why the asserts have
> been favoured previously though.
The older KVM selftests and the KVM specific selftest framework don't
work with the kselftest framework inside the test programs and instead
just run a single test within each test program and die immediately if
there's some issue. This is fine so long as each test only does one
thing but falls apart when you've got multiple tests in a single program
like this one, there the kselftest framework helps a lot. It looks like
the program is mixing the two idioms.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 4/4] KVM: selftests: arm64: Skip all 32 bit IDs when set_id_regs is aarch64 only
2025-12-19 19:28 [PATCH v3 0/4] KVM: selftests: arm64: Improve diagnostics from set_id_regs Mark Brown
` (2 preceding siblings ...)
2025-12-19 19:28 ` [PATCH v3 3/4] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal Mark Brown
@ 2025-12-19 19:28 ` Mark Brown
2026-01-02 14:50 ` Ben Horgan
3 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2025-12-19 19:28 UTC (permalink / raw)
To: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
Shuah Khan, Oliver Upton
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel,
Mark Brown
On an aarch64 only system the 32 bit ID registers have UNDEFINED values.
As a result set_id_regs skips tests for setting fields in these registers
when testing an aarch64 only guest. This has the side effect of meaning
that we don't record an expected value for these registers, meaning that
when the subsequent tests for values being visible in guests and preserved
over reset check the value they can spuriously fail. This can be seen by
running on an emulated system with both NV and 32 bit enabled, NV will
result in the guests created by the test program being 64 bit only but
the 32 bit ID registers will have values.
Also skip those tests that use the values set in the field setting tests
for aarch64 only guests in order to avoid these spurious failures.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/kvm/arm64/set_id_regs.c | 42 +++++++++++++++++--------
1 file changed, 29 insertions(+), 13 deletions(-)
diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c
index 5837da63e9b9..f19ba949aa18 100644
--- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
@@ -675,7 +675,7 @@ static void test_user_set_mte_reg(struct kvm_vcpu *vcpu)
ksft_test_result_pass("ID_AA64PFR1_EL1.MTE_frac no longer 0xF\n");
}
-static void test_guest_reg_read(struct kvm_vcpu *vcpu)
+static void test_guest_reg_read(struct kvm_vcpu *vcpu, bool aarch64_only)
{
bool done = false;
struct ucall uc;
@@ -694,6 +694,13 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
reg_id = uc.args[2];
guest_val = uc.args[3];
expected_val = test_reg_vals[encoding_to_range_idx(reg_id)];
+
+ if (aarch64_only && sys_reg_CRm(reg_id) < 4) {
+ ksft_test_result_skip("%s value seen in guest\n",
+ get_reg_name(reg_id));
+ break;
+ }
+
match = expected_val == guest_val;
if (!match)
ksft_print_msg("%lx != %lx\n",
@@ -785,12 +792,19 @@ static void test_vcpu_non_ftr_id_regs(struct kvm_vcpu *vcpu)
ksft_test_result_pass("%s\n", __func__);
}
-static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encoding)
+static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encoding,
+ bool aarch64_only)
{
size_t idx = encoding_to_range_idx(encoding);
uint64_t observed;
bool pass;
+ if (aarch64_only && sys_reg_CRm(encoding) < 4) {
+ ksft_test_result_skip("%s unchanged by reset\n",
+ get_reg_name(encoding));
+ return;
+ }
+
observed = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(encoding));
pass = test_reg_vals[idx] == observed;
if (!pass)
@@ -801,7 +815,8 @@ static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encodin
#define ID_REG_RESET_UNCHANGED_TEST (ARRAY_SIZE(test_regs) + 6)
-static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
+static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu,
+ bool aarch64_only)
{
/*
* Calls KVM_ARM_VCPU_INIT behind the scenes, which will do an
@@ -810,14 +825,15 @@ static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
aarch64_vcpu_setup(vcpu, NULL);
for (int i = 0; i < ARRAY_SIZE(test_regs); i++)
- test_assert_id_reg_unchanged(vcpu, test_regs[i].reg);
-
- test_assert_id_reg_unchanged(vcpu, SYS_MPIDR_EL1);
- test_assert_id_reg_unchanged(vcpu, SYS_CLIDR_EL1);
- test_assert_id_reg_unchanged(vcpu, SYS_CTR_EL0);
- test_assert_id_reg_unchanged(vcpu, SYS_MIDR_EL1);
- test_assert_id_reg_unchanged(vcpu, SYS_REVIDR_EL1);
- test_assert_id_reg_unchanged(vcpu, SYS_AIDR_EL1);
+ test_assert_id_reg_unchanged(vcpu, test_regs[i].reg,
+ aarch64_only);
+
+ test_assert_id_reg_unchanged(vcpu, SYS_MPIDR_EL1, aarch64_only);
+ test_assert_id_reg_unchanged(vcpu, SYS_CLIDR_EL1, aarch64_only);
+ test_assert_id_reg_unchanged(vcpu, SYS_CTR_EL0, aarch64_only);
+ test_assert_id_reg_unchanged(vcpu, SYS_MIDR_EL1, aarch64_only);
+ test_assert_id_reg_unchanged(vcpu, SYS_REVIDR_EL1, aarch64_only);
+ test_assert_id_reg_unchanged(vcpu, SYS_AIDR_EL1, aarch64_only);
}
int main(void)
@@ -859,9 +875,9 @@ int main(void)
test_user_set_mpam_reg(vcpu);
test_user_set_mte_reg(vcpu);
- test_guest_reg_read(vcpu);
+ test_guest_reg_read(vcpu, aarch64_only);
- test_reset_preserves_id_regs(vcpu);
+ test_reset_preserves_id_regs(vcpu, aarch64_only);
kvm_vm_free(vm);
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 4/4] KVM: selftests: arm64: Skip all 32 bit IDs when set_id_regs is aarch64 only
2025-12-19 19:28 ` [PATCH v3 4/4] KVM: selftests: arm64: Skip all 32 bit IDs when set_id_regs is aarch64 only Mark Brown
@ 2026-01-02 14:50 ` Ben Horgan
2026-01-05 16:45 ` Mark Brown
0 siblings, 1 reply; 12+ messages in thread
From: Ben Horgan @ 2026-01-02 14:50 UTC (permalink / raw)
To: Mark Brown, Marc Zyngier, Joey Gouly, Suzuki K Poulose,
Paolo Bonzini, Shuah Khan, Oliver Upton
Cc: linux-arm-kernel, kvmarm, kvm, linux-kselftest, linux-kernel
Hi Mark,
On 12/19/25 19:28, Mark Brown wrote:
> On an aarch64 only system the 32 bit ID registers have UNDEFINED values.
> As a result set_id_regs skips tests for setting fields in these registers
> when testing an aarch64 only guest. This has the side effect of meaning
> that we don't record an expected value for these registers, meaning that
> when the subsequent tests for values being visible in guests and preserved
> over reset check the value they can spuriously fail. This can be seen by
> running on an emulated system with both NV and 32 bit enabled, NV will
> result in the guests created by the test program being 64 bit only but
> the 32 bit ID registers will have values.
>
> Also skip those tests that use the values set in the field setting tests
> for aarch64 only guests in order to avoid these spurious failures.
>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
> tools/testing/selftests/kvm/arm64/set_id_regs.c | 42 +++++++++++++++++--------
> 1 file changed, 29 insertions(+), 13 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c
> index 5837da63e9b9..f19ba949aa18 100644
> --- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
> +++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
> @@ -675,7 +675,7 @@ static void test_user_set_mte_reg(struct kvm_vcpu *vcpu)
> ksft_test_result_pass("ID_AA64PFR1_EL1.MTE_frac no longer 0xF\n");
> }
>
> -static void test_guest_reg_read(struct kvm_vcpu *vcpu)
> +static void test_guest_reg_read(struct kvm_vcpu *vcpu, bool aarch64_only)
> {
> bool done = false;
> struct ucall uc;
> @@ -694,6 +694,13 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
> reg_id = uc.args[2];
> guest_val = uc.args[3];
> expected_val = test_reg_vals[encoding_to_range_idx(reg_id)];
> +
> + if (aarch64_only && sys_reg_CRm(reg_id) < 4) {
> + ksft_test_result_skip("%s value seen in guest\n",
> + get_reg_name(reg_id));
> + break;
> + }
> +
Unnecessary? The decision for which regs are testing is made in
guest_code().
> match = expected_val == guest_val;
> if (!match)
> ksft_print_msg("%lx != %lx\n",
> @@ -785,12 +792,19 @@ static void test_vcpu_non_ftr_id_regs(struct kvm_vcpu *vcpu)
> ksft_test_result_pass("%s\n", __func__);
> }
>
> -static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encoding)
> +static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encoding,
> + bool aarch64_only)
> {
> size_t idx = encoding_to_range_idx(encoding);
> uint64_t observed;
> bool pass;
>
> + if (aarch64_only && sys_reg_CRm(encoding) < 4) {
Doesn't this exclude more registers than needed? E.g. MIDR?
> + ksft_test_result_skip("%s unchanged by reset\n",
> + get_reg_name(encoding));
> + return;
> + }
> +
> observed = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(encoding));
> pass = test_reg_vals[idx] == observed;
> if (!pass)
> @@ -801,7 +815,8 @@ static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encodin
>
> #define ID_REG_RESET_UNCHANGED_TEST (ARRAY_SIZE(test_regs) + 6)
>
> -static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
> +static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu,
> + bool aarch64_only)
> {
> /*
> * Calls KVM_ARM_VCPU_INIT behind the scenes, which will do an
> @@ -810,14 +825,15 @@ static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
> aarch64_vcpu_setup(vcpu, NULL);
>
> for (int i = 0; i < ARRAY_SIZE(test_regs); i++)
> - test_assert_id_reg_unchanged(vcpu, test_regs[i].reg);
> -
> - test_assert_id_reg_unchanged(vcpu, SYS_MPIDR_EL1);
> - test_assert_id_reg_unchanged(vcpu, SYS_CLIDR_EL1);
> - test_assert_id_reg_unchanged(vcpu, SYS_CTR_EL0);
> - test_assert_id_reg_unchanged(vcpu, SYS_MIDR_EL1);
> - test_assert_id_reg_unchanged(vcpu, SYS_REVIDR_EL1);
> - test_assert_id_reg_unchanged(vcpu, SYS_AIDR_EL1);
> + test_assert_id_reg_unchanged(vcpu, test_regs[i].reg,
> + aarch64_only);
> +
> + test_assert_id_reg_unchanged(vcpu, SYS_MPIDR_EL1, aarch64_only);
> + test_assert_id_reg_unchanged(vcpu, SYS_CLIDR_EL1, aarch64_only);
> + test_assert_id_reg_unchanged(vcpu, SYS_CTR_EL0, aarch64_only);
> + test_assert_id_reg_unchanged(vcpu, SYS_MIDR_EL1, aarch64_only);
> + test_assert_id_reg_unchanged(vcpu, SYS_REVIDR_EL1, aarch64_only);
> + test_assert_id_reg_unchanged(vcpu, SYS_AIDR_EL1, aarch64_only);
> }
>
> int main(void)
> @@ -859,9 +875,9 @@ int main(void)
> test_user_set_mpam_reg(vcpu);
> test_user_set_mte_reg(vcpu);
>
> - test_guest_reg_read(vcpu);
> + test_guest_reg_read(vcpu, aarch64_only);
>
> - test_reset_preserves_id_regs(vcpu);
> + test_reset_preserves_id_regs(vcpu, aarch64_only);
>
> kvm_vm_free(vm);
>
>
Thanks,
Ben
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v3 4/4] KVM: selftests: arm64: Skip all 32 bit IDs when set_id_regs is aarch64 only
2026-01-02 14:50 ` Ben Horgan
@ 2026-01-05 16:45 ` Mark Brown
2026-01-05 17:00 ` Ben Horgan
0 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2026-01-05 16:45 UTC (permalink / raw)
To: Ben Horgan
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
Shuah Khan, Oliver Upton, linux-arm-kernel, kvmarm, kvm,
linux-kselftest, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 1004 bytes --]
On Fri, Jan 02, 2026 at 02:50:07PM +0000, Ben Horgan wrote:
> On 12/19/25 19:28, Mark Brown wrote:
> > + if (aarch64_only && sys_reg_CRm(reg_id) < 4) {
> > + ksft_test_result_skip("%s value seen in guest\n",
> > + get_reg_name(reg_id));
> > + break;
> > + }
> > +
> Unnecessary? The decision for which regs are testing is made in
> guest_code().
The guest code has a fixed list of registers it reads blindly and we
skip the write for these so our expected value won't be something we
explicitly set. The actual test is done here in the host code and it
seems both more maintainable to keep the skip adjacent to the live test
and clearer to be more explicit about nothing actually being tested.
> > + if (aarch64_only && sys_reg_CRm(encoding) < 4) {
> Doesn't this exclude more registers than needed? E.g. MIDR?
Yes, I took this test from somewhere without thinking about it properly
- it's been so long I can't remember but it was clearly wrong in this
context. I'll update.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v3 4/4] KVM: selftests: arm64: Skip all 32 bit IDs when set_id_regs is aarch64 only
2026-01-05 16:45 ` Mark Brown
@ 2026-01-05 17:00 ` Ben Horgan
0 siblings, 0 replies; 12+ messages in thread
From: Ben Horgan @ 2026-01-05 17:00 UTC (permalink / raw)
To: Mark Brown
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
Shuah Khan, Oliver Upton, linux-arm-kernel, kvmarm, kvm,
linux-kselftest, linux-kernel
Hi Mark,
On 1/5/26 16:45, Mark Brown wrote:
> On Fri, Jan 02, 2026 at 02:50:07PM +0000, Ben Horgan wrote:
>> On 12/19/25 19:28, Mark Brown wrote:
>
>>> + if (aarch64_only && sys_reg_CRm(reg_id) < 4) {
>>> + ksft_test_result_skip("%s value seen in guest\n",
>>> + get_reg_name(reg_id));
>>> + break;
>>> + }
>>> +
>
>> Unnecessary? The decision for which regs are testing is made in
>> guest_code().
>
> The guest code has a fixed list of registers it reads blindly and we
> skip the write for these so our expected value won't be something we
> explicitly set. The actual test is done here in the host code and it
> seems both more maintainable to keep the skip adjacent to the live test
> and clearer to be more explicit about nothing actually being tested.
Thanks for the explanation, it seems like a sensible decision.
Ben
^ permalink raw reply [flat|nested] 12+ messages in thread