[PATCH 08/13] KVM: arm64: Move CNT*CT_EL0 userspace accessors to generic infrastructure

Joey Gouly joey.gouly at arm.com
Tue Sep 30 03:45:52 PDT 2025


Observation below

  |
  v

On Mon, Sep 29, 2025 at 05:04:52PM +0100, Marc Zyngier wrote:
> Moving the counter registers is a bit more involved than for the control
> and comparator (there is no shadow data for the counter), but still
> pretty manageable.
> 
> Signed-off-by: Marc Zyngier <maz at kernel.org>
> ---
>  arch/arm64/kvm/guest.c    |  7 -------
>  arch/arm64/kvm/sys_regs.c | 34 +++++++++++++++++++++++++++++++---
>  2 files changed, 31 insertions(+), 10 deletions(-)
> 
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index c23ec9be4ce27..138e5e2dc10c8 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -592,19 +592,12 @@ static unsigned long num_core_regs(const struct kvm_vcpu *vcpu)
>  }
>  
>  static const u64 timer_reg_list[] = {
> -	KVM_REG_ARM_TIMER_CNT,
> -	KVM_REG_ARM_PTIMER_CNT,
>  };
>  
>  #define NUM_TIMER_REGS ARRAY_SIZE(timer_reg_list)
>  
>  static bool is_timer_reg(u64 index)
>  {
> -	switch (index) {
> -	case KVM_REG_ARM_TIMER_CNT:
> -	case KVM_REG_ARM_PTIMER_CNT:
> -		return true;
> -	}
>  	return false;
>  }
>  
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 68e88d5c0dfb5..e67eb39ddc118 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -1605,12 +1605,38 @@ static int arch_timer_set_user(struct kvm_vcpu *vcpu,
>  	case SYS_CNTHP_CTL_EL2:
>  		val &= ~ARCH_TIMER_CTRL_IT_STAT;
>  		break;
> +	case SYS_CNTVCT_EL0:
> +		if (!test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags))
> +			timer_set_offset(vcpu_vtimer(vcpu), kvm_phys_timer_read() - val);
> +		return 0;
> +	case SYS_CNTPCT_EL0:
> +		if (!test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags))
> +			timer_set_offset(vcpu_ptimer(vcpu), kvm_phys_timer_read() - val);
> +		return 0;
>  	}
>  
>  	__vcpu_assign_sys_reg(vcpu, rd->reg, val);
>  	return 0;
>  }
>  
> +static int arch_timer_get_user(struct kvm_vcpu *vcpu,
> +			       const struct sys_reg_desc *rd,
> +			       u64 *val)
> +{
> +	switch (reg_to_encoding(rd)) {
> +	case SYS_CNTVCT_EL0:
> +		*val = kvm_phys_timer_read() - timer_get_offset(vcpu_vtimer(vcpu));
> +		break;
> +	case SYS_CNTPCT_EL0:
> +		*val = kvm_phys_timer_read() - timer_get_offset(vcpu_ptimer(vcpu));
> +		break;
> +	default:
> +		*val = __vcpu_sys_reg(vcpu, rd->reg);

Unsure if this is actually an issue but for the _CTL registers, via
access_arch_timer() (kvm_arm_timer_read_sysreg() -> .. -> read_timer_ctl()),
the ARCH_TIMER_CTRL_IT_STAT bit will be set if the timer expired, but that's
not done here.

> +	}
> +
> +	return 0;
> +}
> +
>  static s64 kvm_arm64_ftr_safe_value(u32 id, const struct arm64_ftr_bits *ftrp,
>  				    s64 new, s64 cur)
>  {
> @@ -2539,7 +2565,7 @@ static bool bad_redir_trap(struct kvm_vcpu *vcpu,
>  
>  #define TIMER_REG(name, vis)					   \
>  	SYS_REG_USER_FILTER(name, access_arch_timer, reset_val, 0, \
> -			    NULL, arch_timer_set_user, vis)
> +			    arch_timer_get_user, arch_timer_set_user, vis)
>  
>  /*
>   * Since reset() callback and field val are not used for idregs, they will be
> @@ -3506,8 +3532,10 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>  	AMU_AMEVTYPER1_EL0(14),
>  	AMU_AMEVTYPER1_EL0(15),
>  
> -	{ SYS_DESC(SYS_CNTPCT_EL0), access_arch_timer },
> -	{ SYS_DESC(SYS_CNTVCT_EL0), access_arch_timer },
> +	{ SYS_DESC(SYS_CNTPCT_EL0), .access = access_arch_timer,
> +	  .get_user = arch_timer_get_user, .set_user = arch_timer_set_user },
> +	{ SYS_DESC(SYS_CNTVCT_EL0), .access = access_arch_timer,
> +	  .get_user = arch_timer_get_user, .set_user = arch_timer_set_user },
>  	{ SYS_DESC(SYS_CNTPCTSS_EL0), access_arch_timer },
>  	{ SYS_DESC(SYS_CNTVCTSS_EL0), access_arch_timer },
>  	{ SYS_DESC(SYS_CNTP_TVAL_EL0), access_arch_timer },

Thanks,
Joey



More information about the linux-arm-kernel mailing list