diff options
author | Tim Deegan <Tim.Deegan@xensource.com> | 2007-01-20 11:17:38 +0000 |
---|---|---|
committer | Tim Deegan <Tim.Deegan@xensource.com> | 2007-01-20 11:17:38 +0000 |
commit | 3969aaf2765ffcda14b4d53a5b0980f0d443331b (patch) | |
tree | 0a0919319f9beb9b179b5af2ff72582f7e786395 /xen/arch/x86/hvm/i8254.c | |
parent | e105919b0120a0eccfe59ece2ce545ddd6f0881f (diff) | |
download | xen-3969aaf2765ffcda14b4d53a5b0980f0d443331b.tar.gz xen-3969aaf2765ffcda14b4d53a5b0980f0d443331b.tar.bz2 xen-3969aaf2765ffcda14b4d53a5b0980f0d443331b.zip |
[HVM] Save/restore cleanups 01: PIT
Define public structure for the saved PIT data and use it instead
of a series of explicit loads and stores.
Don't save ephemeral Xen timer structs; rebuild them instead.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
Diffstat (limited to 'xen/arch/x86/hvm/i8254.c')
-rw-r--r-- | xen/arch/x86/hvm/i8254.c | 241 |
1 files changed, 91 insertions, 150 deletions
diff --git a/xen/arch/x86/hvm/i8254.c b/xen/arch/x86/hvm/i8254.c index cef7fc9a46..61f997c051 100644 --- a/xen/arch/x86/hvm/i8254.c +++ b/xen/arch/x86/hvm/i8254.c @@ -76,37 +76,42 @@ uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) return res.ll; } -static int pit_get_count(PITChannelState *s) +static int pit_get_count(PITState *s, int channel) { uint64_t d; int counter; + struct hvm_hw_pit_channel *c = &s->hw.channels[channel]; + struct periodic_time *pt = &s->pt[channel]; - d = muldiv64(hvm_get_guest_time(s->pt.vcpu) - s->count_load_time, PIT_FREQ, ticks_per_sec(s->pt.vcpu)); - switch(s->mode) { + d = muldiv64(hvm_get_guest_time(pt->vcpu) + - c->count_load_time, PIT_FREQ, ticks_per_sec(pt->vcpu)); + switch(c->mode) { case 0: case 1: case 4: case 5: - counter = (s->count - d) & 0xffff; + counter = (c->count - d) & 0xffff; break; case 3: /* XXX: may be incorrect for odd counts */ - counter = s->count - ((2 * d) % s->count); + counter = c->count - ((2 * d) % c->count); break; default: - counter = s->count - (d % s->count); + counter = c->count - (d % c->count); break; } return counter; } /* get pit output bit */ -static int pit_get_out1(PITChannelState *s, int64_t current_time) +int pit_get_out(PITState *pit, int channel, int64_t current_time) { + struct hvm_hw_pit_channel *s = &pit->hw.channels[channel]; uint64_t d; int out; - d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec(s->pt.vcpu)); + d = muldiv64(current_time - s->count_load_time, + PIT_FREQ, ticks_per_sec(pit->pt[channel].vcpu)); switch(s->mode) { default: case 0: @@ -132,16 +137,11 @@ static int pit_get_out1(PITChannelState *s, int64_t current_time) return out; } -int pit_get_out(PITState *pit, int channel, int64_t current_time) -{ - PITChannelState *s = &pit->channels[channel]; - return pit_get_out1(s, current_time); -} - /* val must be 0 or 1 */ void pit_set_gate(PITState *pit, int channel, int val) { - PITChannelState *s = &pit->channels[channel]; + struct hvm_hw_pit_channel *s = &pit->hw.channels[channel]; + struct periodic_time *pt = &pit->pt[channel]; switch(s->mode) { default: @@ -153,7 +153,7 @@ void pit_set_gate(PITState *pit, int channel, int val) case 5: if (s->gate < val) { /* restart counting on rising edge */ - s->count_load_time = hvm_get_guest_time(s->pt.vcpu); + s->count_load_time = hvm_get_guest_time(pt->vcpu); // pit_irq_timer_update(s, s->count_load_time); } break; @@ -161,7 +161,7 @@ void pit_set_gate(PITState *pit, int channel, int val) case 3: if (s->gate < val) { /* restart counting on rising edge */ - s->count_load_time = hvm_get_guest_time(s->pt.vcpu); + s->count_load_time = hvm_get_guest_time(pt->vcpu); // pit_irq_timer_update(s, s->count_load_time); } /* XXX: disable/enable counting */ @@ -172,23 +172,25 @@ void pit_set_gate(PITState *pit, int channel, int val) int pit_get_gate(PITState *pit, int channel) { - PITChannelState *s = &pit->channels[channel]; - return s->gate; + return pit->hw.channels[channel].gate; } void pit_time_fired(struct vcpu *v, void *priv) { - PITChannelState *s = priv; + struct hvm_hw_pit_channel *s = priv; s->count_load_time = hvm_get_guest_time(v); } -static inline void pit_load_count(PITChannelState *s, int channel, int val) +static inline void pit_load_count(PITState *pit, int channel, int val) { u32 period; + struct hvm_hw_pit_channel *s = &pit->hw.channels[channel]; + struct periodic_time *pt = &pit->pt[channel]; + struct vcpu *v; if (val == 0) val = 0x10000; - s->count_load_time = hvm_get_guest_time(s->pt.vcpu); + s->count_load_time = hvm_get_guest_time(pt->vcpu); s->count = val; period = DIV_ROUND((val * 1000000000ULL), PIT_FREQ); @@ -204,30 +206,38 @@ static inline void pit_load_count(PITChannelState *s, int channel, int val) (long long)s->count_load_time); #endif + /* Choose a vcpu to set the timer on: current if appropriate else vcpu 0 */ + if ( likely(pit == ¤t->domain->arch.hvm_domain.pl_time.vpit) ) + v = current; + else + v = container_of(pit, struct domain, + arch.hvm_domain.pl_time.vpit)->vcpu[0]; + switch (s->mode) { case 2: /* create periodic time */ - create_periodic_time(current, &s->pt, period, 0, 0, pit_time_fired, s); + create_periodic_time(v, pt, period, 0, 0, pit_time_fired, s); break; case 1: /* create one shot time */ - create_periodic_time(current, &s->pt, period, 0, 1, pit_time_fired, s); + create_periodic_time(v, pt, period, 0, 1, pit_time_fired, s); #ifdef DEBUG_PIT printk("HVM_PIT: create one shot time.\n"); #endif break; default: - destroy_periodic_time(&s->pt); + destroy_periodic_time(pt); break; } } /* if already latched, do not latch again */ -static void pit_latch_count(PITChannelState *s) +static void pit_latch_count(PITState *s, int channel) { - if (!s->count_latched) { - s->latched_count = pit_get_count(s); - s->count_latched = s->rw_mode; + struct hvm_hw_pit_channel *c = &s->hw.channels[channel]; + if (!c->count_latched) { + c->latched_count = pit_get_count(s, channel); + c->count_latched = c->rw_mode; } } @@ -235,7 +245,7 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) { PITState *pit = opaque; int channel, access; - PITChannelState *s; + struct hvm_hw_pit_channel *s; val &= 0xff; addr &= 3; @@ -244,15 +254,15 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) if (channel == 3) { /* read back command */ for(channel = 0; channel < 3; channel++) { - s = &pit->channels[channel]; + s = &pit->hw.channels[channel]; if (val & (2 << channel)) { if (!(val & 0x20)) { - pit_latch_count(s); + pit_latch_count(pit, channel); } if (!(val & 0x10) && !s->status_latched) { /* status latch */ /* XXX: add BCD and null count */ - s->status = (pit_get_out1(s, hvm_get_guest_time(s->pt.vcpu)) << 7) | + s->status = (pit_get_out(pit, channel, hvm_get_guest_time(pit->pt[channel].vcpu)) << 7) | (s->rw_mode << 4) | (s->mode << 1) | s->bcd; @@ -261,10 +271,10 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } } else { - s = &pit->channels[channel]; + s = &pit->hw.channels[channel]; access = (val >> 4) & 3; if (access == 0) { - pit_latch_count(s); + pit_latch_count(pit, channel); } else { s->rw_mode = access; s->read_state = access; @@ -276,21 +286,21 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } } else { - s = &pit->channels[addr]; + s = &pit->hw.channels[addr]; switch(s->write_state) { default: case RW_STATE_LSB: - pit_load_count(s, addr, val); + pit_load_count(pit, addr, val); break; case RW_STATE_MSB: - pit_load_count(s, addr, val << 8); + pit_load_count(pit, addr, val << 8); break; case RW_STATE_WORD0: s->write_latch = val; s->write_state = RW_STATE_WORD1; break; case RW_STATE_WORD1: - pit_load_count(s, addr, s->write_latch | (val << 8)); + pit_load_count(pit, addr, s->write_latch | (val << 8)); s->write_state = RW_STATE_WORD0; break; } @@ -301,10 +311,10 @@ static uint32_t pit_ioport_read(void *opaque, uint32_t addr) { PITState *pit = opaque; int ret, count; - PITChannelState *s; + struct hvm_hw_pit_channel *s; addr &= 3; - s = &pit->channels[addr]; + s = &pit->hw.channels[addr]; if (s->status_latched) { s->status_latched = 0; ret = s->status; @@ -328,20 +338,20 @@ static uint32_t pit_ioport_read(void *opaque, uint32_t addr) switch(s->read_state) { default: case RW_STATE_LSB: - count = pit_get_count(s); + count = pit_get_count(pit, addr); ret = count & 0xff; break; case RW_STATE_MSB: - count = pit_get_count(s); + count = pit_get_count(pit, addr); ret = (count >> 8) & 0xff; break; case RW_STATE_WORD0: - count = pit_get_count(s); + count = pit_get_count(pit, addr); ret = count & 0xff; s->read_state = RW_STATE_WORD1; break; case RW_STATE_WORD1: - count = pit_get_count(s); + count = pit_get_count(pit, addr); ret = (count >> 8) & 0xff; s->read_state = RW_STATE_WORD0; break; @@ -352,19 +362,19 @@ static uint32_t pit_ioport_read(void *opaque, uint32_t addr) void pit_stop_channel0_irq(PITState * pit) { - PITChannelState *s = &pit->channels[0]; - destroy_periodic_time(&s->pt); + destroy_periodic_time(&pit->pt[0]); } #ifdef HVM_DEBUG_SUSPEND static void pit_info(PITState *pit) { - PITChannelState *s; + struct hvm_hw_pit_channel *s; + struct periodic_time *pt; int i; for(i = 0; i < 3; i++) { printk("*****pit channel %d's state:*****\n", i); - s = &pit->channels[i]; + s = &pit->hw.channels[i]; printk("pit 0x%x.\n", s->count); printk("pit 0x%x.\n", s->latched_count); printk("pit 0x%x.\n", s->count_latched); @@ -379,8 +389,8 @@ static void pit_info(PITState *pit) printk("pit 0x%x.\n", s->gate); printk("pit %"PRId64"\n", s->count_load_time); - if (s->pt) { - struct periodic_time *pt = s->pt; + pt = &pit->pt[i]; + if (pt) { printk("pit channel %d has a periodic timer:\n", i); printk("pt %d.\n", pt->enabled); printk("pt %d.\n", pt->one_shot); @@ -405,131 +415,64 @@ static void pit_save(hvm_domain_context_t *h, void *opaque) { struct domain *d = opaque; PITState *pit = &d->arch.hvm_domain.pl_time.vpit; - PITChannelState *s; - struct periodic_time *pt; - int i, pti = -1; pit_info(pit); - for(i = 0; i < 3; i++) { - s = &pit->channels[i]; - hvm_put_32u(h, s->count); - hvm_put_16u(h, s->latched_count); - hvm_put_8u(h, s->count_latched); - hvm_put_8u(h, s->status_latched); - hvm_put_8u(h, s->status); - hvm_put_8u(h, s->read_state); - hvm_put_8u(h, s->write_state); - hvm_put_8u(h, s->write_latch); - hvm_put_8u(h, s->rw_mode); - hvm_put_8u(h, s->mode); - hvm_put_8u(h, s->bcd); - hvm_put_8u(h, s->gate); - hvm_put_64u(h, s->count_load_time); - - if (s->pt.enabled && pti == -1) - pti = i; - } - - pt = &pit->channels[pti].pt; - - /* save the vcpu for pt */ - hvm_put_32u(h, pt->vcpu->vcpu_id); - - /* save guest time */ - hvm_put_8u(h, pti); - hvm_put_32u(h, pt->pending_intr_nr); - hvm_put_64u(h, pt->last_plt_gtime); - + /* Save the PIT hardware state */ + hvm_put_struct(h, &pit->hw); } static int pit_load(hvm_domain_context_t *h, void *opaque, int version_id) { struct domain *d = opaque; PITState *pit = &d->arch.hvm_domain.pl_time.vpit; - PITChannelState *s; - int i, pti, vcpu_id; - u32 period; + int i; if (version_id != 1) return -EINVAL; - for(i = 0; i < 3; i++) { - s = &pit->channels[i]; - s->count = hvm_get_32u(h); - s->latched_count = hvm_get_16u(h); - s->count_latched = hvm_get_8u(h); - s->status_latched = hvm_get_8u(h); - s->status = hvm_get_8u(h); - s->read_state = hvm_get_8u(h); - s->write_state = hvm_get_8u(h); - s->write_latch = hvm_get_8u(h); - s->rw_mode = hvm_get_8u(h); - s->mode = hvm_get_8u(h); - s->bcd = hvm_get_8u(h); - s->gate = hvm_get_8u(h); - s->count_load_time = hvm_get_64u(h); - } - - vcpu_id = hvm_get_32u(h); - - pti = hvm_get_8u(h); - if ( pti < 0 || pti > 2) { - printk("pit load get a wrong channel %d when HVM resume.\n", pti); - return -EINVAL; - } - - s = &pit->channels[pti]; - period = DIV_ROUND((s->count * 1000000000ULL), PIT_FREQ); + /* Restore the PIT hardware state */ + hvm_get_struct(h, &pit->hw); + + /* Recreate platform timers from hardware state. There will be some + * time jitter here, but the wall-clock will have jumped massively, so + * we hope the guest can handle it. */ - printk("recreate periodic timer %d in mode %d, freq=%d.\n", pti, s->mode, period); - switch (s->mode) { - case 2: - /* create periodic time */ - create_periodic_time(d->vcpu[vcpu_id], &s->pt, period, 0, 0, pit_time_fired, s); - break; - case 1: - /* create one shot time */ - create_periodic_time(d->vcpu[vcpu_id], &s->pt, period, 0, 1, pit_time_fired, s); - break; - default: - printk("pit mode %"PRId8" should not use periodic timer!\n", s->mode); - return -EINVAL; + for(i = 0; i < 3; i++) { + pit_load_count(pit, i, pit_get_count(pit, i)); + pit->pt[i].last_plt_gtime = hvm_get_guest_time(d->vcpu[0]); } - s->pt.pending_intr_nr = hvm_get_32u(h); - s->pt.last_plt_gtime = hvm_get_64u(h); pit_info(pit); - return 0; } static void pit_reset(void *opaque) { PITState *pit = opaque; - PITChannelState *s; + struct hvm_hw_pit_channel *s; int i; for(i = 0;i < 3; i++) { - s = &pit->channels[i]; - destroy_periodic_time(&s->pt); + s = &pit->hw.channels[i]; + destroy_periodic_time(&pit->pt[i]); s->mode = 0xff; /* the init mode */ s->gate = (i != 2); - pit_load_count(s, i, 0); + pit_load_count(pit, i, 0); } } void pit_init(struct vcpu *v, unsigned long cpu_khz) { PITState *pit = &v->domain->arch.hvm_domain.pl_time.vpit; - PITChannelState *s; + struct periodic_time *pt; - s = &pit->channels[0]; - s->pt.vcpu = v; + pt = &pit->pt[0]; + pt->vcpu = v; /* the timer 0 is connected to an IRQ */ - init_timer(&s->pt.timer, pt_timer_fn, &s->pt, v->processor); - s++; s->pt.vcpu = v; - s++; s->pt.vcpu = v; + init_timer(&pt->timer, pt_timer_fn, pt, v->processor); + pt++; pt->vcpu = v; + pt++; pt->vcpu = v; hvm_register_savevm(v->domain, "xen_hvm_i8254", PIT_BASE, 1, pit_save, pit_load, v->domain); register_portio_handler(v->domain, PIT_BASE, 4, handle_pit_io); @@ -546,20 +489,18 @@ void pit_init(struct vcpu *v, unsigned long cpu_khz) void pit_migrate_timers(struct vcpu *v) { PITState *pit = &v->domain->arch.hvm_domain.pl_time.vpit; - PITChannelState *s; + struct periodic_time *pt; - s = &pit->channels[0]; - if ( s->pt.vcpu == v && s->pt.enabled ) - migrate_timer(&s->pt.timer, v->processor); + pt = &pit->pt[0]; + if ( pt->vcpu == v && pt->enabled ) + migrate_timer(&pt->timer, v->processor); } void pit_deinit(struct domain *d) { PITState *pit = &d->arch.hvm_domain.pl_time.vpit; - PITChannelState *s; - s = &pit->channels[0]; - kill_timer(&s->pt.timer); + kill_timer(&pit->pt[0].timer); } /* the intercept action for PIT DM retval:0--not handled; 1--handled */ @@ -590,7 +531,7 @@ static int handle_pit_io(ioreq_t *p) static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val) { PITState *pit = opaque; - pit->speaker_data_on = (val >> 1) & 1; + pit->hw.speaker_data_on = (val >> 1) & 1; pit_set_gate(pit, 2, val & 1); } @@ -598,10 +539,10 @@ static uint32_t speaker_ioport_read(void *opaque, uint32_t addr) { PITState *pit = opaque; int out = pit_get_out(pit, 2, - hvm_get_guest_time(pit->channels[2].pt.vcpu)); + hvm_get_guest_time(pit->pt[2].vcpu)); /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */ unsigned int refresh_clock = ((unsigned int)NOW() >> 14) & 1; - return ((pit->speaker_data_on << 1) | pit_get_gate(pit, 2) | + return ((pit->hw.speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) | refresh_clock << 4); } |