diff mbox series

[10/14] KVM: arm/arm64: consolidate arch timer trap handlers

Message ID 20190124140032.8588-11-christoffer.dall@arm.com (mailing list archive)
State New, archived
Headers show
Series KVM: arm/arm64: Various rework in preparation of nested virt support | expand

Commit Message

Christoffer Dall Jan. 24, 2019, 2 p.m. UTC
From: Andre Przywara <andre.przywara@arm.com>

At the moment we have separate system register emulation handlers for
each timer register. Actually they are quite similar, and we rely on
kvm_arm_timer_[gs]et_reg() for the actual emulation anyways, so let's
just merge all of those handlers into one function, which just marshalls
the arguments and then hands off to a set of common accessors.
This makes extending the emulation to include EL2 timers much easier.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
[Fixed 32-bit VM breakage and reduced to reworking existing code]
Signed-off-by: Christoffer Dall <christoffer.dall@arm.com>
[Fixed 32bit host, general cleanup]
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm/kvm/coproc.c           |  23 +++---
 arch/arm64/include/asm/sysreg.h |   4 +
 arch/arm64/kvm/sys_regs.c       |  80 +++++++++++---------
 include/kvm/arm_arch_timer.h    |  23 ++++++
 virt/kvm/arm/arch_timer.c       | 129 +++++++++++++++++++++++++++-----
 5 files changed, 196 insertions(+), 63 deletions(-)

Comments

Julien Thierry Jan. 25, 2019, 12:33 p.m. UTC | #1
Hi,

I'm wondering, could this patch be split in two? One for the
introduction of kvm_arm_timer_read_sysreg() +
kvm_arm_timer_write_sysreg() and the other for merging the handlers into
a single function?



On 24/01/2019 14:00, Christoffer Dall wrote:
> From: Andre Przywara <andre.przywara@arm.com>
> 
> At the moment we have separate system register emulation handlers for
> each timer register. Actually they are quite similar, and we rely on
> kvm_arm_timer_[gs]et_reg() for the actual emulation anyways, so let's
> just merge all of those handlers into one function, which just marshalls
> the arguments and then hands off to a set of common accessors.
> This makes extending the emulation to include EL2 timers much easier.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> [Fixed 32-bit VM breakage and reduced to reworking existing code]
> Signed-off-by: Christoffer Dall <christoffer.dall@arm.com>
> [Fixed 32bit host, general cleanup]
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  arch/arm/kvm/coproc.c           |  23 +++---
>  arch/arm64/include/asm/sysreg.h |   4 +
>  arch/arm64/kvm/sys_regs.c       |  80 +++++++++++---------
>  include/kvm/arm_arch_timer.h    |  23 ++++++
>  virt/kvm/arm/arch_timer.c       | 129 +++++++++++++++++++++++++++-----
>  5 files changed, 196 insertions(+), 63 deletions(-)
> 
> diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
> index 222c1635bc7a..51863364f8d1 100644
> --- a/arch/arm/kvm/coproc.c
> +++ b/arch/arm/kvm/coproc.c
> @@ -293,15 +293,16 @@ static bool access_cntp_tval(struct kvm_vcpu *vcpu,
>  			     const struct coproc_params *p,
>  			     const struct coproc_reg *r)
>  {
> -	u64 now = kvm_phys_timer_read();
> -	u64 val;
> +	u32 val;
>  
>  	if (p->is_write) {
>  		val = *vcpu_reg(vcpu, p->Rt1);
> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL, val + now);
> +		kvm_arm_timer_write_sysreg(vcpu,
> +					   TIMER_PTIMER, TIMER_REG_TVAL, val);
>  	} else {
> -		val = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
> -		*vcpu_reg(vcpu, p->Rt1) = val - now;
> +		val = kvm_arm_timer_read_sysreg(vcpu,
> +						TIMER_PTIMER, TIMER_REG_TVAL);
> +		*vcpu_reg(vcpu, p->Rt1) = val;
>  	}
>  
>  	return true;
> @@ -315,9 +316,11 @@ static bool access_cntp_ctl(struct kvm_vcpu *vcpu,
>  
>  	if (p->is_write) {
>  		val = *vcpu_reg(vcpu, p->Rt1);
> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CTL, val);
> +		kvm_arm_timer_write_sysreg(vcpu,
> +					   TIMER_PTIMER, TIMER_REG_CTL, val);
>  	} else {
> -		val = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CTL);
> +		val = kvm_arm_timer_read_sysreg(vcpu,
> +						TIMER_PTIMER, TIMER_REG_CTL);
>  		*vcpu_reg(vcpu, p->Rt1) = val;
>  	}
>  
> @@ -333,9 +336,11 @@ static bool access_cntp_cval(struct kvm_vcpu *vcpu,
>  	if (p->is_write) {
>  		val = (u64)*vcpu_reg(vcpu, p->Rt2) << 32;
>  		val |= *vcpu_reg(vcpu, p->Rt1);
> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL, val);
> +		kvm_arm_timer_write_sysreg(vcpu,
> +					   TIMER_PTIMER, TIMER_REG_CVAL, val);
>  	} else {
> -		val = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
> +		val = kvm_arm_timer_read_sysreg(vcpu,
> +						TIMER_PTIMER, TIMER_REG_CVAL);
>  		*vcpu_reg(vcpu, p->Rt1) = val;
>  		*vcpu_reg(vcpu, p->Rt2) = val >> 32;
>  	}
> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> index 3e5650903d6d..6482e8bcf1b8 100644
> --- a/arch/arm64/include/asm/sysreg.h
> +++ b/arch/arm64/include/asm/sysreg.h
> @@ -392,6 +392,10 @@
>  #define SYS_CNTP_CTL_EL0		sys_reg(3, 3, 14, 2, 1)
>  #define SYS_CNTP_CVAL_EL0		sys_reg(3, 3, 14, 2, 2)
>  
> +#define SYS_AARCH32_CNTP_TVAL		sys_reg(0, 0, 14, 2, 0)
> +#define SYS_AARCH32_CNTP_CTL		sys_reg(0, 0, 14, 2, 1)
> +#define SYS_AARCH32_CNTP_CVAL		sys_reg(0, 2, 0, 14, 0)
> +
>  #define __PMEV_op2(n)			((n) & 0x7)
>  #define __CNTR_CRm(n)			(0x8 | (((n) >> 3) & 0x3))
>  #define SYS_PMEVCNTRn_EL0(n)		sys_reg(3, 3, 14, __CNTR_CRm(n), __PMEV_op2(n))
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 1a5bea4285e4..65ea63366c67 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -990,44 +990,51 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
>  	{ SYS_DESC(SYS_PMEVTYPERn_EL0(n)),					\
>  	  access_pmu_evtyper, reset_unknown, (PMEVTYPER0_EL0 + n), }
>  
> -static bool access_cntp_tval(struct kvm_vcpu *vcpu,
> -		struct sys_reg_params *p,
> -		const struct sys_reg_desc *r)
> +static bool access_arch_timer(struct kvm_vcpu *vcpu,
> +			      struct sys_reg_params *p,
> +			      const struct sys_reg_desc *r)
>  {
> -	u64 now = kvm_phys_timer_read();
> -	u64 cval;
> +	enum kvm_arch_timers tmr;
> +	enum kvm_arch_timer_regs treg;
> +	u64 reg = reg_to_encoding(r);
>  
> -	if (p->is_write) {
> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL,
> -				      p->regval + now);
> -	} else {
> -		cval = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
> -		p->regval = cval - now;
> +	switch (reg) {
> +	case SYS_CNTP_TVAL_EL0:
> +	case SYS_CNTP_CTL_EL0:
> +	case SYS_CNTP_CVAL_EL0:
> +	case SYS_AARCH32_CNTP_TVAL:
> +	case SYS_AARCH32_CNTP_CTL:
> +	case SYS_AARCH32_CNTP_CVAL:
> +		tmr = TIMER_PTIMER;
> +		break;
> +	default:
> +		BUG();
>  	}
>  
> -	return true;
> -}
> +	switch (reg) {

I find having two consecutive switch on the same element a bit weird
(and takes a lot of space).

Either I'd merge the two since the valid cases are the same, or I'd put
them in separate function "reg_get_timer(reg)",
"reg_get_timer_reg(reg)". (Can probably fit those in
include/kvm/arm_arch_timer.h)

> +	case SYS_CNTP_CVAL_EL0:
> +	case SYS_AARCH32_CNTP_CVAL:
> +		treg = TIMER_REG_CVAL;
> +		break;
>  
> -static bool access_cntp_ctl(struct kvm_vcpu *vcpu,
> -		struct sys_reg_params *p,
> -		const struct sys_reg_desc *r)
> -{
> -	if (p->is_write)
> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CTL, p->regval);
> -	else
> -		p->regval = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CTL);
> +	case SYS_CNTP_TVAL_EL0:
> +	case SYS_AARCH32_CNTP_TVAL:
> +		treg = TIMER_REG_TVAL;
> +		break;
>  
> -	return true;
> -}
> +	case SYS_CNTP_CTL_EL0:
> +	case SYS_AARCH32_CNTP_CTL:
> +		treg = TIMER_REG_CTL;
> +		break;
> +
> +	default:
> +		BUG();
> +	}
>  
> -static bool access_cntp_cval(struct kvm_vcpu *vcpu,
> -		struct sys_reg_params *p,
> -		const struct sys_reg_desc *r)
> -{
>  	if (p->is_write)
> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL, p->regval);
> +		kvm_arm_timer_write_sysreg(vcpu, tmr, treg, p->regval);
>  	else
> -		p->regval = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
> +		p->regval = kvm_arm_timer_read_sysreg(vcpu, tmr, treg);
>  
>  	return true;
>  }
> @@ -1392,9 +1399,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>  	{ SYS_DESC(SYS_TPIDR_EL0), NULL, reset_unknown, TPIDR_EL0 },
>  	{ SYS_DESC(SYS_TPIDRRO_EL0), NULL, reset_unknown, TPIDRRO_EL0 },
>  
> -	{ SYS_DESC(SYS_CNTP_TVAL_EL0), access_cntp_tval },
> -	{ SYS_DESC(SYS_CNTP_CTL_EL0), access_cntp_ctl },
> -	{ SYS_DESC(SYS_CNTP_CVAL_EL0), access_cntp_cval },
> +	{ SYS_DESC(SYS_CNTP_TVAL_EL0), access_arch_timer },
> +	{ SYS_DESC(SYS_CNTP_CTL_EL0), access_arch_timer },
> +	{ SYS_DESC(SYS_CNTP_CVAL_EL0), access_arch_timer },
>  
>  	/* PMEVCNTRn_EL0 */
>  	PMU_PMEVCNTR_EL0(0),
> @@ -1715,10 +1722,9 @@ static const struct sys_reg_desc cp15_regs[] = {
>  
>  	{ Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
>  
> -	/* CNTP_TVAL */
> -	{ Op1( 0), CRn(14), CRm( 2), Op2( 0), access_cntp_tval },
> -	/* CNTP_CTL */
> -	{ Op1( 0), CRn(14), CRm( 2), Op2( 1), access_cntp_ctl },
> +	/* Arch Tmers */
> +	{ SYS_DESC(SYS_AARCH32_CNTP_TVAL), access_arch_timer },
> +	{ SYS_DESC(SYS_AARCH32_CNTP_CTL), access_arch_timer },
>  
>  	/* PMEVCNTRn */
>  	PMU_PMEVCNTR(0),
> @@ -1795,7 +1801,7 @@ static const struct sys_reg_desc cp15_64_regs[] = {
>  	{ Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR1 },
>  	{ Op1( 1), CRn( 0), CRm(12), Op2( 0), access_gic_sgi }, /* ICC_ASGI1R */
>  	{ Op1( 2), CRn( 0), CRm(12), Op2( 0), access_gic_sgi }, /* ICC_SGI0R */
> -	{ Op1( 2), CRn( 0), CRm(14), Op2( 0), access_cntp_cval },
> +	{ SYS_DESC(SYS_AARCH32_CNTP_CVAL),    access_arch_timer },
>  };
>  
>  /* Target specific emulation tables */
> diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
> index d6e6a45d1d24..d26b7fde9935 100644
> --- a/include/kvm/arm_arch_timer.h
> +++ b/include/kvm/arm_arch_timer.h
> @@ -22,6 +22,19 @@
>  #include <linux/clocksource.h>
>  #include <linux/hrtimer.h>
>  
> +enum kvm_arch_timers {
> +	TIMER_PTIMER,
> +	TIMER_VTIMER,
> +	NR_KVM_TIMERS
> +};
> +
> +enum kvm_arch_timer_regs {
> +	TIMER_REG_CNT,
> +	TIMER_REG_CVAL,
> +	TIMER_REG_TVAL,
> +	TIMER_REG_CTL,
> +};
> +
>  struct arch_timer_context {
>  	/* Registers: control register, timer value */
>  	u32				cnt_ctl;
> @@ -87,5 +100,15 @@ bool kvm_arch_timer_get_input_level(int vintid);
>  
>  #define vcpu_vtimer(v)	(&(v)->arch.timer_cpu.vtimer)
>  #define vcpu_ptimer(v)	(&(v)->arch.timer_cpu.ptimer)
> +#define vcpu_get_timer(v,t)					\
> +	(t == TIMER_VTIMER ? vcpu_vtimer(v) : vcpu_ptimer(v))
> +
> +u64 kvm_arm_timer_read_sysreg(struct kvm_vcpu *vcpu,
> +			      enum kvm_arch_timers tmr,
> +			      enum kvm_arch_timer_regs treg);
> +void kvm_arm_timer_write_sysreg(struct kvm_vcpu *vcpu,
> +				enum kvm_arch_timers tmr,
> +				enum kvm_arch_timer_regs treg,
> +				u64 val);
> 
>  #endif
> diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
> index 4986028d9829..9502bb91776b 100644
> --- a/virt/kvm/arm/arch_timer.c
> +++ b/virt/kvm/arm/arch_timer.c
> @@ -25,6 +25,7 @@
>  
>  #include <clocksource/arm_arch_timer.h>
>  #include <asm/arch_timer.h>
> +#include <asm/kvm_emulate.h>
>  #include <asm/kvm_hyp.h>
>  
>  #include <kvm/arm_vgic.h>
> @@ -52,6 +53,13 @@ static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx);
>  static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
>  				 struct arch_timer_context *timer_ctx);
>  static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx);
> +static void kvm_arm_timer_write(struct kvm_vcpu *vcpu,
> +				struct arch_timer_context *timer,
> +				enum kvm_arch_timer_regs treg,
> +				u64 val);
> +static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
> +			      struct arch_timer_context *timer,
> +			      enum kvm_arch_timer_regs treg);
>  
>  u64 kvm_phys_timer_read(void)
>  {
> @@ -628,24 +636,25 @@ static void kvm_timer_init_interrupt(void *info)
>  
>  int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
>  {
> -	struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
> -	struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
> -
>  	switch (regid) {
>  	case KVM_REG_ARM_TIMER_CTL:
> -		vtimer->cnt_ctl = value & ~ARCH_TIMER_CTRL_IT_STAT;
> +		kvm_arm_timer_write(vcpu,
> +				    vcpu_vtimer(vcpu), TIMER_REG_CTL, value);
>  		break;
>  	case KVM_REG_ARM_TIMER_CNT:
>  		update_vtimer_cntvoff(vcpu, kvm_phys_timer_read() - value);
>  		break;
>  	case KVM_REG_ARM_TIMER_CVAL:
> -		vtimer->cnt_cval = value;
> +		kvm_arm_timer_write(vcpu,
> +				    vcpu_vtimer(vcpu), TIMER_REG_CVAL, value);
>  		break;
>  	case KVM_REG_ARM_PTIMER_CTL:
> -		ptimer->cnt_ctl = value & ~ARCH_TIMER_CTRL_IT_STAT;
> +		kvm_arm_timer_write(vcpu,
> +				    vcpu_ptimer(vcpu), TIMER_REG_CTL, value);
>  		break;
>  	case KVM_REG_ARM_PTIMER_CVAL:
> -		ptimer->cnt_cval = value;
> +		kvm_arm_timer_write(vcpu,
> +				    vcpu_ptimer(vcpu), TIMER_REG_CVAL, value);
>  		break;
>  
>  	default:
> @@ -672,26 +681,112 @@ static u64 read_timer_ctl(struct arch_timer_context *timer)
>  
>  u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
>  {
> -	struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
> -	struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
> -
>  	switch (regid) {
>  	case KVM_REG_ARM_TIMER_CTL:
> -		return read_timer_ctl(vtimer);
> +		return kvm_arm_timer_read(vcpu,
> +					  vcpu_vtimer(vcpu), TIMER_REG_CTL);
>  	case KVM_REG_ARM_TIMER_CNT:
> -		return kvm_phys_timer_read() - vtimer->cntvoff;
> +		return kvm_arm_timer_read(vcpu,
> +					  vcpu_vtimer(vcpu), TIMER_REG_CNT);
>  	case KVM_REG_ARM_TIMER_CVAL:
> -		return vtimer->cnt_cval;
> +		return kvm_arm_timer_read(vcpu,
> +					  vcpu_vtimer(vcpu), TIMER_REG_CVAL);
>  	case KVM_REG_ARM_PTIMER_CTL:
> -		return read_timer_ctl(ptimer);
> -	case KVM_REG_ARM_PTIMER_CVAL:
> -		return ptimer->cnt_cval;
> +		return kvm_arm_timer_read(vcpu,
> +					  vcpu_ptimer(vcpu), TIMER_REG_CTL);
>  	case KVM_REG_ARM_PTIMER_CNT:
> -		return kvm_phys_timer_read();
> +		return kvm_arm_timer_read(vcpu,
> +					  vcpu_vtimer(vcpu), TIMER_REG_CNT);
> +	case KVM_REG_ARM_PTIMER_CVAL:
> +		return kvm_arm_timer_read(vcpu,
> +					  vcpu_ptimer(vcpu), TIMER_REG_CVAL);
>  	}
>  	return (u64)-1;
>  }
>  
> +static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
> +			      struct arch_timer_context *timer,
> +			      enum kvm_arch_timer_regs treg)
> +{
> +	u64 val;
> +
> +	switch (treg) {
> +	case TIMER_REG_TVAL:
> +		val = kvm_phys_timer_read() - timer->cntvoff - timer->cnt_cval;
> +		break;
> +
> +	case TIMER_REG_CTL:
> +		val = read_timer_ctl(timer);
> +		break;
> +
> +	case TIMER_REG_CVAL:
> +		val = timer->cnt_cval;
> +		break;
> +
> +	case TIMER_REG_CNT:
> +		val = kvm_phys_timer_read() - timer->cntvoff;

Unless you really don't want people to read this register, you might
want to add a "break;" here :) .

> +
> +	default:
> +		BUG();
> +	}
> +
> +	return val;
> +}

Cheers,
Marc Zyngier Jan. 30, 2019, 5:38 p.m. UTC | #2
On 25/01/2019 12:33, Julien Thierry wrote:
> Hi,
> 
> I'm wondering, could this patch be split in two? One for the
> introduction of kvm_arm_timer_read_sysreg() +
> kvm_arm_timer_write_sysreg() and the other for merging the handlers into
> a single function?

Maybe. I'm not sure it brings us much though.

> 
> 
> 
> On 24/01/2019 14:00, Christoffer Dall wrote:
>> From: Andre Przywara <andre.przywara@arm.com>
>>
>> At the moment we have separate system register emulation handlers for
>> each timer register. Actually they are quite similar, and we rely on
>> kvm_arm_timer_[gs]et_reg() for the actual emulation anyways, so let's
>> just merge all of those handlers into one function, which just marshalls
>> the arguments and then hands off to a set of common accessors.
>> This makes extending the emulation to include EL2 timers much easier.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> [Fixed 32-bit VM breakage and reduced to reworking existing code]
>> Signed-off-by: Christoffer Dall <christoffer.dall@arm.com>
>> [Fixed 32bit host, general cleanup]
>> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>>  arch/arm/kvm/coproc.c           |  23 +++---
>>  arch/arm64/include/asm/sysreg.h |   4 +
>>  arch/arm64/kvm/sys_regs.c       |  80 +++++++++++---------
>>  include/kvm/arm_arch_timer.h    |  23 ++++++
>>  virt/kvm/arm/arch_timer.c       | 129 +++++++++++++++++++++++++++-----
>>  5 files changed, 196 insertions(+), 63 deletions(-)
>>
>> diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
>> index 222c1635bc7a..51863364f8d1 100644
>> --- a/arch/arm/kvm/coproc.c
>> +++ b/arch/arm/kvm/coproc.c
>> @@ -293,15 +293,16 @@ static bool access_cntp_tval(struct kvm_vcpu *vcpu,
>>  			     const struct coproc_params *p,
>>  			     const struct coproc_reg *r)
>>  {
>> -	u64 now = kvm_phys_timer_read();
>> -	u64 val;
>> +	u32 val;
>>  
>>  	if (p->is_write) {
>>  		val = *vcpu_reg(vcpu, p->Rt1);
>> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL, val + now);
>> +		kvm_arm_timer_write_sysreg(vcpu,
>> +					   TIMER_PTIMER, TIMER_REG_TVAL, val);
>>  	} else {
>> -		val = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
>> -		*vcpu_reg(vcpu, p->Rt1) = val - now;
>> +		val = kvm_arm_timer_read_sysreg(vcpu,
>> +						TIMER_PTIMER, TIMER_REG_TVAL);
>> +		*vcpu_reg(vcpu, p->Rt1) = val;
>>  	}
>>  
>>  	return true;
>> @@ -315,9 +316,11 @@ static bool access_cntp_ctl(struct kvm_vcpu *vcpu,
>>  
>>  	if (p->is_write) {
>>  		val = *vcpu_reg(vcpu, p->Rt1);
>> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CTL, val);
>> +		kvm_arm_timer_write_sysreg(vcpu,
>> +					   TIMER_PTIMER, TIMER_REG_CTL, val);
>>  	} else {
>> -		val = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CTL);
>> +		val = kvm_arm_timer_read_sysreg(vcpu,
>> +						TIMER_PTIMER, TIMER_REG_CTL);
>>  		*vcpu_reg(vcpu, p->Rt1) = val;
>>  	}
>>  
>> @@ -333,9 +336,11 @@ static bool access_cntp_cval(struct kvm_vcpu *vcpu,
>>  	if (p->is_write) {
>>  		val = (u64)*vcpu_reg(vcpu, p->Rt2) << 32;
>>  		val |= *vcpu_reg(vcpu, p->Rt1);
>> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL, val);
>> +		kvm_arm_timer_write_sysreg(vcpu,
>> +					   TIMER_PTIMER, TIMER_REG_CVAL, val);
>>  	} else {
>> -		val = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
>> +		val = kvm_arm_timer_read_sysreg(vcpu,
>> +						TIMER_PTIMER, TIMER_REG_CVAL);
>>  		*vcpu_reg(vcpu, p->Rt1) = val;
>>  		*vcpu_reg(vcpu, p->Rt2) = val >> 32;
>>  	}
>> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
>> index 3e5650903d6d..6482e8bcf1b8 100644
>> --- a/arch/arm64/include/asm/sysreg.h
>> +++ b/arch/arm64/include/asm/sysreg.h
>> @@ -392,6 +392,10 @@
>>  #define SYS_CNTP_CTL_EL0		sys_reg(3, 3, 14, 2, 1)
>>  #define SYS_CNTP_CVAL_EL0		sys_reg(3, 3, 14, 2, 2)
>>  
>> +#define SYS_AARCH32_CNTP_TVAL		sys_reg(0, 0, 14, 2, 0)
>> +#define SYS_AARCH32_CNTP_CTL		sys_reg(0, 0, 14, 2, 1)
>> +#define SYS_AARCH32_CNTP_CVAL		sys_reg(0, 2, 0, 14, 0)
>> +
>>  #define __PMEV_op2(n)			((n) & 0x7)
>>  #define __CNTR_CRm(n)			(0x8 | (((n) >> 3) & 0x3))
>>  #define SYS_PMEVCNTRn_EL0(n)		sys_reg(3, 3, 14, __CNTR_CRm(n), __PMEV_op2(n))
>> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
>> index 1a5bea4285e4..65ea63366c67 100644
>> --- a/arch/arm64/kvm/sys_regs.c
>> +++ b/arch/arm64/kvm/sys_regs.c
>> @@ -990,44 +990,51 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
>>  	{ SYS_DESC(SYS_PMEVTYPERn_EL0(n)),					\
>>  	  access_pmu_evtyper, reset_unknown, (PMEVTYPER0_EL0 + n), }
>>  
>> -static bool access_cntp_tval(struct kvm_vcpu *vcpu,
>> -		struct sys_reg_params *p,
>> -		const struct sys_reg_desc *r)
>> +static bool access_arch_timer(struct kvm_vcpu *vcpu,
>> +			      struct sys_reg_params *p,
>> +			      const struct sys_reg_desc *r)
>>  {
>> -	u64 now = kvm_phys_timer_read();
>> -	u64 cval;
>> +	enum kvm_arch_timers tmr;
>> +	enum kvm_arch_timer_regs treg;
>> +	u64 reg = reg_to_encoding(r);
>>  
>> -	if (p->is_write) {
>> -		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL,
>> -				      p->regval + now);
>> -	} else {
>> -		cval = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
>> -		p->regval = cval - now;
>> +	switch (reg) {
>> +	case SYS_CNTP_TVAL_EL0:
>> +	case SYS_CNTP_CTL_EL0:
>> +	case SYS_CNTP_CVAL_EL0:
>> +	case SYS_AARCH32_CNTP_TVAL:
>> +	case SYS_AARCH32_CNTP_CTL:
>> +	case SYS_AARCH32_CNTP_CVAL:
>> +		tmr = TIMER_PTIMER;
>> +		break;
>> +	default:
>> +		BUG();
>>  	}
>>  
>> -	return true;
>> -}
>> +	switch (reg) {
> 
> I find having two consecutive switch on the same element a bit weird
> (and takes a lot of space).
> 
> Either I'd merge the two since the valid cases are the same, or I'd put
> them in separate function "reg_get_timer(reg)",
> "reg_get_timer_reg(reg)". (Can probably fit those in
> include/kvm/arm_arch_timer.h)

Nah, I'll just merge them. I wrote it this way as it was just easier to
reason about one thing at a time, but in the end this is (as you
noticed) fairly pointless.

I guess I'll have more fun rebasing the NV stuff on top! ;-)

[...]

>> +static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
>> +			      struct arch_timer_context *timer,
>> +			      enum kvm_arch_timer_regs treg)
>> +{
>> +	u64 val;
>> +
>> +	switch (treg) {
>> +	case TIMER_REG_TVAL:
>> +		val = kvm_phys_timer_read() - timer->cntvoff - timer->cnt_cval;
>> +		break;
>> +
>> +	case TIMER_REG_CTL:
>> +		val = read_timer_ctl(timer);
>> +		break;
>> +
>> +	case TIMER_REG_CVAL:
>> +		val = timer->cnt_cval;
>> +		break;
>> +
>> +	case TIMER_REG_CNT:
>> +		val = kvm_phys_timer_read() - timer->cntvoff;
> 
> Unless you really don't want people to read this register, you might
> want to add a "break;" here :) .

Timers are for wimps. Real hax0rz use a calibrated loop with interrupts
disabled! ;-)

Thanks for the heads up!

	M.
diff mbox series

Patch

diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
index 222c1635bc7a..51863364f8d1 100644
--- a/arch/arm/kvm/coproc.c
+++ b/arch/arm/kvm/coproc.c
@@ -293,15 +293,16 @@  static bool access_cntp_tval(struct kvm_vcpu *vcpu,
 			     const struct coproc_params *p,
 			     const struct coproc_reg *r)
 {
-	u64 now = kvm_phys_timer_read();
-	u64 val;
+	u32 val;
 
 	if (p->is_write) {
 		val = *vcpu_reg(vcpu, p->Rt1);
-		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL, val + now);
+		kvm_arm_timer_write_sysreg(vcpu,
+					   TIMER_PTIMER, TIMER_REG_TVAL, val);
 	} else {
-		val = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
-		*vcpu_reg(vcpu, p->Rt1) = val - now;
+		val = kvm_arm_timer_read_sysreg(vcpu,
+						TIMER_PTIMER, TIMER_REG_TVAL);
+		*vcpu_reg(vcpu, p->Rt1) = val;
 	}
 
 	return true;
@@ -315,9 +316,11 @@  static bool access_cntp_ctl(struct kvm_vcpu *vcpu,
 
 	if (p->is_write) {
 		val = *vcpu_reg(vcpu, p->Rt1);
-		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CTL, val);
+		kvm_arm_timer_write_sysreg(vcpu,
+					   TIMER_PTIMER, TIMER_REG_CTL, val);
 	} else {
-		val = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CTL);
+		val = kvm_arm_timer_read_sysreg(vcpu,
+						TIMER_PTIMER, TIMER_REG_CTL);
 		*vcpu_reg(vcpu, p->Rt1) = val;
 	}
 
@@ -333,9 +336,11 @@  static bool access_cntp_cval(struct kvm_vcpu *vcpu,
 	if (p->is_write) {
 		val = (u64)*vcpu_reg(vcpu, p->Rt2) << 32;
 		val |= *vcpu_reg(vcpu, p->Rt1);
-		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL, val);
+		kvm_arm_timer_write_sysreg(vcpu,
+					   TIMER_PTIMER, TIMER_REG_CVAL, val);
 	} else {
-		val = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
+		val = kvm_arm_timer_read_sysreg(vcpu,
+						TIMER_PTIMER, TIMER_REG_CVAL);
 		*vcpu_reg(vcpu, p->Rt1) = val;
 		*vcpu_reg(vcpu, p->Rt2) = val >> 32;
 	}
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 3e5650903d6d..6482e8bcf1b8 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -392,6 +392,10 @@ 
 #define SYS_CNTP_CTL_EL0		sys_reg(3, 3, 14, 2, 1)
 #define SYS_CNTP_CVAL_EL0		sys_reg(3, 3, 14, 2, 2)
 
+#define SYS_AARCH32_CNTP_TVAL		sys_reg(0, 0, 14, 2, 0)
+#define SYS_AARCH32_CNTP_CTL		sys_reg(0, 0, 14, 2, 1)
+#define SYS_AARCH32_CNTP_CVAL		sys_reg(0, 2, 0, 14, 0)
+
 #define __PMEV_op2(n)			((n) & 0x7)
 #define __CNTR_CRm(n)			(0x8 | (((n) >> 3) & 0x3))
 #define SYS_PMEVCNTRn_EL0(n)		sys_reg(3, 3, 14, __CNTR_CRm(n), __PMEV_op2(n))
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1a5bea4285e4..65ea63366c67 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -990,44 +990,51 @@  static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
 	{ SYS_DESC(SYS_PMEVTYPERn_EL0(n)),					\
 	  access_pmu_evtyper, reset_unknown, (PMEVTYPER0_EL0 + n), }
 
-static bool access_cntp_tval(struct kvm_vcpu *vcpu,
-		struct sys_reg_params *p,
-		const struct sys_reg_desc *r)
+static bool access_arch_timer(struct kvm_vcpu *vcpu,
+			      struct sys_reg_params *p,
+			      const struct sys_reg_desc *r)
 {
-	u64 now = kvm_phys_timer_read();
-	u64 cval;
+	enum kvm_arch_timers tmr;
+	enum kvm_arch_timer_regs treg;
+	u64 reg = reg_to_encoding(r);
 
-	if (p->is_write) {
-		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL,
-				      p->regval + now);
-	} else {
-		cval = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
-		p->regval = cval - now;
+	switch (reg) {
+	case SYS_CNTP_TVAL_EL0:
+	case SYS_CNTP_CTL_EL0:
+	case SYS_CNTP_CVAL_EL0:
+	case SYS_AARCH32_CNTP_TVAL:
+	case SYS_AARCH32_CNTP_CTL:
+	case SYS_AARCH32_CNTP_CVAL:
+		tmr = TIMER_PTIMER;
+		break;
+	default:
+		BUG();
 	}
 
-	return true;
-}
+	switch (reg) {
+	case SYS_CNTP_CVAL_EL0:
+	case SYS_AARCH32_CNTP_CVAL:
+		treg = TIMER_REG_CVAL;
+		break;
 
-static bool access_cntp_ctl(struct kvm_vcpu *vcpu,
-		struct sys_reg_params *p,
-		const struct sys_reg_desc *r)
-{
-	if (p->is_write)
-		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CTL, p->regval);
-	else
-		p->regval = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CTL);
+	case SYS_CNTP_TVAL_EL0:
+	case SYS_AARCH32_CNTP_TVAL:
+		treg = TIMER_REG_TVAL;
+		break;
 
-	return true;
-}
+	case SYS_CNTP_CTL_EL0:
+	case SYS_AARCH32_CNTP_CTL:
+		treg = TIMER_REG_CTL;
+		break;
+
+	default:
+		BUG();
+	}
 
-static bool access_cntp_cval(struct kvm_vcpu *vcpu,
-		struct sys_reg_params *p,
-		const struct sys_reg_desc *r)
-{
 	if (p->is_write)
-		kvm_arm_timer_set_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL, p->regval);
+		kvm_arm_timer_write_sysreg(vcpu, tmr, treg, p->regval);
 	else
-		p->regval = kvm_arm_timer_get_reg(vcpu, KVM_REG_ARM_PTIMER_CVAL);
+		p->regval = kvm_arm_timer_read_sysreg(vcpu, tmr, treg);
 
 	return true;
 }
@@ -1392,9 +1399,9 @@  static const struct sys_reg_desc sys_reg_descs[] = {
 	{ SYS_DESC(SYS_TPIDR_EL0), NULL, reset_unknown, TPIDR_EL0 },
 	{ SYS_DESC(SYS_TPIDRRO_EL0), NULL, reset_unknown, TPIDRRO_EL0 },
 
-	{ SYS_DESC(SYS_CNTP_TVAL_EL0), access_cntp_tval },
-	{ SYS_DESC(SYS_CNTP_CTL_EL0), access_cntp_ctl },
-	{ SYS_DESC(SYS_CNTP_CVAL_EL0), access_cntp_cval },
+	{ SYS_DESC(SYS_CNTP_TVAL_EL0), access_arch_timer },
+	{ SYS_DESC(SYS_CNTP_CTL_EL0), access_arch_timer },
+	{ SYS_DESC(SYS_CNTP_CVAL_EL0), access_arch_timer },
 
 	/* PMEVCNTRn_EL0 */
 	PMU_PMEVCNTR_EL0(0),
@@ -1715,10 +1722,9 @@  static const struct sys_reg_desc cp15_regs[] = {
 
 	{ Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
 
-	/* CNTP_TVAL */
-	{ Op1( 0), CRn(14), CRm( 2), Op2( 0), access_cntp_tval },
-	/* CNTP_CTL */
-	{ Op1( 0), CRn(14), CRm( 2), Op2( 1), access_cntp_ctl },
+	/* Arch Tmers */
+	{ SYS_DESC(SYS_AARCH32_CNTP_TVAL), access_arch_timer },
+	{ SYS_DESC(SYS_AARCH32_CNTP_CTL), access_arch_timer },
 
 	/* PMEVCNTRn */
 	PMU_PMEVCNTR(0),
@@ -1795,7 +1801,7 @@  static const struct sys_reg_desc cp15_64_regs[] = {
 	{ Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR1 },
 	{ Op1( 1), CRn( 0), CRm(12), Op2( 0), access_gic_sgi }, /* ICC_ASGI1R */
 	{ Op1( 2), CRn( 0), CRm(12), Op2( 0), access_gic_sgi }, /* ICC_SGI0R */
-	{ Op1( 2), CRn( 0), CRm(14), Op2( 0), access_cntp_cval },
+	{ SYS_DESC(SYS_AARCH32_CNTP_CVAL),    access_arch_timer },
 };
 
 /* Target specific emulation tables */
diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
index d6e6a45d1d24..d26b7fde9935 100644
--- a/include/kvm/arm_arch_timer.h
+++ b/include/kvm/arm_arch_timer.h
@@ -22,6 +22,19 @@ 
 #include <linux/clocksource.h>
 #include <linux/hrtimer.h>
 
+enum kvm_arch_timers {
+	TIMER_PTIMER,
+	TIMER_VTIMER,
+	NR_KVM_TIMERS
+};
+
+enum kvm_arch_timer_regs {
+	TIMER_REG_CNT,
+	TIMER_REG_CVAL,
+	TIMER_REG_TVAL,
+	TIMER_REG_CTL,
+};
+
 struct arch_timer_context {
 	/* Registers: control register, timer value */
 	u32				cnt_ctl;
@@ -87,5 +100,15 @@  bool kvm_arch_timer_get_input_level(int vintid);
 
 #define vcpu_vtimer(v)	(&(v)->arch.timer_cpu.vtimer)
 #define vcpu_ptimer(v)	(&(v)->arch.timer_cpu.ptimer)
+#define vcpu_get_timer(v,t)					\
+	(t == TIMER_VTIMER ? vcpu_vtimer(v) : vcpu_ptimer(v))
+
+u64 kvm_arm_timer_read_sysreg(struct kvm_vcpu *vcpu,
+			      enum kvm_arch_timers tmr,
+			      enum kvm_arch_timer_regs treg);
+void kvm_arm_timer_write_sysreg(struct kvm_vcpu *vcpu,
+				enum kvm_arch_timers tmr,
+				enum kvm_arch_timer_regs treg,
+				u64 val);
 
 #endif
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 4986028d9829..9502bb91776b 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -25,6 +25,7 @@ 
 
 #include <clocksource/arm_arch_timer.h>
 #include <asm/arch_timer.h>
+#include <asm/kvm_emulate.h>
 #include <asm/kvm_hyp.h>
 
 #include <kvm/arm_vgic.h>
@@ -52,6 +53,13 @@  static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx);
 static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
 				 struct arch_timer_context *timer_ctx);
 static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx);
+static void kvm_arm_timer_write(struct kvm_vcpu *vcpu,
+				struct arch_timer_context *timer,
+				enum kvm_arch_timer_regs treg,
+				u64 val);
+static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
+			      struct arch_timer_context *timer,
+			      enum kvm_arch_timer_regs treg);
 
 u64 kvm_phys_timer_read(void)
 {
@@ -628,24 +636,25 @@  static void kvm_timer_init_interrupt(void *info)
 
 int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
 {
-	struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
-	struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
-
 	switch (regid) {
 	case KVM_REG_ARM_TIMER_CTL:
-		vtimer->cnt_ctl = value & ~ARCH_TIMER_CTRL_IT_STAT;
+		kvm_arm_timer_write(vcpu,
+				    vcpu_vtimer(vcpu), TIMER_REG_CTL, value);
 		break;
 	case KVM_REG_ARM_TIMER_CNT:
 		update_vtimer_cntvoff(vcpu, kvm_phys_timer_read() - value);
 		break;
 	case KVM_REG_ARM_TIMER_CVAL:
-		vtimer->cnt_cval = value;
+		kvm_arm_timer_write(vcpu,
+				    vcpu_vtimer(vcpu), TIMER_REG_CVAL, value);
 		break;
 	case KVM_REG_ARM_PTIMER_CTL:
-		ptimer->cnt_ctl = value & ~ARCH_TIMER_CTRL_IT_STAT;
+		kvm_arm_timer_write(vcpu,
+				    vcpu_ptimer(vcpu), TIMER_REG_CTL, value);
 		break;
 	case KVM_REG_ARM_PTIMER_CVAL:
-		ptimer->cnt_cval = value;
+		kvm_arm_timer_write(vcpu,
+				    vcpu_ptimer(vcpu), TIMER_REG_CVAL, value);
 		break;
 
 	default:
@@ -672,26 +681,112 @@  static u64 read_timer_ctl(struct arch_timer_context *timer)
 
 u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
 {
-	struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
-	struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
-
 	switch (regid) {
 	case KVM_REG_ARM_TIMER_CTL:
-		return read_timer_ctl(vtimer);
+		return kvm_arm_timer_read(vcpu,
+					  vcpu_vtimer(vcpu), TIMER_REG_CTL);
 	case KVM_REG_ARM_TIMER_CNT:
-		return kvm_phys_timer_read() - vtimer->cntvoff;
+		return kvm_arm_timer_read(vcpu,
+					  vcpu_vtimer(vcpu), TIMER_REG_CNT);
 	case KVM_REG_ARM_TIMER_CVAL:
-		return vtimer->cnt_cval;
+		return kvm_arm_timer_read(vcpu,
+					  vcpu_vtimer(vcpu), TIMER_REG_CVAL);
 	case KVM_REG_ARM_PTIMER_CTL:
-		return read_timer_ctl(ptimer);
-	case KVM_REG_ARM_PTIMER_CVAL:
-		return ptimer->cnt_cval;
+		return kvm_arm_timer_read(vcpu,
+					  vcpu_ptimer(vcpu), TIMER_REG_CTL);
 	case KVM_REG_ARM_PTIMER_CNT:
-		return kvm_phys_timer_read();
+		return kvm_arm_timer_read(vcpu,
+					  vcpu_vtimer(vcpu), TIMER_REG_CNT);
+	case KVM_REG_ARM_PTIMER_CVAL:
+		return kvm_arm_timer_read(vcpu,
+					  vcpu_ptimer(vcpu), TIMER_REG_CVAL);
 	}
 	return (u64)-1;
 }
 
+static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
+			      struct arch_timer_context *timer,
+			      enum kvm_arch_timer_regs treg)
+{
+	u64 val;
+
+	switch (treg) {
+	case TIMER_REG_TVAL:
+		val = kvm_phys_timer_read() - timer->cntvoff - timer->cnt_cval;
+		break;
+
+	case TIMER_REG_CTL:
+		val = read_timer_ctl(timer);
+		break;
+
+	case TIMER_REG_CVAL:
+		val = timer->cnt_cval;
+		break;
+
+	case TIMER_REG_CNT:
+		val = kvm_phys_timer_read() - timer->cntvoff;
+
+	default:
+		BUG();
+	}
+
+	return val;
+}
+
+u64 kvm_arm_timer_read_sysreg(struct kvm_vcpu *vcpu,
+			      enum kvm_arch_timers tmr,
+			      enum kvm_arch_timer_regs treg)
+{
+	u64 val;
+
+	preempt_disable();
+	kvm_timer_vcpu_put(vcpu);
+
+	val = kvm_arm_timer_read(vcpu, vcpu_get_timer(vcpu, tmr), treg);
+
+	kvm_timer_vcpu_load(vcpu);
+	preempt_enable();
+
+	return val;
+}
+
+static void kvm_arm_timer_write(struct kvm_vcpu *vcpu,
+				struct arch_timer_context *timer,
+				enum kvm_arch_timer_regs treg,
+				u64 val)
+{
+	switch (treg) {
+	case TIMER_REG_TVAL:
+		timer->cnt_cval = val - kvm_phys_timer_read() - timer->cntvoff;
+		break;
+
+	case TIMER_REG_CTL:
+		timer->cnt_ctl = val & ~ARCH_TIMER_CTRL_IT_STAT;
+		break;
+
+	case TIMER_REG_CVAL:
+		timer->cnt_cval = val;
+		break;
+
+	default:
+		BUG();
+	}
+}
+
+void kvm_arm_timer_write_sysreg(struct kvm_vcpu *vcpu,
+				enum kvm_arch_timers tmr,
+				enum kvm_arch_timer_regs treg,
+				u64 val)
+{
+	preempt_disable();
+	kvm_timer_vcpu_put(vcpu);
+
+	kvm_arm_timer_write(vcpu, vcpu_get_timer(vcpu, tmr), treg, val);
+
+	kvm_timer_vcpu_load(vcpu);
+	preempt_enable();
+}
+
 static int kvm_timer_starting_cpu(unsigned int cpu)
 {
 	kvm_timer_init_interrupt(NULL);