#include <asm/hvm/io.h>
#include <asm/hvm/support.h>
-#define TMR_STS (1 << 0)
-static void pmt_update_status(void *opaque)
-{
- PMTState *s = opaque;
- s->pm1_status |= TMR_STS;
-
- /* TODO: When TMR_EN == 1, generate a SCI event */
-
- set_timer(&s->timer, NOW() + (1000000000ULL << 31) / FREQUENCE_PMTIMER);
-}
-
static int handle_pmt_io(ioreq_t *p)
{
struct vcpu *v = current;
/* PM_TMR_BLK is read-only */
return 1;
} else if (p->dir == 1) { /* read */
+ /* Set the correct value in the timer, accounting for time
+ * elapsed since the last time we did that. */
curr_gtime = hvm_get_guest_time(s->vcpu);
- s->pm1_timer += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
- p->data = s->pm1_timer;
+ s->pm.timer += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
+ p->data = s->pm.timer;
s->last_gtime = curr_gtime;
return 1;
}
return 0;
}
-void pmtimer_init(struct vcpu *v, int base)
+static int pmtimer_save(struct domain *d, hvm_domain_context_t *h)
{
- PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
-
- s->pm1_timer = 0;
- s->pm1_status = 0;
- s->scale = ((uint64_t)FREQUENCE_PMTIMER << 32) / ticks_per_sec(v);
- s->vcpu = v;
+ PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
+ uint32_t x;
- init_timer(&s->timer, pmt_update_status, s, v->processor);
- /* ACPI supports a 32-bit power management timer */
- set_timer(&s->timer, NOW() + (1000000000ULL << 31) / FREQUENCE_PMTIMER);
-
- register_portio_handler(v->domain, base, 4, handle_pmt_io);
+ /* Update the counter to the guest's current time. We always save
+ * with the domain paused, so the saved time should be after the
+ * last_gtime, but just in case, make sure we only go forwards */
+ x = ((s->vcpu->arch.hvm_vcpu.guest_time - s->last_gtime) * s->scale) >> 32;
+ if ( x < 1UL<<31 )
+ s->pm.timer += x;
+ return hvm_save_entry(PMTIMER, 0, h, &s->pm);
}
-void pmtimer_migrate_timers(struct vcpu *v)
+static int pmtimer_load(struct domain *d, hvm_domain_context_t *h)
{
- struct PMTState *vpmt = &v->domain->arch.hvm_domain.pl_time.vpmt;
+ PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
- if (vpmt->vcpu == v)
- migrate_timer(&vpmt->timer, v->processor);
+ /* Reload the counter */
+ if ( hvm_load_entry(PMTIMER, h, &s->pm) )
+ return -EINVAL;
+
+ /* Calculate future counter values from now. */
+ s->last_gtime = hvm_get_guest_time(s->vcpu);
+
+ return 0;
}
-void pmtimer_deinit(struct domain *d)
+HVM_REGISTER_SAVE_RESTORE(PMTIMER, pmtimer_save, pmtimer_load,
+ 1, HVMSR_PER_DOM);
+
+
+void pmtimer_init(struct vcpu *v, int base)
{
- PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
+ PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
+
+ s->pm.timer = 0;
+ s->scale = ((uint64_t)FREQUENCE_PMTIMER << 32) / ticks_per_sec(v);
+ s->vcpu = v;
- kill_timer(&s->timer);
+ /* Not implemented: we should set TMR_STS (bit 0 of PM1a_STS) every
+ * time the timer's top bit flips, and generate an SCI if TMR_EN
+ * (bit 0 of PM1a_EN) is set. For now, those registers are in
+ * qemu-dm, and we just calculate the timer's value on demand. */
+
+ register_portio_handler(v->domain, base, 4, handle_pmt_io);
}
+
struct periodic_time pt;
} RTCState;
-#define FREQUENCE_PMTIMER 3579545
+#define FREQUENCE_PMTIMER 3579545 /* Timer should run at 3.579545 MHz */
typedef struct PMTState {
- uint32_t pm1_timer;
- uint32_t pm1_status;
- uint64_t last_gtime;
- struct timer timer;
- uint64_t scale;
- struct vcpu *vcpu;
+ struct hvm_hw_pmtimer pm; /* 32bit timer value */
+ struct vcpu *vcpu; /* Keeps sync with this vcpu's guest-time */
+ uint64_t last_gtime; /* Last (guest) time we updated the timer */
+ uint64_t scale; /* Multiplier to get from tsc to timer ticks */
} PMTState;
struct pl_time { /* platform time */
void rtc_deinit(struct domain *d);
int is_rtc_periodic_irq(void *opaque);
void pmtimer_init(struct vcpu *v, int base);
-void pmtimer_migrate_timers(struct vcpu *v);
-void pmtimer_deinit(struct domain *d);
void hpet_migrate_timers(struct vcpu *v);
void hpet_init(struct vcpu *v);