]> xenbits.xensource.com Git - people/aperard/xen-arm.git/commitdiff
xen/arm: support the ARM generic virtual timer
authorStefano Stabellini <stefano.stabellini@eu.citrix.com>
Thu, 17 Jan 2013 16:48:24 +0000 (16:48 +0000)
committerStefano Stabellini <stefano.stabellini@eu.citrix.com>
Thu, 17 Jan 2013 16:48:24 +0000 (16:48 +0000)
Save and restore the virtual timer registers during the context switch.
At save time initialize an internal Xen timer to make sure that Xen
schedules the guest vcpu at the time of the next virtual timer
interrupt.
Receive the virtual timer interrupt into the hypervisor and inject it
into the running guest.

Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
Committed-by: Ian Campbell <ian.campbell@citrix.com>
xen/arch/arm/domain.c
xen/arch/arm/gic.c
xen/arch/arm/time.c
xen/arch/arm/vtimer.c
xen/arch/arm/vtimer.h
xen/include/asm-arm/domain.h
xen/include/asm-arm/time.h

index 2ad5c9063e6c6a504922b05c67465b877f901eda..59d8d73be4a452bd90ed457ce181e1adf2c3709b 100644 (file)
@@ -51,9 +51,7 @@ static void ctxt_switch_from(struct vcpu *p)
     p->arch.tpidrprw = READ_CP32(TPIDRPRW);
 
     /* Arch timer */
-    p->arch.cntvoff = READ_CP64(CNTVOFF);
-    p->arch.cntv_cval = READ_CP64(CNTV_CVAL);
-    p->arch.cntv_ctl = READ_CP32(CNTV_CTL);
+    virt_timer_save(p);
 
     /* XXX only save these if ThumbEE e.g. ID_PFR0.THUMB_EE_SUPPORT */
     p->arch.teecr = READ_CP32(TEECR);
@@ -134,11 +132,6 @@ static void ctxt_switch_to(struct vcpu *n)
     WRITE_CP32(n->arch.mair1, MAIR1);
     isb();
 
-    /* Arch timer */
-    WRITE_CP64(n->arch.cntvoff, CNTVOFF);
-    WRITE_CP64(n->arch.cntv_cval, CNTV_CVAL);
-    WRITE_CP32(n->arch.cntv_ctl, CNTV_CTL);
-
     /* Control Registers */
     WRITE_CP32(n->arch.actlr, ACTLR);
     WRITE_CP32(n->arch.sctlr, SCTLR);
@@ -165,6 +158,10 @@ static void ctxt_switch_to(struct vcpu *n)
 
     WRITE_CP32(hcr, HCR);
     isb();
+
+    /* This is could trigger an hardware interrupt from the virtual
+     * timer. The interrupt needs to be injected into the guest. */
+    virt_timer_restore(n);
 }
 
 static void schedule_tail(struct vcpu *prev)
index b77761e245a1e1d3f6faa967b35bcd1db5d8a40e..293efdce0d61bc8bf0b994c3625ed2086e60c64e 100644 (file)
@@ -379,7 +379,9 @@ void gic_route_ppis(void)
     gic_route_irq(25, 1, 1u << smp_processor_id(), 0xa0);
     /* Hypervisor Timer */
     gic_route_irq(26, 1, 1u << smp_processor_id(), 0xa0);
-    /* Timer */
+    /* Virtual Timer */
+    gic_route_irq(27, 1, 1u << smp_processor_id(), 0xa0);
+    /* Physical Timer */
     gic_route_irq(30, 1, 1u << smp_processor_id(), 0xa0);
 }
 
index 0f9335ead4c0a773c72339e3078910e073f91b50..07628e1bc3744aafb42c763058e772ed7ef8eb46 100644 (file)
 #include <xen/lib.h>
 #include <xen/mm.h>
 #include <xen/softirq.h>
+#include <xen/sched.h>
 #include <xen/time.h>
 #include <xen/sched.h>
 #include <xen/event.h>
 #include <asm/system.h>
+#include <asm/time.h>
+#include <asm/gic.h>
 
 /*
  * Unfortunately the hypervisor timer interrupt appears to be buggy in
  */
 #define USE_HYP_TIMER 1
 
+uint64_t __read_mostly boot_count;
+
 /* For fine-grained timekeeping, we use the ARM "Generic Timer", a
  * register-mapped time source in the SoC. */
 static uint32_t __read_mostly cntfrq;      /* Ticks per second */
-static uint64_t __read_mostly boot_count;  /* Counter value at boot time */
 
 /*static inline*/ s_time_t ticks_to_ns(uint64_t ticks)
 {
@@ -155,6 +159,13 @@ static void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
     }
 }
 
+static void vtimer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
+{
+    current->arch.virt_timer.ctl = READ_CP32(CNTV_CTL);
+    WRITE_CP32(current->arch.virt_timer.ctl | CNTx_CTL_MASK, CNTV_CTL);
+    vgic_vcpu_inject_irq(current, irq, 1);
+}
+
 /* Set up the timer interrupt on this CPU */
 void __cpuinit init_timer_interrupt(void)
 {
@@ -174,6 +185,7 @@ void __cpuinit init_timer_interrupt(void)
 
     /* XXX Need to find this IRQ number from devicetree? */
     request_irq(26, timer_interrupt, 0, "hyptimer", NULL);
+    request_irq(27, vtimer_interrupt, 0, "virtimer", NULL);
     request_irq(30, timer_interrupt, 0, "phytimer", NULL);
 }
 
index fc452e34f8c96e75298972d19d6ea1cca01c7f66..361687918f4113798e5fb0d36d28455760044b67 100644 (file)
 #include <xen/lib.h>
 #include <xen/timer.h>
 #include <xen/sched.h>
+#include <asm/irq.h>
+#include <asm/time.h>
 #include <asm/gic.h>
 #include <asm/regs.h>
 
 extern s_time_t ticks_to_ns(uint64_t ticks);
 extern uint64_t ns_to_ticks(s_time_t ns);
 
-static void vtimer_expired(void *data)
+static void phys_timer_expired(void *data)
 {
-    struct vcpu *v = data;
-    v->arch.vtimer.ctl |= CNTx_CTL_PENDING;
-    v->arch.vtimer.ctl &= ~CNTx_CTL_MASK;
-    vgic_vcpu_inject_irq(v, 30, 1);
+    struct vtimer *t = data;
+    t->ctl |= CNTx_CTL_PENDING;
+    t->ctl &= ~CNTx_CTL_MASK;
+    vgic_vcpu_inject_irq(t->v, 30, 1);
 }
 
+static void virt_timer_expired(void *data)
+{
+    struct vtimer *t = data;
+    vcpu_wake(t->v);
+}
 int vcpu_vtimer_init(struct vcpu *v)
 {
-    init_timer(&v->arch.vtimer.timer,
-               vtimer_expired, v,
-               smp_processor_id());
-    v->arch.vtimer.ctl = 0;
-    v->arch.vtimer.offset = NOW();
-    v->arch.vtimer.cval = NOW();
+    struct vtimer *t = &v->arch.phys_timer;
+
+    init_timer(&t->timer, phys_timer_expired, t, smp_processor_id());
+    t->ctl = 0;
+    t->offset = NOW();
+    t->cval = NOW();
+    t->irq = 30;
+    t->v = v;
+
+    t = &v->arch.virt_timer;
+    init_timer(&t->timer, virt_timer_expired, t, smp_processor_id());
+    t->ctl = 0;
+    t->offset = READ_CP64(CNTVCT) + READ_CP64(CNTVOFF);
+    t->cval = 0;
+    t->irq = 27;
+    t->v = v;
+
     return 0;
 }
 
+int virt_timer_save(struct vcpu *v)
+{
+    v->arch.virt_timer.ctl = READ_CP32(CNTV_CTL);
+    WRITE_CP32(v->arch.virt_timer.ctl & ~CNTx_CTL_ENABLE, CNTV_CTL);
+    v->arch.virt_timer.cval = READ_CP64(CNTV_CVAL);
+    if ( v->arch.virt_timer.ctl & CNTx_CTL_ENABLE )
+    {
+        set_timer(&v->arch.virt_timer.timer, ticks_to_ns(v->arch.virt_timer.cval +
+                  v->arch.virt_timer.offset - boot_count));
+    }
+    return 0;
+}
+
+int virt_timer_restore(struct vcpu *v)
+{
+    stop_timer(&v->arch.virt_timer.timer);
+
+    WRITE_CP32(v->arch.virt_timer.ctl & ~CNTx_CTL_ENABLE, CNTV_CTL);
+    WRITE_CP64(v->arch.virt_timer.offset, CNTVOFF);
+    WRITE_CP64(v->arch.virt_timer.cval, CNTV_CVAL);
+    WRITE_CP32(v->arch.virt_timer.ctl, CNTV_CTL);
+    return 0;
+}
 static int vtimer_emulate_32(struct cpu_user_regs *regs, union hsr hsr)
 {
     struct vcpu *v = current;
@@ -58,36 +101,36 @@ static int vtimer_emulate_32(struct cpu_user_regs *regs, union hsr hsr)
     case HSR_CPREG32(CNTP_CTL):
         if ( cp32.read )
         {
-            *r = v->arch.vtimer.ctl;
+            *r = v->arch.phys_timer.ctl;
         }
         else
         {
-            v->arch.vtimer.ctl = *r;
+            v->arch.phys_timer.ctl = *r;
 
-            if ( v->arch.vtimer.ctl & CNTx_CTL_ENABLE )
+            if ( v->arch.phys_timer.ctl & CNTx_CTL_ENABLE )
             {
-                set_timer(&v->arch.vtimer.timer,
-                          v->arch.vtimer.cval + v->arch.vtimer.offset);
+                set_timer(&v->arch.phys_timer.timer,
+                          v->arch.phys_timer.cval + v->arch.phys_timer.offset);
             }
             else
-                stop_timer(&v->arch.vtimer.timer);
+                stop_timer(&v->arch.phys_timer.timer);
         }
 
         return 1;
 
     case HSR_CPREG32(CNTP_TVAL):
-        now = NOW() - v->arch.vtimer.offset;
+        now = NOW() - v->arch.phys_timer.offset;
         if ( cp32.read )
         {
-            *r = (uint32_t)(ns_to_ticks(v->arch.vtimer.cval - now) & 0xffffffffull);
+            *r = (uint32_t)(ns_to_ticks(v->arch.phys_timer.cval - now) & 0xffffffffull);
         }
         else
         {
-            v->arch.vtimer.cval = now + ticks_to_ns(*r);
-            if ( v->arch.vtimer.ctl & CNTx_CTL_ENABLE )
+            v->arch.phys_timer.cval = now + ticks_to_ns(*r);
+            if ( v->arch.phys_timer.ctl & CNTx_CTL_ENABLE )
             {
-                set_timer(&v->arch.vtimer.timer,
-                          v->arch.vtimer.cval + v->arch.vtimer.offset);
+                set_timer(&v->arch.phys_timer.timer,
+                          v->arch.phys_timer.cval + v->arch.phys_timer.offset);
             }
         }
 
@@ -112,7 +155,7 @@ static int vtimer_emulate_64(struct cpu_user_regs *regs, union hsr hsr)
     case HSR_CPREG64(CNTPCT):
         if ( cp64.read )
         {
-            now = NOW() - v->arch.vtimer.offset;
+            now = NOW() - v->arch.phys_timer.offset;
             ticks = ns_to_ticks(now);
             *r1 = (uint32_t)(ticks & 0xffffffff);
             *r2 = (uint32_t)(ticks >> 32);
index d87bb25a4189e33fc3d6cd7428a2333a5bb66f35..faebd6889d1fc64ad59f7681cbc6fc24de79eab2 100644 (file)
@@ -22,6 +22,8 @@
 
 extern int vcpu_vtimer_init(struct vcpu *v);
 extern int vtimer_emulate(struct cpu_user_regs *regs, union hsr hsr);
+extern int virt_timer_save(struct vcpu *v);
+extern int virt_timer_restore(struct vcpu *v);
 
 #endif
 
index 114a8f6d78214117e89647eef4d65643f59793ae..577ad1969a1c22f254463624cdef06b5b99bb24c 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <xen/config.h>
 #include <xen/cache.h>
+#include <xen/sched.h>
 #include <asm/page.h>
 #include <asm/p2m.h>
 #include <public/hvm/params.h>
@@ -69,6 +70,15 @@ struct arch_domain
 
 }  __cacheline_aligned;
 
+struct vtimer {
+        struct vcpu *v;
+        int irq;
+        struct timer timer;
+        uint32_t ctl;
+        s_time_t offset;
+        s_time_t cval;
+};
+
 struct arch_vcpu
 {
     struct {
@@ -118,11 +128,6 @@ struct arch_vcpu
     uint32_t teecr, teehbr;
     uint32_t joscr, jmcr;
 
-    /* Arch timers */
-    uint64_t cntvoff;
-    uint64_t cntv_cval;
-    uint32_t cntv_ctl;
-
     /* CP 15 */
     uint32_t csselr;
 
@@ -157,12 +162,8 @@ struct arch_vcpu
         spinlock_t lock;
     } vgic;
 
-    struct {
-        struct timer timer;
-        uint32_t ctl;
-        s_time_t offset;
-        s_time_t cval;
-    } vtimer;
+    struct vtimer phys_timer;
+    struct vtimer virt_timer;
 }  __cacheline_aligned;
 
 void vcpu_show_execution_state(struct vcpu *);
index 8cc9e78888e74551ffc16f17c70e6bd47c754377..0d4a052307fc4145d48af429b37c54c1d7d045da 100644 (file)
@@ -15,6 +15,9 @@ struct tm wallclock_time(void);
 /* Set up the timer interrupt on this CPU */
 extern void __cpuinit init_timer_interrupt(void);
 
+/* Counter value at boot time */
+extern uint64_t boot_count;
+
 #endif /* __ARM_TIME_H__ */
 /*
  * Local variables: