|
|
|
@@ -51,6 +51,93 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
|
|
|
|
|
struct arch_timer_context *timer,
|
|
|
|
|
enum kvm_arch_timer_regs treg);
|
|
|
|
|
|
|
|
|
|
u32 timer_get_ctl(struct arch_timer_context *ctxt)
|
|
|
|
|
{
|
|
|
|
|
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
|
|
|
|
|
|
|
|
|
switch(arch_timer_ctx_index(ctxt)) {
|
|
|
|
|
case TIMER_VTIMER:
|
|
|
|
|
return __vcpu_sys_reg(vcpu, CNTV_CTL_EL0);
|
|
|
|
|
case TIMER_PTIMER:
|
|
|
|
|
return __vcpu_sys_reg(vcpu, CNTP_CTL_EL0);
|
|
|
|
|
default:
|
|
|
|
|
WARN_ON(1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 timer_get_cval(struct arch_timer_context *ctxt)
|
|
|
|
|
{
|
|
|
|
|
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
|
|
|
|
|
|
|
|
|
switch(arch_timer_ctx_index(ctxt)) {
|
|
|
|
|
case TIMER_VTIMER:
|
|
|
|
|
return __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
|
|
|
|
|
case TIMER_PTIMER:
|
|
|
|
|
return __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0);
|
|
|
|
|
default:
|
|
|
|
|
WARN_ON(1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static u64 timer_get_offset(struct arch_timer_context *ctxt)
|
|
|
|
|
{
|
|
|
|
|
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
|
|
|
|
|
|
|
|
|
switch(arch_timer_ctx_index(ctxt)) {
|
|
|
|
|
case TIMER_VTIMER:
|
|
|
|
|
return __vcpu_sys_reg(vcpu, CNTVOFF_EL2);
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void timer_set_ctl(struct arch_timer_context *ctxt, u32 ctl)
|
|
|
|
|
{
|
|
|
|
|
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
|
|
|
|
|
|
|
|
|
switch(arch_timer_ctx_index(ctxt)) {
|
|
|
|
|
case TIMER_VTIMER:
|
|
|
|
|
__vcpu_sys_reg(vcpu, CNTV_CTL_EL0) = ctl;
|
|
|
|
|
break;
|
|
|
|
|
case TIMER_PTIMER:
|
|
|
|
|
__vcpu_sys_reg(vcpu, CNTP_CTL_EL0) = ctl;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
WARN_ON(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void timer_set_cval(struct arch_timer_context *ctxt, u64 cval)
|
|
|
|
|
{
|
|
|
|
|
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
|
|
|
|
|
|
|
|
|
switch(arch_timer_ctx_index(ctxt)) {
|
|
|
|
|
case TIMER_VTIMER:
|
|
|
|
|
__vcpu_sys_reg(vcpu, CNTV_CVAL_EL0) = cval;
|
|
|
|
|
break;
|
|
|
|
|
case TIMER_PTIMER:
|
|
|
|
|
__vcpu_sys_reg(vcpu, CNTP_CVAL_EL0) = cval;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
WARN_ON(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void timer_set_offset(struct arch_timer_context *ctxt, u64 offset)
|
|
|
|
|
{
|
|
|
|
|
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
|
|
|
|
|
|
|
|
|
switch(arch_timer_ctx_index(ctxt)) {
|
|
|
|
|
case TIMER_VTIMER:
|
|
|
|
|
__vcpu_sys_reg(vcpu, CNTVOFF_EL2) = offset;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
WARN(offset, "timer %ld\n", arch_timer_ctx_index(ctxt));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 kvm_phys_timer_read(void)
|
|
|
|
|
{
|
|
|
|
|
return timecounter->cc->read(timecounter->cc);
|
|
|
|
@@ -124,8 +211,8 @@ static u64 kvm_timer_compute_delta(struct arch_timer_context *timer_ctx)
|
|
|
|
|
{
|
|
|
|
|
u64 cval, now;
|
|
|
|
|
|
|
|
|
|
cval = timer_ctx->cnt_cval;
|
|
|
|
|
now = kvm_phys_timer_read() - timer_ctx->cntvoff;
|
|
|
|
|
cval = timer_get_cval(timer_ctx);
|
|
|
|
|
now = kvm_phys_timer_read() - timer_get_offset(timer_ctx);
|
|
|
|
|
|
|
|
|
|
if (now < cval) {
|
|
|
|
|
u64 ns;
|
|
|
|
@@ -144,8 +231,8 @@ static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx)
|
|
|
|
|
{
|
|
|
|
|
WARN_ON(timer_ctx && timer_ctx->loaded);
|
|
|
|
|
return timer_ctx &&
|
|
|
|
|
!(timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_IT_MASK) &&
|
|
|
|
|
(timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_ENABLE);
|
|
|
|
|
((timer_get_ctl(timer_ctx) &
|
|
|
|
|
(ARCH_TIMER_CTRL_IT_MASK | ARCH_TIMER_CTRL_ENABLE)) == ARCH_TIMER_CTRL_ENABLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -256,8 +343,8 @@ static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx)
|
|
|
|
|
if (!kvm_timer_irq_can_fire(timer_ctx))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
cval = timer_ctx->cnt_cval;
|
|
|
|
|
now = kvm_phys_timer_read() - timer_ctx->cntvoff;
|
|
|
|
|
cval = timer_get_cval(timer_ctx);
|
|
|
|
|
now = kvm_phys_timer_read() - timer_get_offset(timer_ctx);
|
|
|
|
|
|
|
|
|
|
return cval <= now;
|
|
|
|
|
}
|
|
|
|
@@ -350,8 +437,8 @@ static void timer_save_state(struct arch_timer_context *ctx)
|
|
|
|
|
|
|
|
|
|
switch (index) {
|
|
|
|
|
case TIMER_VTIMER:
|
|
|
|
|
ctx->cnt_ctl = read_sysreg_el0(SYS_CNTV_CTL);
|
|
|
|
|
ctx->cnt_cval = read_sysreg_el0(SYS_CNTV_CVAL);
|
|
|
|
|
timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTV_CTL));
|
|
|
|
|
timer_set_cval(ctx, read_sysreg_el0(SYS_CNTV_CVAL));
|
|
|
|
|
|
|
|
|
|
/* Disable the timer */
|
|
|
|
|
write_sysreg_el0(0, SYS_CNTV_CTL);
|
|
|
|
@@ -359,8 +446,8 @@ static void timer_save_state(struct arch_timer_context *ctx)
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case TIMER_PTIMER:
|
|
|
|
|
ctx->cnt_ctl = read_sysreg_el0(SYS_CNTP_CTL);
|
|
|
|
|
ctx->cnt_cval = read_sysreg_el0(SYS_CNTP_CVAL);
|
|
|
|
|
timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTP_CTL));
|
|
|
|
|
timer_set_cval(ctx, read_sysreg_el0(SYS_CNTP_CVAL));
|
|
|
|
|
|
|
|
|
|
/* Disable the timer */
|
|
|
|
|
write_sysreg_el0(0, SYS_CNTP_CTL);
|
|
|
|
@@ -429,14 +516,14 @@ static void timer_restore_state(struct arch_timer_context *ctx)
|
|
|
|
|
|
|
|
|
|
switch (index) {
|
|
|
|
|
case TIMER_VTIMER:
|
|
|
|
|
write_sysreg_el0(ctx->cnt_cval, SYS_CNTV_CVAL);
|
|
|
|
|
write_sysreg_el0(timer_get_cval(ctx), SYS_CNTV_CVAL);
|
|
|
|
|
isb();
|
|
|
|
|
write_sysreg_el0(ctx->cnt_ctl, SYS_CNTV_CTL);
|
|
|
|
|
write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTV_CTL);
|
|
|
|
|
break;
|
|
|
|
|
case TIMER_PTIMER:
|
|
|
|
|
write_sysreg_el0(ctx->cnt_cval, SYS_CNTP_CVAL);
|
|
|
|
|
write_sysreg_el0(timer_get_cval(ctx), SYS_CNTP_CVAL);
|
|
|
|
|
isb();
|
|
|
|
|
write_sysreg_el0(ctx->cnt_ctl, SYS_CNTP_CTL);
|
|
|
|
|
write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTP_CTL);
|
|
|
|
|
break;
|
|
|
|
|
case NR_KVM_TIMERS:
|
|
|
|
|
BUG();
|
|
|
|
@@ -528,7 +615,7 @@ void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu)
|
|
|
|
|
kvm_timer_vcpu_load_nogic(vcpu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_cntvoff(map.direct_vtimer->cntvoff);
|
|
|
|
|
set_cntvoff(timer_get_offset(map.direct_vtimer));
|
|
|
|
|
|
|
|
|
|
kvm_timer_unblocking(vcpu);
|
|
|
|
|
|
|
|
|
@@ -639,8 +726,8 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)
|
|
|
|
|
* resets the timer to be disabled and unmasked and is compliant with
|
|
|
|
|
* the ARMv7 architecture.
|
|
|
|
|
*/
|
|
|
|
|
vcpu_vtimer(vcpu)->cnt_ctl = 0;
|
|
|
|
|
vcpu_ptimer(vcpu)->cnt_ctl = 0;
|
|
|
|
|
timer_set_ctl(vcpu_vtimer(vcpu), 0);
|
|
|
|
|
timer_set_ctl(vcpu_ptimer(vcpu), 0);
|
|
|
|
|
|
|
|
|
|
if (timer->enabled) {
|
|
|
|
|
kvm_timer_update_irq(vcpu, false, vcpu_vtimer(vcpu));
|
|
|
|
@@ -668,13 +755,13 @@ static void update_vtimer_cntvoff(struct kvm_vcpu *vcpu, u64 cntvoff)
|
|
|
|
|
|
|
|
|
|
mutex_lock(&kvm->lock);
|
|
|
|
|
kvm_for_each_vcpu(i, tmp, kvm)
|
|
|
|
|
vcpu_vtimer(tmp)->cntvoff = cntvoff;
|
|
|
|
|
timer_set_offset(vcpu_vtimer(tmp), cntvoff);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* When called from the vcpu create path, the CPU being created is not
|
|
|
|
|
* included in the loop above, so we just set it here as well.
|
|
|
|
|
*/
|
|
|
|
|
vcpu_vtimer(vcpu)->cntvoff = cntvoff;
|
|
|
|
|
timer_set_offset(vcpu_vtimer(vcpu), cntvoff);
|
|
|
|
|
mutex_unlock(&kvm->lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -684,9 +771,12 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
|
|
|
|
|
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
|
|
|
|
|
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
|
|
|
|
|
|
|
|
|
|
vtimer->vcpu = vcpu;
|
|
|
|
|
ptimer->vcpu = vcpu;
|
|
|
|
|
|
|
|
|
|
/* Synchronize cntvoff across all vtimers of a VM. */
|
|
|
|
|
update_vtimer_cntvoff(vcpu, kvm_phys_timer_read());
|
|
|
|
|
ptimer->cntvoff = 0;
|
|
|
|
|
timer_set_offset(ptimer, 0);
|
|
|
|
|
|
|
|
|
|
hrtimer_init(&timer->bg_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
|
|
|
|
|
timer->bg_timer.function = kvm_bg_timer_expire;
|
|
|
|
@@ -704,9 +794,6 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
|
|
|
|
|
|
|
|
|
|
vtimer->host_timer_irq_flags = host_vtimer_irq_flags;
|
|
|
|
|
ptimer->host_timer_irq_flags = host_ptimer_irq_flags;
|
|
|
|
|
|
|
|
|
|
vtimer->vcpu = vcpu;
|
|
|
|
|
ptimer->vcpu = vcpu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void kvm_timer_init_interrupt(void *info)
|
|
|
|
@@ -756,10 +843,12 @@ static u64 read_timer_ctl(struct arch_timer_context *timer)
|
|
|
|
|
* UNKNOWN when ENABLE bit is 0, so we chose to set ISTATUS bit
|
|
|
|
|
* regardless of ENABLE bit for our implementation convenience.
|
|
|
|
|
*/
|
|
|
|
|
u32 ctl = timer_get_ctl(timer);
|
|
|
|
|
|
|
|
|
|
if (!kvm_timer_compute_delta(timer))
|
|
|
|
|
return timer->cnt_ctl | ARCH_TIMER_CTRL_IT_STAT;
|
|
|
|
|
else
|
|
|
|
|
return timer->cnt_ctl;
|
|
|
|
|
ctl |= ARCH_TIMER_CTRL_IT_STAT;
|
|
|
|
|
|
|
|
|
|
return ctl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
|
|
|
|
@@ -795,8 +884,8 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
|
|
|
|
|
|
|
|
|
|
switch (treg) {
|
|
|
|
|
case TIMER_REG_TVAL:
|
|
|
|
|
val = timer->cnt_cval - kvm_phys_timer_read() + timer->cntvoff;
|
|
|
|
|
val &= lower_32_bits(val);
|
|
|
|
|
val = timer_get_cval(timer) - kvm_phys_timer_read() + timer_get_offset(timer);
|
|
|
|
|
val = lower_32_bits(val);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TIMER_REG_CTL:
|
|
|
|
@@ -804,11 +893,11 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TIMER_REG_CVAL:
|
|
|
|
|
val = timer->cnt_cval;
|
|
|
|
|
val = timer_get_cval(timer);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TIMER_REG_CNT:
|
|
|
|
|
val = kvm_phys_timer_read() - timer->cntvoff;
|
|
|
|
|
val = kvm_phys_timer_read() - timer_get_offset(timer);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
@@ -842,15 +931,15 @@ static void kvm_arm_timer_write(struct kvm_vcpu *vcpu,
|
|
|
|
|
{
|
|
|
|
|
switch (treg) {
|
|
|
|
|
case TIMER_REG_TVAL:
|
|
|
|
|
timer->cnt_cval = kvm_phys_timer_read() - timer->cntvoff + (s32)val;
|
|
|
|
|
timer_set_cval(timer, kvm_phys_timer_read() - timer_get_offset(timer) + (s32)val);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TIMER_REG_CTL:
|
|
|
|
|
timer->cnt_ctl = val & ~ARCH_TIMER_CTRL_IT_STAT;
|
|
|
|
|
timer_set_ctl(timer, val & ~ARCH_TIMER_CTRL_IT_STAT);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TIMER_REG_CVAL:
|
|
|
|
|
timer->cnt_cval = val;
|
|
|
|
|
timer_set_cval(timer, val);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|