X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/d06cddf517d2b33389c02971b353d10dd4edda1a..f708e736d0dafc05f8b7e9e73d6440c930b94686:/hw/pl031.c diff --git a/hw/pl031.c b/hw/pl031.c index 69abc4f345..757867ff79 100644 --- a/hw/pl031.c +++ b/hw/pl031.c @@ -12,7 +12,8 @@ */ #include "sysbus.h" -#include "qemu-timer.h" +#include "qemu/timer.h" +#include "sysemu/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,32 +74,28 @@ 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()); } } -static uint64_t pl031_read(void *opaque, target_phys_addr_t offset, +static uint64_t pl031_read(void *opaque, hwaddr offset, unsigned size) { pl031_state *s = (pl031_state *)opaque; @@ -133,18 +120,20 @@ static uint64_t pl031_read(void *opaque, target_phys_addr_t offset, case RTC_MIS: return s->is & s->im; case RTC_ICR: - fprintf(stderr, "qemu: pl031_read: Unexpected offset 0x%x\n", - (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "pl031: read of write-only register at offset 0x%x\n", + (int)offset); break; default: - hw_error("pl031_read: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "pl031_read: Bad offset 0x%x\n", (int)offset); break; } return 0; } -static void pl031_write(void * opaque, target_phys_addr_t offset, +static void pl031_write(void * opaque, hwaddr offset, uint64_t value, unsigned size) { pl031_state *s = (pl031_state *)opaque; @@ -180,12 +169,14 @@ static void pl031_write(void * opaque, target_phys_addr_t offset, case RTC_DR: case RTC_MIS: case RTC_RIS: - fprintf(stderr, "qemu: pl031_write: Unexpected offset 0x%x\n", - (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "pl031: write to read-only register at offset 0x%x\n", + (int)offset); break; default: - hw_error("pl031_write: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "pl031_write: Bad offset 0x%x\n", (int)offset); break; } } @@ -205,14 +196,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); @@ -223,7 +250,7 @@ static void pl031_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl031; } -static TypeInfo pl031_info = { +static const TypeInfo pl031_info = { .name = "pl031", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl031_state),