diff mbox

[2/3] x86/vlapic: Handle change of timer Divide Configuration Register

Message ID 20170323114701.25207-3-anthony.perard@citrix.com (mailing list archive)
State New, archived
Headers show

Commit Message

Anthony PERARD March 23, 2017, 11:47 a.m. UTC
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.perard@citrix.com>
---
 xen/arch/x86/hvm/vlapic.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

Comments

Jan Beulich March 24, 2017, 9:43 a.m. UTC | #1
>>> On 23.03.17 at 12:47, <anthony.perard@citrix.com> wrote:
> 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.

General and formatting comments given on patch 1 apply here too,
the logic looks okay assuming the intended / observed behavior is
that on-the-fly updates to the register take immediate effect on all
other functionality.

Jan
diff mbox

Patch

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);