[PATCH v2 02/25] KVM: arm64: Add feature checking helpers
Suzuki K Poulose
suzuki.poulose at arm.com
Mon Feb 5 02:10:41 PST 2024
On 04/02/2024 11:44, Marc Zyngier wrote:
> On Sun, 04 Feb 2024 11:08:45 +0000,
> Marc Zyngier <maz at kernel.org> wrote:
>>
>> On Fri, 02 Feb 2024 17:13:07 +0000,
>> Suzuki K Poulose <suzuki.poulose at arm.com> wrote:
>>>
>>> Hi Marc,
>>>
>>> On 30/01/2024 20:45, Marc Zyngier wrote:
>>>> In order to make it easier to check whether a particular feature
>>>> is exposed to a guest, add a new set of helpers, with kvm_has_feat()
>>>> being the most useful.
>>>>
>>>> Let's start making use of them in the PMU code (courtesy of Oliver).
>>>> Follow-up work will intricude additional use patterns.
>>>
>>> I think there is a bit of inconsistency in the macros for signed
>>> and unsigned. The unsigned fields are extracted (i.e., as if they
>>> were shifted to bit 0). But the signed fields are not shifted
>>> completely to bit '0' (in fact to different positions) and eventually
>>> we compare wrong things.
>>>
>>> Using ID_AA64PFR0_EL1, fld=EL2, val=IMP for unsigned and
>>> ID_AA64PFR0_EL1, AdvSIMD, NI for signed.
>>>
>>>>
>>>> Co-developed--by: Oliver Upton <oliver.upton at linux.dev>
>>>> Signed-off-by: Oliver Upton <oliver.upton at linux.dev>
>>>> Signed-off-by: Marc Zyngier <maz at kernel.org>
>>>> ---
>>>> arch/arm64/include/asm/kvm_host.h | 53 +++++++++++++++++++++++++++++++
>>>> arch/arm64/kvm/pmu-emul.c | 11 ++++---
>>>> arch/arm64/kvm/sys_regs.c | 6 ++--
>>>> include/kvm/arm_pmu.h | 11 -------
>>>> 4 files changed, 61 insertions(+), 20 deletions(-)
>>>>
>>>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>>>> index 21c57b812569..c0cf9c5f5e8d 100644
>>>> --- a/arch/arm64/include/asm/kvm_host.h
>>>> +++ b/arch/arm64/include/asm/kvm_host.h
>>>> @@ -1233,4 +1233,57 @@ static inline void kvm_hyp_reserve(void) { }
>>>> void kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu);
>>>> bool kvm_arm_vcpu_stopped(struct kvm_vcpu *vcpu);
>>>> +#define __expand_field_sign_unsigned(id, fld, val)
>>>> \
>>>> + ((u64)(id##_##fld##_##val))
>>>
>>> For unsigned features we get the actual "field" value, not the value
>>> in position. e.g,: ID_AA64PFR0_EL1_EL2_IMP = (0x1)
>>>
>>>> +
>>>> +#define __expand_field_sign_signed(id, fld, val) \
>>>> + ({ \
>>>> + s64 __val = id##_##fld##_##val; \
>>>> + __val <<= 64 - id##_##fld##_WIDTH; \
>>>> + __val >>= 64 - id##_##fld##_SHIFT - id##_##fld##_WIDTH; \
>>>
>>> But for signed fields, we shift them back into the "position" as in
>>> the ID_REG. e.g.,
>>>
>>> ID_AA64PFR0_EL1, AdvSIMD, NI we get:
>>>
>>> __val = ID_AA64PFR0_EL1_AdvSIMD_NI; /* = 0xf */
>>> __val <<= 64 - 4; /* 0xf0_00_00_00_00_00_00_00 */
>>> __val >>= 64 - 20 - 4; /* 0xff_ff_ff_ff_ff_f0_00_00 */
>>>
>>> I think the last line instead should be:
>>> __val >>= 64 - id##_##fld##_WIDTH;
>>
>> Huh, you're absolutely right.
>>
>>>
>>>> + \
>>>> + __val; \
>>>> + })
>>>> +
>>>> +#define expand_field_sign(id, fld, val) \
>>>> + (id##_##fld##_SIGNED ? \
>>>> + __expand_field_sign_signed(id, fld, val) : \
>>>> + __expand_field_sign_unsigned(id, fld, val))
>>>> +
>>>> +#define get_idreg_field_unsigned(kvm, id, fld) \
>>>> + ({ \
>>>> + u64 __val = IDREG(kvm, SYS_##id); \
>>>> + __val &= id##_##fld##_MASK; \
>>>> + __val >>= id##_##fld##_SHIFT; \
>>>> + \
>>>
>>> We extract the field value for unsigned field, i.e., shifted to bit"0"
>>> and that matches the expand_field_sign().
>>>
>>>> + __val; \
>>>> + })
>>>> +
>>>> +#define get_idreg_field_signed(kvm, id, fld) \
>>>> + ({ \
>>>> + s64 __val = IDREG(kvm, SYS_##id); \
>>>> + __val <<= 64 - id##_##fld##_SHIFT - id##_##fld##_WIDTH; \
>>>> + __val >>= id##_##fld##_SHIFT; \
>>>
>>> However, here we get (assuming value ID_AA64PFR0_EL1_ASIMD = 0xf, and
>>> all other fields are 0 for clarity)
>>>
>>> __val = IDREG(kvm, SYS_ID_AA64PFR0_EL1); = 0xf0_00_00; /* 0xf << 20 */
>>> __val <<= 64 - 20 - 4; /* = 0xf0_00_00_00_00_00_00_00 */
>>> __val >>= 20; /* = 0xff_ff_ff_00_00_00_00_00 */
>>
>> Gah... again!
>>
>>>
>>> Thus they don;t match. Instead the last line should be :
>>>
>>> __val >>= id##_##fld##_WIDTH;
>>
>> Shouldn't this be (64 - WIDTH) instead, since we want the value to be
>> shifted to bit 0? Otherwise, you get 0xff_00_00_00_00_00_00_00 (as per
>> your example).
Bah, You're right. See, its so easy to mess it up ;-)
>>
>> Thanks a lot for spotting those, much appreciated.
>
> And since it is now obvious to anyone that I can't shift properly,
> let's just not do that. As it turns out, the kernel already has the
> required macros, and I can stop being clever with these:
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index ea8acc1914aa..3457fa313bad 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -1290,11 +1290,8 @@ bool kvm_arm_vcpu_stopped(struct kvm_vcpu *vcpu);
>
> #define __expand_field_sign_signed(id, fld, val) \
> ({ \
> - s64 __val = id##_##fld##_##val; \
> - __val <<= 64 - id##_##fld##_WIDTH; \
> - __val >>= 64 - id##_##fld##_SHIFT - id##_##fld##_WIDTH; \
> - \
> - __val; \
> + u64 __val = id##_##fld##_##val; \
> + sign_extend64(__val, id##_##fld##_WIDTH - 1); \
> })
>
> #define expand_field_sign(id, fld, val) \
> @@ -1313,11 +1310,9 @@ bool kvm_arm_vcpu_stopped(struct kvm_vcpu *vcpu);
>
> #define get_idreg_field_signed(kvm, id, fld) \
> ({ \
> - s64 __val = IDREG(kvm, SYS_##id); \
> - __val <<= 64 - id##_##fld##_SHIFT - id##_##fld##_WIDTH; \
> - __val >>= id##_##fld##_SHIFT; \
> - \
> - __val; \
> + u64 __val = IDREG(kvm, SYS_##id); \
> + __val = FIELD_GET(id##_##fld##_MASK, __val); \
> + sign_extend64(__val, id##_##fld##_WIDTH - 1); \
> })
>
> #define get_idreg_field_enum(kvm, id, fld) \
>
> Thoughts?
Thats much much better and saves you some staring at the screen. Didn't
know that existed. Thanks for that.
Suzuki
>
> M.
>
More information about the linux-arm-kernel
mailing list