static void hpet_set_timer(HPETState *h, unsigned int tn) { uint64_t tn_cmp, cur_tick, diff; unsigned int irq; unsigned int oneshot; ASSERT(tn < HPET_TIMER_NUM); ASSERT(spin_is_locked(&h->lock)); if ( (tn == 0) && (h->hpet.config & HPET_CFG_LEGACY) ) { /* HPET specification requires PIT shouldn't generate * interrupts if LegacyReplacementRoute is set for timer0 */ PITState *pit = &vhpet_domain(h)->arch.hvm_domain.pl_time.vpit; pit_stop_channel0_irq(pit); } if ( !timer_enabled(h, tn) ) return; tn_cmp = hpet_get_comparator(h, tn); cur_tick = hpet_read_maincounter(h); if ( timer_is_32bit(h, tn) ) { tn_cmp = (uint32_t)tn_cmp; cur_tick = (uint32_t)cur_tick; } diff = tn_cmp - cur_tick; /* * Detect time values set in the past. This is hard to do for 32-bit * comparators as the timer does not have to be set that far in the future * for the counter difference to wrap a 32-bit signed integer. We fudge * by looking for a 'small' time value in the past. */ if ( (int64_t)diff < 0 ) diff = (timer_is_32bit(h, tn) && (-diff > HPET_TINY_TIME_SPAN)) ? (uint32_t)diff : 0; if ( (tn <= 1) && (h->hpet.config & HPET_CFG_LEGACY) ) /* if LegacyReplacementRoute bit is set, HPET specification requires timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. */ irq = (tn == 0) ? 0 : 8; else irq = timer_int_route(h, tn); /* * diff is the time from now when the timer should fire, for a periodic * 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). */ oneshot = !timer_is_periodic(h, 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); }
static inline uint64_t hpet_read64(HPETState *h, unsigned long addr) { addr &= ~7; switch ( addr ) { case HPET_ID: return h->hpet.capability; case HPET_CFG: return h->hpet.config; case HPET_STATUS: return h->hpet.isr; case HPET_COUNTER: return hpet_read_maincounter(h); case HPET_Tn_CFG(0): case HPET_Tn_CFG(1): case HPET_Tn_CFG(2): return h->hpet.timers[HPET_TN(CFG, addr)].config; case HPET_Tn_CMP(0): case HPET_Tn_CMP(1): case HPET_Tn_CMP(2): return hpet_get_comparator(h, HPET_TN(CMP, addr)); case HPET_Tn_ROUTE(0): case HPET_Tn_ROUTE(1): case HPET_Tn_ROUTE(2): return h->hpet.timers[HPET_TN(ROUTE, addr)].fsb; } return 0; }
static inline uint64_t hpet_read64(HPETState *h, unsigned long addr) { addr &= ~7; switch ( addr ) { case HPET_ID: return h->hpet.capability; case HPET_CFG: return h->hpet.config; case HPET_STATUS: return h->hpet.isr; case HPET_COUNTER: return hpet_read_maincounter(h); case HPET_T0_CFG: case HPET_T1_CFG: case HPET_T2_CFG: return h->hpet.timers[(addr - HPET_T0_CFG) >> 5].config; case HPET_T0_CMP: case HPET_T1_CMP: case HPET_T2_CMP: return hpet_get_comparator(h, (addr - HPET_T0_CMP) >> 5); case HPET_T0_ROUTE: case HPET_T1_ROUTE: case HPET_T2_ROUTE: return h->hpet.timers[(addr - HPET_T0_ROUTE) >> 5].fsb; } return 0; }
static uint64_t hpet_get_comparator(HPETState *h, unsigned int tn) { uint64_t comparator; uint64_t elapsed; comparator = h->hpet.comparator64[tn]; if ( timer_is_periodic(h, tn) ) { /* update comparator by number of periods elapsed since last update */ uint64_t period = h->hpet.period[tn]; if (period) { elapsed = hpet_read_maincounter(h) + period - 1 - comparator; comparator += (elapsed / period) * period; h->hpet.comparator64[tn] = comparator; } } /* truncate if timer is in 32 bit mode */ if ( timer_is_32bit(h, tn) ) comparator = (uint32_t)comparator; h->hpet.timers[tn].cmp = comparator; return comparator; }