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);
+ irq, NULL, NULL, false);
}
static inline uint64_t hpet_fixup_reg(
/* Periodic timer. */
TRACE_2D(TRC_HVM_EMUL_PIT_START_TIMER, period, period);
create_periodic_time(v, &pit->pt0, period, period, 0, pit_time_fired,
- &pit->count_load_time[channel]);
+ &pit->count_load_time[channel], false);
break;
case 1:
case 4:
/* One-shot timer. */
TRACE_2D(TRC_HVM_EMUL_PIT_START_TIMER, period, 0);
create_periodic_time(v, &pit->pt0, period, 0, 0, pit_time_fired,
- &pit->count_load_time[channel]);
+ &pit->count_load_time[channel], false);
break;
default:
TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER);
{
TRACE_2D(TRC_HVM_EMUL_RTC_START_TIMER, delta, period);
create_periodic_time(v, &s->pt, delta, period,
- RTC_IRQ, rtc_pf_callback, s);
+ RTC_IRQ, rtc_pf_callback, s, false);
}
else
s->check_ticks_since = now;
create_periodic_time(current, &vlapic->pt, delta,
is_periodic ? period : 0, vlapic->pt.irq,
is_periodic ? vlapic_pt_cb : NULL,
- &vlapic->timer_last_update);
+ &vlapic->timer_last_update, false);
vlapic->timer_last_update = vlapic->pt.last_plt_gtime;
if ( !tmict_updated )
TRC_PAR_LONG(0LL), vlapic->pt.irq);
create_periodic_time(v, &vlapic->pt, delta, 0,
vlapic->pt.irq, vlapic_tdt_pt_cb,
- &vlapic->timer_last_update);
+ &vlapic->timer_last_update, false);
vlapic->timer_last_update = vlapic->pt.last_plt_gtime;
}
else
TRC_PAR_LONG(0LL), vlapic->pt.irq);
create_periodic_time(v, &vlapic->pt, 0, 0,
vlapic->pt.irq, vlapic_tdt_pt_cb,
- &vlapic->timer_last_update);
+ &vlapic->timer_last_update, false);
vlapic->timer_last_update = vlapic->pt.last_plt_gtime;
}
else
vlapic_lvtt_period(s) ? period : 0,
s->pt.irq,
vlapic_lvtt_period(s) ? vlapic_pt_cb : NULL,
- &s->timer_last_update);
+ &s->timer_last_update, false);
s->timer_last_update = s->pt.last_plt_gtime;
}
struct periodic_time *pt, *temp, *earliest_pt;
uint64_t max_lag;
int irq, pt_vector = -1;
+ bool level;
spin_lock(&v->arch.hvm_vcpu.tm_lock);
if ( pt->pending_intr_nr )
{
/* RTC code takes care of disabling the timer itself. */
- if ( (pt->irq != RTC_IRQ || !pt->priv) && pt_irq_masked(pt) )
+ if ( (pt->irq != RTC_IRQ || !pt->priv) && pt_irq_masked(pt) &&
+ /* Level interrupts should be asserted even if masked. */
+ !pt->level )
{
/* suspend timer emulation */
list_del(&pt->list);
earliest_pt->irq_issued = 1;
irq = earliest_pt->irq;
+ level = earliest_pt->level;
spin_unlock(&v->arch.hvm_vcpu.tm_lock);
break;
case PTSRC_ioapic:
- /*
- * NB: At the moment IO-APIC routed interrupts generated by vpt devices
- * (HPET) are edge-triggered.
- */
- pt_vector = hvm_ioapic_assert(v->domain, irq, false);
+ pt_vector = hvm_ioapic_assert(v->domain, irq, level);
if ( pt_vector < 0 || !vlapic_test_irq(vcpu_vlapic(v), pt_vector) )
+ {
pt_vector = -1;
+ if ( level )
+ {
+ /*
+ * Level interrupts are always asserted because the pin assert
+ * count is incremented regardless of whether the pin is masked
+ * or the vector latched in IRR, so also execute the callback
+ * associated with the timer.
+ */
+ time_cb *cb = NULL;
+ void *cb_priv;
+
+ spin_lock(&v->arch.hvm_vcpu.tm_lock);
+ /* Make sure the timer is still on the list. */
+ list_for_each_entry ( pt, &v->arch.hvm_vcpu.tm_list, list )
+ if ( pt == earliest_pt )
+ {
+ pt_irq_fired(v, pt);
+ cb = pt->cb;
+ cb_priv = pt->priv;
+ break;
+ }
+ spin_unlock(&v->arch.hvm_vcpu.tm_lock);
+
+ if ( cb != NULL )
+ cb(v, cb_priv);
+ }
+ }
break;
}
void create_periodic_time(
struct vcpu *v, struct periodic_time *pt, uint64_t delta,
- uint64_t period, uint8_t irq, time_cb *cb, void *data)
+ uint64_t period, uint8_t irq, time_cb *cb, void *data, bool level)
{
if ( !pt->source ||
(irq >= NR_ISAIRQS && pt->source == PTSRC_isa) ||
- (irq >= hvm_domain_irq(v->domain)->nr_gsis &&
- pt->source == PTSRC_ioapic) )
+ (level && period) ||
+ (pt->source == PTSRC_ioapic ? irq >= hvm_domain_irq(v->domain)->nr_gsis
+ : level) )
{
ASSERT_UNREACHABLE();
return;
pt->last_plt_gtime = hvm_get_guest_time(pt->vcpu);
pt->irq = irq;
pt->one_shot = !period;
+ pt->level = level;
pt->scheduled = NOW() + delta;
if ( !pt->one_shot )
bool do_not_freeze;
bool irq_issued;
bool warned_timeout_too_short;
+ bool level;
#define PTSRC_isa 1 /* ISA time source */
#define PTSRC_lapic 2 /* LAPIC time source */
#define PTSRC_ioapic 3 /* IOAPIC time source */
*/
void create_periodic_time(
struct vcpu *v, struct periodic_time *pt, uint64_t delta,
- uint64_t period, uint8_t irq, time_cb *cb, void *data);
+ uint64_t period, uint8_t irq, time_cb *cb, void *data, bool level);
void destroy_periodic_time(struct periodic_time *pt);
int pv_pit_handler(int port, int data, int write);