When the divide value change, uptade the timer according to the new value, and keep the Counter Register (TMCCT) value the same between before and after the divisor change.
Signed-off-by: Anthony PERARD <anthony.per...@citrix.com> --- xen/arch/x86/hvm/vlapic.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c index 97b7774b61..f70a25f5b9 100644 --- a/xen/arch/x86/hvm/vlapic.c +++ b/xen/arch/x86/hvm/vlapic.c @@ -705,6 +705,35 @@ static void vlapic_update_timer(struct vlapic *vlapic, is_periodic = vlapic_lvtt_period(vlapic); is_oneshot = vlapic_lvtt_oneshot(vlapic); break; + case APIC_TDCR: + is_periodic = vlapic_lvtt_period(vlapic); + is_oneshot = vlapic_lvtt_oneshot(vlapic); + + period = (uint64_t)vlapic_get_reg(vlapic, APIC_TMICT) + * APIC_BUS_CYCLE_NS * vlapic->hw.timer_divisor; + + /* Calculate the next time the timer should trigger an interrupt. */ + if ( period && vlapic->timer_last_update ) + { + uint64_t time_passed = hvm_get_guest_time(current) + - vlapic->timer_last_update; + if ( is_periodic ) + time_passed %= period; + if ( time_passed < period ) + delta = period - time_passed; + } + + val = ((val & 3) | ((val & 8) >> 1)) + 1; + val = 1 << (val & 7); + + period = (uint64_t)vlapic_get_reg(vlapic, APIC_TMICT) + * APIC_BUS_CYCLE_NS * val; + + /* Calculate time left until next interrupt, base on the difference + * between the current timer_divisor and the new one */ + delta = delta * val / vlapic->hw.timer_divisor; + + break; default: BUG(); } @@ -848,6 +877,7 @@ static void vlapic_reg_write(struct vcpu *v, break; case APIC_TDCR: + vlapic_update_timer(vlapic, APIC_TDCR, val); vlapic_set_tdcr(vlapic, val & 0xb); HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, "timer divisor is %#x", vlapic->hw.timer_divisor); -- Anthony PERARD _______________________________________________ Xen-devel mailing list Xen-devel@lists.xen.org https://lists.xen.org/xen-devel