diff options
Diffstat (limited to 'hw/pl031.c')
-rw-r--r-- | hw/pl031.c | 38 |
1 files changed, 26 insertions, 12 deletions
diff --git a/hw/pl031.c b/hw/pl031.c index 713f85d836..9602664da6 100644 --- a/hw/pl031.c +++ b/hw/pl031.c @@ -13,6 +13,7 @@ #include "sysbus.h" #include "qemu-timer.h" +#include "sysemu.h" //#define DEBUG_PL031 @@ -38,6 +39,11 @@ typedef struct { QEMUTimer *timer; qemu_irq irq; + /* Needed to preserve the tick_count across migration, even if the + * absolute value of the rtc_clock is different on the source and + * destination. + */ + uint32_t tick_offset_vmstate; uint32_t tick_offset; uint32_t mr; @@ -68,27 +74,23 @@ static void pl031_interrupt(void * opaque) static uint32_t pl031_get_count(pl031_state *s) { - /* This assumes qemu_get_clock_ns returns the time since the machine was - created. */ - return s->tick_offset + qemu_get_clock_ns(vm_clock) / get_ticks_per_sec(); + int64_t now = qemu_get_clock_ns(rtc_clock); + return s->tick_offset + now / get_ticks_per_sec(); } static void pl031_set_alarm(pl031_state *s) { - int64_t now; uint32_t ticks; - now = qemu_get_clock_ns(vm_clock); - ticks = s->tick_offset + now / get_ticks_per_sec(); - /* The timer wraps around. This subtraction also wraps in the same way, and gives correct results when alarm < now_ticks. */ - ticks = s->mr - ticks; + ticks = s->mr - pl031_get_count(s); DPRINTF("Alarm set in %ud ticks\n", ticks); if (ticks == 0) { qemu_del_timer(s->timer); pl031_interrupt(s); } else { + int64_t now = qemu_get_clock_ns(rtc_clock); qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec()); } } @@ -190,18 +192,29 @@ static int pl031_init(SysBusDevice *dev) sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); - /* ??? We assume vm_clock is zero at this point. */ qemu_get_timedate(&tm, 0); - s->tick_offset = mktimegm(&tm); + s->tick_offset = mktimegm(&tm) - qemu_get_clock_ns(rtc_clock) / get_ticks_per_sec(); - s->timer = qemu_new_timer_ns(vm_clock, pl031_interrupt, s); + s->timer = qemu_new_timer_ns(rtc_clock, pl031_interrupt, s); return 0; } +static void pl031_pre_save(void *opaque) +{ + pl031_state *s = opaque; + + /* tick_offset is base_time - rtc_clock base time. Instead, we want to + * store the base time relative to the vm_clock for backwards-compatibility. */ + int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock); + s->tick_offset_vmstate = s->tick_offset + delta / get_ticks_per_sec(); +} + static int pl031_post_load(void *opaque, int version_id) { pl031_state *s = opaque; + int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock); + s->tick_offset = s->tick_offset_vmstate - delta / get_ticks_per_sec(); pl031_set_alarm(s); return 0; } @@ -210,9 +223,10 @@ static const VMStateDescription vmstate_pl031 = { .name = "pl031", .version_id = 1, .minimum_version_id = 1, + .pre_save = pl031_pre_save, .post_load = pl031_post_load, .fields = (VMStateField[]) { - VMSTATE_UINT32(tick_offset, pl031_state), + VMSTATE_UINT32(tick_offset_vmstate, pl031_state), VMSTATE_UINT32(mr, pl031_state), VMSTATE_UINT32(lr, pl031_state), VMSTATE_UINT32(cr, pl031_state), |