mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v7 0/3] KVM: selftests: arm64: Improve diagnostics from set_id_regs
@ 2026-09-01 18:50 Mark Brown
  2026-09-01 18:50 ` [PATCH v7 1/3] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests Mark Brown
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Mark Brown @ 2026-09-01 18:50 UTC (permalink / raw)
  To: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
	Shuah Khan, Oliver Upton
  Cc: Ben Horgan, linux-arm-kernel, kvmarm, kvm, linux-kselftest,
	linux-kernel, Mark Brown

While debugging issues related to aarch64 only systems I ran into
speedbumps due to the lack of detail in the results reported when the
guest register read and reset value preservation tests were run, they
generated an immediately fatal assert without indicating which register
was being tested. Update these tests to non-fatally report a standard
kselftest result per register, making it much easier to see what the
problem being reported is.

A similar issue exists with the validation of the individual bitfields
in registers due to the use of immediately fatal asserts. Update those
asserts to be standard kselftest reports.

Signed-off-by: Mark Brown <broonie@kernel.org>
---
Changes in v7:
- Rebase onto v7.3-rc1.
- Link to v6: https://patch.msgid.link/20260719-kvm-arm64-set-id-regs-aarch64-v6-0-724287f5f108@kernel.org

Changes in v6:
- Rebase onto v7.2-rc3.
- Drop changes to handling of aarch32 related registers on 64 bit only
  systems, an in kernel solution is preferred for those.
- Use a define to count the non-test_regs registers.
- Link to v5: https://patch.msgid.link/20260317-kvm-arm64-set-id-regs-aarch64-v5-0-a60f2b956e22@kernel.org

Changes in v5:
- Rebase onto v7.0-rc1.
- Link to v4: https://patch.msgid.link/20260106-kvm-arm64-set-id-regs-aarch64-v4-0-c7ef4551afb3@kernel.org

Changes in v4:
- Correct check for 32 bit ID registers.
- Link to v3: https://patch.msgid.link/20251219-kvm-arm64-set-id-regs-aarch64-v3-0-bfa474ec3218@kernel.org

Changes in v3:
- Rebase onto v6.19-rc1.
- Link to v2: https://patch.msgid.link/20251114-kvm-arm64-set-id-regs-aarch64-v2-0-672f214f41bf@kernel.org

Changes in v2:
- Add a fix for spurious failures with 64 bit only guests.
- Link to v1: https://patch.msgid.link/20251030-kvm-arm64-set-id-regs-aarch64-v1-0-96fe0d2b178e@kernel.org

---
Mark Brown (3):
      KVM: selftests: arm64: Report set_id_reg reads of test registers as tests
      KVM: selftests: arm64: Report register reset tests individually
      KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal

 tools/testing/selftests/kvm/arm64/set_id_regs.c | 121 ++++++++++++++++++------
 1 file changed, 91 insertions(+), 30 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20251028-kvm-arm64-set-id-regs-aarch64-ebb77969401c

Best regards,
--  
Mark Brown <broonie@kernel.org>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v7 1/3] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests
  2026-09-01 18:50 [PATCH v7 0/3] KVM: selftests: arm64: Improve diagnostics from set_id_regs Mark Brown
@ 2026-09-01 18:50 ` Mark Brown
  2026-09-28 15:26   ` Lorenzo Stoakes (ARM)
  2026-09-01 18:50 ` [PATCH v7 2/3] KVM: selftests: arm64: Report register reset tests individually Mark Brown
  2026-09-01 18:50 ` [PATCH v7 3/3] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal Mark Brown
  2 siblings, 1 reply; 8+ messages in thread
From: Mark Brown @ 2026-09-01 18:50 UTC (permalink / raw)
  To: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
	Shuah Khan, Oliver Upton
  Cc: Ben Horgan, 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.

Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 tools/testing/selftests/kvm/arm64/set_id_regs.c | 82 +++++++++++++++++++------
 1 file changed, 63 insertions(+), 19 deletions(-)

diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c
index 7429a1055df5..db6414a93ad3 100644
--- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
@@ -43,6 +43,7 @@ struct reg_ftr_bits {
 };
 
 struct test_feature_reg {
+	const char *name;
 	u32 reg;
 	const struct reg_ftr_bits *ftr_bits;
 };
@@ -227,30 +228,32 @@ 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);
 
 static void guest_code(void)
 {
+	/* Registers in test_regs array */
 	GUEST_REG_SYNC(SYS_ID_AA64DFR0_EL1);
 	GUEST_REG_SYNC(SYS_ID_DFR0_EL1);
 	GUEST_REG_SYNC(SYS_ID_AA64ISAR0_EL1);
@@ -264,6 +267,8 @@ static void guest_code(void)
 	GUEST_REG_SYNC(SYS_ID_AA64MMFR2_EL1);
 	GUEST_REG_SYNC(SYS_ID_AA64MMFR3_EL1);
 	GUEST_REG_SYNC(SYS_ID_AA64ZFR0_EL1);
+
+	/* Additional registers counted in NUM_EXTRA_REGS */
 	GUEST_REG_SYNC(SYS_MPIDR_EL1);
 	GUEST_REG_SYNC(SYS_CLIDR_EL1);
 	GUEST_REG_SYNC(SYS_CTR_EL0);
@@ -274,6 +279,35 @@ static void guest_code(void)
 	GUEST_DONE();
 }
 
+#define NUM_EXTRA_REGS 6
+#define GUEST_READ_TEST (ARRAY_SIZE(test_regs) + NUM_EXTRA_REGS)
+
+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 */
 u64 get_safe_value(const struct reg_ftr_bits *ftr_bits, u64 ftr)
 {
@@ -674,7 +708,8 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
 	struct ucall uc;
 
 	while (!done) {
-		u64 val;
+		u64 reg_id, expected_val, guest_val;
+		bool match;
 
 		vcpu_run(vcpu);
 
@@ -683,11 +718,20 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
 			REPORT_GUEST_ASSERT(uc);
 			break;
 		case UCALL_SYNC:
-			val = test_reg_vals[encoding_to_range_idx(uc.args[2])];
-			val = reset_mutable_bits(uc.args[2], val);
+			expected_val = test_reg_vals[encoding_to_range_idx(uc.args[2])];
+			expected_val = reset_mutable_bits(uc.args[2], expected_val);
 
 			/* Make sure the written values are seen by guest */
-			TEST_ASSERT_EQ(val, reset_mutable_bits(uc.args[2], uc.args[3]));
+			reg_id = uc.args[2];
+			guest_val = reset_mutable_bits(uc.args[2], uc.args[3]);
+
+			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;
@@ -828,7 +872,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	[flat|nested] 8+ messages in thread

* [PATCH v7 2/3] KVM: selftests: arm64: Report register reset tests individually
  2026-09-01 18:50 [PATCH v7 0/3] KVM: selftests: arm64: Improve diagnostics from set_id_regs Mark Brown
  2026-09-01 18:50 ` [PATCH v7 1/3] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests Mark Brown
@ 2026-09-01 18:50 ` Mark Brown
  2026-09-28 15:32   ` Lorenzo Stoakes (ARM)
  2026-09-01 18:50 ` [PATCH v7 3/3] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal Mark Brown
  2 siblings, 1 reply; 8+ messages in thread
From: Mark Brown @ 2026-09-01 18:50 UTC (permalink / raw)
  To: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
	Shuah Khan, Oliver Upton
  Cc: Ben Horgan, 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.

Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 tools/testing/selftests/kvm/arm64/set_id_regs.c | 19 +++++++++++++------
 1 file changed, 13 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 db6414a93ad3..3aa8886e8b70 100644
--- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
@@ -819,13 +819,21 @@ static void test_vcpu_non_ftr_id_regs(struct kvm_vcpu *vcpu)
 static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, u32 encoding)
 {
 	size_t idx = encoding_to_range_idx(encoding);
-	u64 observed;
+	u64 observed, expected;
+	bool pass;
 
 	observed = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(encoding));
-	TEST_ASSERT_EQ(reset_mutable_bits(encoding, test_reg_vals[idx]),
-		       reset_mutable_bits(encoding, observed));
+	observed = reset_mutable_bits(encoding, observed);
+	expected = reset_mutable_bits(encoding, test_reg_vals[idx]);
+	pass = expected == observed;
+	if (!pass)
+		ksft_print_msg("%lx != %lx\n", expected, observed);
+	ksft_test_result(pass, "%s unchanged by reset\n",
+			 get_reg_name(encoding));
 }
 
+#define ID_REG_RESET_UNCHANGED_TEST (ARRAY_SIZE(test_regs) + NUM_EXTRA_REGS)
+
 static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
 {
 	/*
@@ -843,8 +851,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)
@@ -872,7 +878,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	[flat|nested] 8+ messages in thread

* [PATCH v7 3/3] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal
  2026-09-01 18:50 [PATCH v7 0/3] KVM: selftests: arm64: Improve diagnostics from set_id_regs Mark Brown
  2026-09-01 18:50 ` [PATCH v7 1/3] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests Mark Brown
  2026-09-01 18:50 ` [PATCH v7 2/3] KVM: selftests: arm64: Report register reset tests individually Mark Brown
@ 2026-09-01 18:50 ` Mark Brown
  2026-09-28 15:43   ` Lorenzo Stoakes (ARM)
  2 siblings, 1 reply; 8+ messages in thread
From: Mark Brown @ 2026-09-01 18:50 UTC (permalink / raw)
  To: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
	Shuah Khan, Oliver Upton
  Cc: Ben Horgan, 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.

Reviewed-by: Ben Horgan <ben.horgan@arm.com>
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 3aa8886e8b70..385e1f1c0133 100644
--- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
@@ -422,6 +422,7 @@ static u64 test_reg_set_success(struct kvm_vcpu *vcpu, u64 reg,
 	u8 shift = ftr_bits->shift;
 	u64 mask = ftr_bits->mask;
 	u64 val, new_val, ftr;
+	bool match;
 
 	val = vcpu_get_reg(vcpu, reg);
 	ftr = (val & mask) >> shift;
@@ -434,7 +435,10 @@ static u64 test_reg_set_success(struct kvm_vcpu *vcpu, u64 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;
 }
@@ -446,6 +450,7 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu, u64 reg,
 	u64 mask = ftr_bits->mask;
 	u64 val, old_val, ftr;
 	int r;
+	bool match;
 
 	val = vcpu_get_reg(vcpu, reg);
 	ftr = (val & mask) >> shift;
@@ -462,7 +467,10 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu, u64 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 u64 test_reg_vals[KVM_ARM_FEATURE_ID_RANGE_SIZE];
@@ -502,7 +510,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;
 			}
@@ -514,8 +526,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);
 		}
 	}
 }
@@ -882,7 +892,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	[flat|nested] 8+ messages in thread

* Re: [PATCH v7 1/3] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests
  2026-09-01 18:50 ` [PATCH v7 1/3] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests Mark Brown
@ 2026-09-28 15:26   ` Lorenzo Stoakes (ARM)
  2026-09-28 16:29     ` Mark Brown
  0 siblings, 1 reply; 8+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-28 15:26 UTC (permalink / raw)
  To: Mark Brown
  Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
	Shuah Khan, Oliver Upton, Ben Horgan, linux-arm-kernel, kvmarm,
	kvm, linux-kselftest, linux-kernel

On Tue, Sep 01, 2026 at 07:50:01PM +0100, 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.
>
> Reviewed-by: Ben Horgan <ben.horgan@arm.com>
> Signed-off-by: Mark Brown <broonie@kernel.org>

All looks reasonable to me.

Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>

Couple comments below but more like understanding-your-changes stuff.

> ---
>  tools/testing/selftests/kvm/arm64/set_id_regs.c | 82 +++++++++++++++++++------
>  1 file changed, 63 insertions(+), 19 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c
> index 7429a1055df5..db6414a93ad3 100644
> --- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
> +++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
> @@ -43,6 +43,7 @@ struct reg_ftr_bits {
>  };
>
>  struct test_feature_reg {
> +	const char *name;
>  	u32 reg;
>  	const struct reg_ftr_bits *ftr_bits;
>  };
> @@ -227,30 +228,32 @@ 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);
>
>  static void guest_code(void)
>  {
> +	/* Registers in test_regs array */
>  	GUEST_REG_SYNC(SYS_ID_AA64DFR0_EL1);
>  	GUEST_REG_SYNC(SYS_ID_DFR0_EL1);
>  	GUEST_REG_SYNC(SYS_ID_AA64ISAR0_EL1);
> @@ -264,6 +267,8 @@ static void guest_code(void)
>  	GUEST_REG_SYNC(SYS_ID_AA64MMFR2_EL1);
>  	GUEST_REG_SYNC(SYS_ID_AA64MMFR3_EL1);
>  	GUEST_REG_SYNC(SYS_ID_AA64ZFR0_EL1);
> +
> +	/* Additional registers counted in NUM_EXTRA_REGS */
>  	GUEST_REG_SYNC(SYS_MPIDR_EL1);
>  	GUEST_REG_SYNC(SYS_CLIDR_EL1);
>  	GUEST_REG_SYNC(SYS_CTR_EL0);
> @@ -274,6 +279,35 @@ static void guest_code(void)
>  	GUEST_DONE();
>  }
>
> +#define NUM_EXTRA_REGS 6
> +#define GUEST_READ_TEST (ARRAY_SIZE(test_regs) + NUM_EXTRA_REGS)
> +
> +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");
> +	}

I guess these are for GUEST_REG_SYNC() registers, etc. that don't appear in the
TEST_REG() list?

> +}
> +
>  /* Return a safe value to a given ftr_bits an ftr value */
>  u64 get_safe_value(const struct reg_ftr_bits *ftr_bits, u64 ftr)
>  {
> @@ -674,7 +708,8 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
>  	struct ucall uc;
>
>  	while (!done) {
> -		u64 val;
> +		u64 reg_id, expected_val, guest_val;
> +		bool match;
>
>  		vcpu_run(vcpu);
>
> @@ -683,11 +718,20 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
>  			REPORT_GUEST_ASSERT(uc);
>  			break;
>  		case UCALL_SYNC:
> -			val = test_reg_vals[encoding_to_range_idx(uc.args[2])];
> -			val = reset_mutable_bits(uc.args[2], val);
> +			expected_val = test_reg_vals[encoding_to_range_idx(uc.args[2])];
> +			expected_val = reset_mutable_bits(uc.args[2], expected_val);
>
>  			/* Make sure the written values are seen by guest */
> -			TEST_ASSERT_EQ(val, reset_mutable_bits(uc.args[2], uc.args[3]));
> +			reg_id = uc.args[2];
> +			guest_val = reset_mutable_bits(uc.args[2], uc.args[3]);
> +
> +			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;
> @@ -828,7 +872,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;

I guess because you made things use TAP now?

>  	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
>
>

--
Cheers, Lorenzo

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v7 2/3] KVM: selftests: arm64: Report register reset tests individually
  2026-09-01 18:50 ` [PATCH v7 2/3] KVM: selftests: arm64: Report register reset tests individually Mark Brown
@ 2026-09-28 15:32   ` Lorenzo Stoakes (ARM)
  0 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-28 15:32 UTC (permalink / raw)
  To: Mark Brown
  Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
	Shuah Khan, Oliver Upton, Ben Horgan, linux-arm-kernel, kvmarm,
	kvm, linux-kselftest, linux-kernel

On Tue, Sep 01, 2026 at 07:50:02PM +0100, 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.

Sounds reasonable.

>
> Reviewed-by: Ben Horgan <ben.horgan@arm.com>
> Signed-off-by: Mark Brown <broonie@kernel.org>

All LGTM so:

Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>

> ---
>  tools/testing/selftests/kvm/arm64/set_id_regs.c | 19 +++++++++++++------
>  1 file changed, 13 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 db6414a93ad3..3aa8886e8b70 100644
> --- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
> +++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
> @@ -819,13 +819,21 @@ static void test_vcpu_non_ftr_id_regs(struct kvm_vcpu *vcpu)
>  static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, u32 encoding)
>  {
>  	size_t idx = encoding_to_range_idx(encoding);
> -	u64 observed;
> +	u64 observed, expected;
> +	bool pass;
>
>  	observed = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(encoding));
> -	TEST_ASSERT_EQ(reset_mutable_bits(encoding, test_reg_vals[idx]),
> -		       reset_mutable_bits(encoding, observed));
> +	observed = reset_mutable_bits(encoding, observed);
> +	expected = reset_mutable_bits(encoding, test_reg_vals[idx]);
> +	pass = expected == observed;
> +	if (!pass)
> +		ksft_print_msg("%lx != %lx\n", expected, observed);
> +	ksft_test_result(pass, "%s unchanged by reset\n",
> +			 get_reg_name(encoding));

Ah yeah -> individual tests.

>  }
>
> +#define ID_REG_RESET_UNCHANGED_TEST (ARRAY_SIZE(test_regs) + NUM_EXTRA_REGS)

Yeah can see from original test_reset_preserves_id_regs() that this is correct.

> +
>  static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
>  {
>  	/*
> @@ -843,8 +851,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)
> @@ -872,7 +878,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
>
>

--
Cheers, Lorenzo

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v7 3/3] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal
  2026-09-01 18:50 ` [PATCH v7 3/3] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal Mark Brown
@ 2026-09-28 15:43   ` Lorenzo Stoakes (ARM)
  0 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-28 15:43 UTC (permalink / raw)
  To: Mark Brown
  Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
	Shuah Khan, Oliver Upton, Ben Horgan, linux-arm-kernel, kvmarm,
	kvm, linux-kselftest, linux-kernel

Typo in subject: validatity -> validity

On Tue, Sep 01, 2026 at 07:50:03PM +0100, 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.
>
> Reviewed-by: Ben Horgan <ben.horgan@arm.com>
> Signed-off-by: Mark Brown <broonie@kernel.org>

One NIT below and typo noted above, with those addressed LGTM so:

Reviewed-by: Lorenzo Stoakes (ARM) <ljs@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 3aa8886e8b70..385e1f1c0133 100644
> --- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
> +++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
> @@ -422,6 +422,7 @@ static u64 test_reg_set_success(struct kvm_vcpu *vcpu, u64 reg,
>  	u8 shift = ftr_bits->shift;
>  	u64 mask = ftr_bits->mask;
>  	u64 val, new_val, ftr;
> +	bool match;
>
>  	val = vcpu_get_reg(vcpu, reg);
>  	ftr = (val & mask) >> shift;
> @@ -434,7 +435,10 @@ static u64 test_reg_set_success(struct kvm_vcpu *vcpu, u64 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);

Same pattern (I wonder, not for this series, but perhaps a follow up whether
this could be generalised?)

>
>  	return new_val;
>  }
> @@ -446,6 +450,7 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu, u64 reg,
>  	u64 mask = ftr_bits->mask;
>  	u64 val, old_val, ftr;
>  	int r;
> +	bool match;
>
>  	val = vcpu_get_reg(vcpu, reg);
>  	ftr = (val & mask) >> shift;
> @@ -462,7 +467,10 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu, u64 reg,
>  		    "Unexpected KVM_SET_ONE_REG error: r=%d, errno=%d", r, errno);
>

NIT:

There's a:

	TEST_ASSERT(r < 0 && errno == EINVAL,
		    "Unexpected KVM_SET_ONE_REG error: r=%d, errno=%d", r, errno);

Above, do we want to make that non-fatal too?

>  	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 u64 test_reg_vals[KVM_ARM_FEATURE_ID_RANGE_SIZE];
> @@ -502,7 +510,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;
>  			}
> @@ -514,8 +526,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);
>  		}
>  	}
>  }
> @@ -882,7 +892,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
>
>

--
Cheers, Lorenzo

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v7 1/3] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests
  2026-09-28 15:26   ` Lorenzo Stoakes (ARM)
@ 2026-09-28 16:29     ` Mark Brown
  0 siblings, 0 replies; 8+ messages in thread
From: Mark Brown @ 2026-09-28 16:29 UTC (permalink / raw)
  To: Lorenzo Stoakes (ARM)
  Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Paolo Bonzini,
	Shuah Khan, Oliver Upton, Ben Horgan, linux-arm-kernel, kvmarm,
	kvm, linux-kselftest, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 929 bytes --]

On Mon, Sep 28, 2026 at 04:26:57PM +0100, Lorenzo Stoakes (ARM) wrote:
> On Tue, Sep 01, 2026 at 07:50:01PM +0100, Mark Brown wrote:

> > +	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");
> > +	}

> I guess these are for GUEST_REG_SYNC() registers, etc. that don't appear in the
> TEST_REG() list?

Yes.

> > @@ -828,7 +872,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;
> 
> I guess because you made things use TAP now?

Yes.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-09-28 16:29 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-01 18:50 [PATCH v7 0/3] KVM: selftests: arm64: Improve diagnostics from set_id_regs Mark Brown
2026-09-01 18:50 ` [PATCH v7 1/3] KVM: selftests: arm64: Report set_id_reg reads of test registers as tests Mark Brown
2026-09-28 15:26   ` Lorenzo Stoakes (ARM)
2026-09-28 16:29     ` Mark Brown
2026-09-01 18:50 ` [PATCH v7 2/3] KVM: selftests: arm64: Report register reset tests individually Mark Brown
2026-09-28 15:32   ` Lorenzo Stoakes (ARM)
2026-09-01 18:50 ` [PATCH v7 3/3] KVM: selftests: arm64: Make set_id_regs bitfield validatity checks non-fatal Mark Brown
2026-09-28 15:43   ` Lorenzo Stoakes (ARM)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®