[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v3 6/6] 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 <stefan.bader@xxxxxxxxxxxxx> Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx> --- Cc: Jan Beulich <jbeulich@xxxxxxxx> Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> Cc: Stefan Bader <stefan.bader@xxxxxxxxxxxxx> --- xen/arch/x86/hvm/hpet.c | 59 ++++++++++++++++++++++++++++------- xen/arch/x86/hvm/irq.c | 15 +++++++++ xen/include/asm-x86/hvm/irq.h | 3 +- 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/xen/arch/x86/hvm/hpet.c b/xen/arch/x86/hvm/hpet.c index 72209350ba..9720efb5c6 100644 --- a/xen/arch/x86/hvm/hpet.c +++ b/xen/arch/x86/hvm/hpet.c @@ -223,6 +223,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 int)data; + HPETState *h = vcpu_vhpet(v); + + write_lock(&h->lock); + ASSERT(!test_bit(tn, &h->hpet.isr)); + __set_bit(tn, &h->hpet.isr); + 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) @@ -244,7 +255,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) ) @@ -293,8 +305,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 : @@ -302,7 +318,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( @@ -338,7 +355,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; @@ -394,6 +411,32 @@ 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 ) + { + /* + * Should pt->irq better be used here in case the guest changes + * the configured IRQ while it's active? Guest changing the IRQ + * while the interrupt is active is not documented. + */ + 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) ) @@ -415,14 +458,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); -- 2.17.1 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |