diff --git a/xen/arch/x86/hvm/hpet.c b/xen/arch/x86/hvm/hpet.c index 28377091ca..f7ef4f7514 100644 --- a/xen/arch/x86/hvm/hpet.c +++ b/xen/arch/x86/hvm/hpet.c @@ -73,6 +73,9 @@ ((timer_config(h, n) & HPET_TN_INT_ROUTE_CAP_MASK) \ >> HPET_TN_INT_ROUTE_CAP_SHIFT) +#define timer_int_route_valid(h, n) \ + ((1u << timer_int_route(h, n)) & timer_int_route_cap(h, n)) + static inline uint64_t hpet_read_maincounter(HPETState *h, uint64_t guest_time) { ASSERT(rw_is_locked(&h->lock)); @@ -244,6 +247,12 @@ static void hpet_set_timer(HPETState *h, unsigned int tn, if ( !timer_enabled(h, tn) ) return; + if ( !timer_int_route_valid(h, tn) ) + { + ASSERT_UNREACHABLE(); + return; + } + tn_cmp = hpet_get_comparator(h, tn, guest_time); cur_tick = hpet_read_maincounter(h, guest_time); if ( timer_is_32bit(h, tn) ) @@ -304,6 +313,24 @@ static inline uint64_t hpet_fixup_reg( return new; } +static void timer_sanitize_int_route(HPETState *h, unsigned int tn) +{ + if ( timer_int_route_valid(h, tn) ) + return; + + timer_config(h, tn) &= ~HPET_TN_ROUTE; + if ( !timer_enabled(h, tn) ) + return; + + /* + * If the requested interrupt is not valid and the timer is + * enabled pick the first irq. + */ + timer_config(h, tn) |= + MASK_INSR(find_first_set_bit(timer_int_route_cap(h, tn)), + HPET_TN_ROUTE); +} + static int hpet_write( struct vcpu *v, unsigned long addr, unsigned int length, unsigned long val) @@ -386,6 +413,8 @@ static int hpet_write( h->hpet.timers[tn].config = hpet_fixup_reg(new_val, old_val, 0x3f4e); + timer_sanitize_int_route(h, tn); + if ( timer_level(h, tn) ) { gdprintk(XENLOG_ERR, @@ -621,6 +650,7 @@ static int hpet_load(struct domain *d, hvm_domain_context_t *h) if ( timer_is_32bit(hp, i) ) cmp = (uint32_t)cmp; hp->hpet.timers[i].cmp = cmp; + timer_sanitize_int_route(hp, i); } #undef C