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 <stefan.bader@canonical.com>
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Roger Pau Monné authored and Jan Beulich committed 2018-07-24 15:54:18 +02:00
1 parent 0bcbc971fe
commit be07023be1
3 files changed
+59 -13

No files matched your search

+42 -12
View File
@@ -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;
+15
View File
@@ -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);
+2 -1
View File
@@ -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);
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.