diff options
Diffstat (limited to 'hw/pl031.c')
-rw-r--r-- | hw/pl031.c | 75 |
1 files changed, 49 insertions, 26 deletions
diff --git a/hw/pl031.c b/hw/pl031.c index 69abc4f345..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; @@ -47,21 +53,6 @@ typedef struct { uint32_t is; } pl031_state; -static const VMStateDescription vmstate_pl031 = { - .name = "pl031", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(tick_offset, pl031_state), - VMSTATE_UINT32(mr, pl031_state), - VMSTATE_UINT32(lr, pl031_state), - VMSTATE_UINT32(cr, pl031_state), - VMSTATE_UINT32(im, pl031_state), - VMSTATE_UINT32(is, pl031_state), - VMSTATE_END_OF_LIST() - } -}; - static const unsigned char pl031_id[] = { 0x31, 0x10, 0x14, 0x00, /* Device ID */ 0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */ @@ -83,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()); } } @@ -205,14 +192,50 @@ 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(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; - s->timer = qemu_new_timer_ns(vm_clock, pl031_interrupt, s); + 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; } +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_vmstate, pl031_state), + VMSTATE_UINT32(mr, pl031_state), + VMSTATE_UINT32(lr, pl031_state), + VMSTATE_UINT32(cr, pl031_state), + VMSTATE_UINT32(im, pl031_state), + VMSTATE_UINT32(is, pl031_state), + VMSTATE_END_OF_LIST() + } +}; + static void pl031_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); |