Hi Mark,
On 3/6/26 17:01, Mark Brown wrote:
> The set_id_regs test currently assumes that there will always be invalid
> values available in bitfields for it to generate but this may not be the
> case if the architecture has defined meanings for every possible value for
> the bitfield. An assert added in commit bf09ee918053e ("KVM: arm64:
> selftests: Remove ARM64_FEATURE_FIELD_BITS and its last user") refuses to
> run for single bit fields which will show the issue most readily but there
> is no reason wider ones can't show the same issue.
>
> Rework the tests for invalid value to check if an invalid value can be
> generated and skip the test if not, removing the assert.
>
> Signed-off-by: Mark Brown <[email protected]>
> ---
> tools/testing/selftests/kvm/arm64/set_id_regs.c | 63
> +++++++++++++++++++++----
> 1 file changed, 53 insertions(+), 10 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c
> b/tools/testing/selftests/kvm/arm64/set_id_regs.c
> index bfca7be3e766..928e7d9e5ab7 100644
> --- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
> +++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
> @@ -317,11 +317,12 @@ uint64_t get_safe_value(const struct reg_ftr_bits
> *ftr_bits, uint64_t ftr)
> }
>
> /* Return an invalid value to a given ftr_bits an ftr value */
> -uint64_t get_invalid_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr)
> +uint64_t get_invalid_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr,
> + bool *skip)
> {
> uint64_t ftr_max = ftr_bits->mask >> ftr_bits->shift;
>
> - TEST_ASSERT(ftr_max > 1, "This test doesn't support single bit
> features");
> + *skip = false;
>
> if (ftr_bits->sign == FTR_UNSIGNED) {
> switch (ftr_bits->type) {
> @@ -329,42 +330,81 @@ uint64_t get_invalid_value(const struct reg_ftr_bits
> *ftr_bits, uint64_t ftr)
> ftr = max((uint64_t)ftr_bits->safe_val + 1, ftr + 1);
> break;
> case FTR_LOWER_SAFE:
> + if (ftr == ftr_max)
> + *skip = true;
> ftr++;
> break;
> case FTR_HIGHER_SAFE:
> + if (ftr == 0)
> + *skip = true;
> ftr--;
> break;
> case FTR_HIGHER_OR_ZERO_SAFE:
> - if (ftr == 0)
> + switch (ftr) {
> + case 0:
> ftr = ftr_max;
> - else
> + break;
> + case 1:
> + *skip = true;
> + break;
> + default:
> ftr--;
> + break;
> + }
> break;
> default:
> + *skip = true;
> break;
> }
> } else if (ftr != ftr_max) {
> switch (ftr_bits->type) {
> case FTR_EXACT:
> ftr = max((uint64_t)ftr_bits->safe_val + 1, ftr + 1);
> + if (ftr >= ftr_max)
> + *skip = true;
> break;
> case FTR_LOWER_SAFE:
> ftr++;
> break;
> case FTR_HIGHER_SAFE:
> - ftr--;
> + /* FIXME: "need to check for the actual highest." */
> + if (ftr == ftr_max)
> + *skip = true;
> + else
> + ftr--;
> break;
> case FTR_HIGHER_OR_ZERO_SAFE:
> - if (ftr == 0)
> - ftr = ftr_max - 1;
> - else
> + switch (ftr) {
> + case 0:
> + if (ftr_max > 1)
> + ftr = ftr_max - 1;
> + else
> + *skip = true;
> + break;
> + case 1:
> + *skip = true;
> + break;
> + default:
> ftr--;
> + break;
> + }
> break;
> default:
> + *skip = true;
> break;
> }
> } else {
> - ftr = 0;
> + switch (ftr_bits->type) {
> + case FTR_LOWER_SAFE:
> + if (ftr == 0)
> + *skip = true;
> + else
> + ftr = 0;
> + break;
> + default:
> + *skip = true;
> + break;
> + }
> }
I hacked up a quick loop to check what this function is doing.
With a mask=0x1 I see some value returned that have bits set
outside of the mask.
safe_val ftr out
UNSIGNED
FTR_EXACT
0x0 0x0 0x1
0x0 0x1 0x2 # out of range
0x1 0x0 0x2 # out of range
0x1 0x1 0x2 # out of range
FTR_LOWER_SAFE
0x0 0x0 0x1
0x0 0x1 SKIP
0x1 0x0 0x1
0x1 0x1 SKIP
FTR_HIGHER_SAFE
0x0 0x0 SKIP
0x0 0x1 0x0
0x1 0x0 SKIP
0x1 0x1 0x0
FTR_HIGHER_OR_ZERO_SAFE
0x0 0x0 0x1
0x0 0x1 SKIP
0x1 0x0 0x1
0x1 0x1 SKIP
SIGNED
FTR_EXACT
0x0 0x0 SKIP
0x0 0x1 SKIP
0x1 0x0 SKIP
0x1 0x1 SKIP
FTR_LOWER_SAFE
0x0 0x0 0x1
0x0 0x1 0x0
0x1 0x0 0x1
0x1 0x1 0x0
FTR_HIGHER_SAFE
0x0 0x0 0xffffffffffffffff # out of range
0x0 0x1 SKIP
0x1 0x0 0xffffffffffffffff # out of range
0x1 0x1 SKIP
FTR_HIGHER_OR_ZERO_SAFE
0x0 0x0 SKIP
0x0 0x1 SKIP
0x1 0x0 SKIP
0x1 0x1 SKIP
Thanks,
Ben
>
> return ftr;
> @@ -399,12 +439,15 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu,
> uint64_t reg,
> uint8_t shift = ftr_bits->shift;
> uint64_t mask = ftr_bits->mask;
> uint64_t val, old_val, ftr;
> + bool skip;
> int r;
>
> val = vcpu_get_reg(vcpu, reg);
> ftr = (val & mask) >> shift;
>
> - ftr = get_invalid_value(ftr_bits, ftr);
> + ftr = get_invalid_value(ftr_bits, ftr, &skip);
> + if (skip)
> + return;
>
> old_val = val;
> ftr <<= shift;
>