[xen master] x86/vhpet: add support for level triggered interrupts

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[xen master] x86/vhpet: add support for level triggered interrupts

patchbot
commit be07023be115c94b7fbb51d2ef6f421ddd680de8
Author:     Roger Pau Monné <[hidden email]>
AuthorDate: Tue Jul 24 15:54:18 2018 +0200
Commit:     Jan Beulich <[hidden email]>
CommitDate: Tue Jul 24 15:54:18 2018 +0200

    x86/vhpet: add support for level triggered interrupts
   
    Level triggered interrupts are not an optional feature of HPET, and
    must be implemented in order to comply with the HPET specification.
   
    Implement them by adding a callback to the timer which sets the
    interrupt bit in the general interrupt status register. Further
    interrupts (in case of periodic mode) will not be injected until the
    bit is cleared.
   
    In order to reset the interrupts when the status bit is clear Xen must
    also detect accesses to such register.
   
    While there convert tn and i in hpet_write to unsigned.
   
    Reported-by: Stefan Bader <[hidden email]>
    Signed-off-by: Roger Pau Monné <[hidden email]>
    Reviewed-by: Jan Beulich <[hidden email]>
---
 xen/arch/x86/hvm/hpet.c       | 54 +++++++++++++++++++++++++++++++++----------
 xen/arch/x86/hvm/irq.c        | 15 ++++++++++++
 xen/include/asm-x86/hvm/irq.h |  3 ++-
 3 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/xen/arch/x86/hvm/hpet.c b/xen/arch/x86/hvm/hpet.c
index b7dcfa8af9..a594254a41 100644
--- a/xen/arch/x86/hvm/hpet.c
+++ b/xen/arch/x86/hvm/hpet.c
@@ -213,6 +213,17 @@ static void hpet_stop_timer(HPETState *h, unsigned int tn,
     hpet_get_comparator(h, tn, guest_time);
 }
 
+static void hpet_timer_fired(struct vcpu *v, void *data)
+{
+    unsigned int tn = (unsigned long)data;
+    HPETState *h = vcpu_vhpet(v);
+
+    write_lock(&h->lock);
+    if ( __test_and_set_bit(tn, &h->hpet.isr) )
+        ASSERT_UNREACHABLE();
+    write_unlock(&h->lock);
+}
+
 /* the number of HPET tick that stands for
  * 1/(2^10) second, namely, 0.9765625 milliseconds */
 #define  HPET_TINY_TIME_SPAN  ((h->stime_freq >> 10) / STIME_PER_HPET_TICK)
@@ -234,7 +245,8 @@ static void hpet_set_timer(HPETState *h, unsigned int tn,
         pit_stop_channel0_irq(&vhpet_domain(h)->arch.vpit);
     }
 
-    if ( !timer_enabled(h, tn) )
+    if ( !timer_enabled(h, tn) ||
+         (timer_level(h, tn) && test_bit(tn, &h->hpet.isr)) )
         return;
 
     if ( !timer_int_route_valid(h, tn) )
@@ -283,8 +295,12 @@ static void hpet_set_timer(HPETState *h, unsigned int tn,
      * timer we also need the period which may be different because time may
      * have elapsed between the time the comparator was written and the timer
      * being enabled (now).
+     *
+     * NB: set periodic timers as oneshot if interrupt type is set to level
+     * because the user must ack the interrupt (by writing 1 to the interrupt
+     * status register) before another interrupt can be delivered.
      */
-    oneshot = !timer_is_periodic(h, tn);
+    oneshot = !timer_is_periodic(h, tn) || timer_level(h, tn);
     TRACE_2_LONG_4D(TRC_HVM_EMUL_HPET_START_TIMER, tn, irq,
                     TRC_PAR_LONG(hpet_tick_to_ns(h, diff)),
                     TRC_PAR_LONG(oneshot ? 0LL :
@@ -292,7 +308,8 @@ static void hpet_set_timer(HPETState *h, unsigned int tn,
     create_periodic_time(vhpet_vcpu(h), &h->pt[tn],
                          hpet_tick_to_ns(h, diff),
                          oneshot ? 0 : hpet_tick_to_ns(h, h->hpet.period[tn]),
-                         irq, NULL, NULL, false);
+                         irq, timer_level(h, tn) ? hpet_timer_fired : NULL,
+                         (void *)(unsigned long)tn, timer_level(h, tn));
 }
 
 static inline uint64_t hpet_fixup_reg(
@@ -328,7 +345,7 @@ static int hpet_write(
     HPETState *h = vcpu_vhpet(v);
     uint64_t old_val, new_val;
     uint64_t guest_time;
-    int tn, i;
+    unsigned int tn, i;
 
     /* Acculumate a bit mask of timers whos state is changed by this write. */
     unsigned long start_timers = 0;
@@ -385,6 +402,27 @@ static int hpet_write(
         }
         break;
 
+    case HPET_STATUS:
+        /* write 1 to clear. */
+        while ( new_val )
+        {
+            bool active;
+
+            i = find_first_set_bit(new_val);
+            if ( i >= HPET_TIMER_NUM )
+                break;
+            __clear_bit(i, &new_val);
+            active = __test_and_clear_bit(i, &h->hpet.isr);
+            if ( active )
+            {
+                hvm_ioapic_deassert(v->domain, timer_int_route(h, i));
+                if ( hpet_enabled(h) && timer_enabled(h, i) &&
+                     timer_level(h, i) && timer_is_periodic(h, i) )
+                    set_start_timer(i);
+            }
+        }
+        break;
+
     case HPET_COUNTER:
         h->hpet.mc64 = new_val;
         if ( hpet_enabled(h) )
@@ -410,14 +448,6 @@ static int hpet_write(
 
         timer_sanitize_int_route(h, tn);
 
-        if ( timer_level(h, tn) )
-        {
-            gdprintk(XENLOG_ERR,
-                     "HPET: level triggered interrupt not supported now\n");
-            domain_crash(current->domain);
-            break;
-        }
-
         if ( new_val & HPET_TN_32BIT )
         {
             h->hpet.timers[tn].cmp = (uint32_t)h->hpet.timers[tn].cmp;
diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c
index c85d004402..8095c829b6 100644
--- a/xen/arch/x86/hvm/irq.c
+++ b/xen/arch/x86/hvm/irq.c
@@ -61,6 +61,21 @@ int hvm_ioapic_assert(struct domain *d, unsigned int gsi, bool level)
     return vector;
 }
 
+void hvm_ioapic_deassert(struct domain *d, unsigned int gsi)
+{
+    struct hvm_irq *hvm_irq = hvm_domain_irq(d);
+
+    if ( gsi >= hvm_irq->nr_gsis )
+    {
+        ASSERT_UNREACHABLE();
+        return;
+    }
+
+    spin_lock(&d->arch.hvm_domain.irq_lock);
+    hvm_irq->gsi_assert_count[gsi]--;
+    spin_unlock(&d->arch.hvm_domain.irq_lock);
+}
+
 static void assert_irq(struct domain *d, unsigned ioapic_gsi, unsigned pic_irq)
 {
     assert_gsi(d, ioapic_gsi);
diff --git a/xen/include/asm-x86/hvm/irq.h b/xen/include/asm-x86/hvm/irq.h
index 1a52ec6045..8a43cb97af 100644
--- a/xen/include/asm-x86/hvm/irq.h
+++ b/xen/include/asm-x86/hvm/irq.h
@@ -207,8 +207,9 @@ int hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq);
 
 int hvm_inject_msi(struct domain *d, uint64_t addr, uint32_t data);
 
-/* Assert an IO APIC pin. */
+/* Assert/deassert an IO APIC pin. */
 int hvm_ioapic_assert(struct domain *d, unsigned int gsi, bool level);
+void hvm_ioapic_deassert(struct domain *d, unsigned int gsi);
 
 void hvm_maybe_deassert_evtchn_irq(void);
 void hvm_assert_evtchn_irq(struct vcpu *v);
--
generated by git-patchbot for /home/xen/git/xen.git#master


_______________________________________________
Xen-changelog mailing list
[hidden email]
https://lists.xenproject.org/xen-changelog