aboutsummaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorOt ten Thije <ottenthije@google.com>2010-09-06 15:03:52 +0100
committerOt ten Thije <ottenthije@google.com>2010-09-06 15:47:45 +0100
commita7f114bcbd85d71f59f716df5a38340bdfe30637 (patch)
tree0697b0290c7e710fa74797b820bcd8a184edc74a /hw
parentcefa7443eb3d3e4bb134595f756145426d5613e3 (diff)
downloadexternal_qemu-a7f114bcbd85d71f59f716df5a38340bdfe30637.zip
external_qemu-a7f114bcbd85d71f59f716df5a38340bdfe30637.tar.gz
external_qemu-a7f114bcbd85d71f59f716df5a38340bdfe30637.tar.bz2
Fix suspending and resuming timers.
This brings the code for saving and restoring the vm clock up to date with the current codebase. It also includes a workaround for an issue that occurred when the state of a goldfish timer was restored. For unexplained reasons, the compiler generated code to copy the least significant 32 bits only when reading a 64 bit number, dropping the remaining 32 bits. In order to facilitate debugging, the variables in the goldfish timer code have been suffixed with their units ("ns" for nanoseconds or "tks" for ticks).
Diffstat (limited to 'hw')
-rw-r--r--hw/goldfish_timer.c63
1 files changed, 37 insertions, 26 deletions
diff --git a/hw/goldfish_timer.c b/hw/goldfish_timer.c
index d5c9a33..ca90e0e 100644
--- a/hw/goldfish_timer.c
+++ b/hw/goldfish_timer.c
@@ -14,6 +14,7 @@
#include "cpu.h"
#include "arm_pic.h"
#include "goldfish_device.h"
+#include "hw/hw.h"
enum {
TIMER_TIME_LOW = 0x00, // get low bits of current time and update TIMER_TIME_HIGH
@@ -26,25 +27,35 @@ enum {
struct timer_state {
struct goldfish_device dev;
- uint32_t alarm_low;
- int32_t alarm_high;
- int64_t now;
+ uint32_t alarm_low_ns;
+ int32_t alarm_high_ns;
+ int64_t now_ns;
int armed;
QEMUTimer *timer;
};
+/* Converts nanoseconds into ticks */
+static int64_t ns2tks(int64_t ns) {
+ return muldiv64(ns, get_ticks_per_sec(), 1000000000);
+}
+
+/* Converts ticks into nanoseconds */
+static int64_t tks2ns(int64_t tks) {
+ return muldiv64(tks, 1000000000, get_ticks_per_sec());
+}
+
#define GOLDFISH_TIMER_SAVE_VERSION 1
static void goldfish_timer_save(QEMUFile* f, void* opaque)
{
struct timer_state* s = opaque;
- qemu_put_be64(f, s->now); /* in case the kernel is in the middle of a timer read */
+ qemu_put_be64(f, s->now_ns); /* in case the kernel is in the middle of a timer read */
qemu_put_byte(f, s->armed);
if (s->armed) {
- int64_t now = qemu_get_clock(vm_clock);
- int64_t alarm = muldiv64(s->alarm_low | (int64_t)s->alarm_high << 32, get_ticks_per_sec(), 1000000000);
- qemu_put_be64(f, alarm-now);
+ int64_t now_tks = qemu_get_clock(vm_clock);
+ int64_t alarm_tks = ns2tks(s->alarm_low_ns | (int64_t)s->alarm_high_ns << 32);
+ qemu_put_be64(f, alarm_tks - now_tks);
}
}
@@ -55,18 +66,19 @@ static int goldfish_timer_load(QEMUFile* f, void* opaque, int version_id)
if (version_id != GOLDFISH_TIMER_SAVE_VERSION)
return -1;
- s->now = qemu_get_be64(f);
- s->armed = qemu_get_byte(f);
+ s->now_ns = qemu_get_sbe64(f); /* using qemu_get_be64 (without 's') causes faulty code generation
+ in the compiler, dropping the 32 most significant bits */
+ s->armed = qemu_get_byte(f);
if (s->armed) {
- int64_t now = qemu_get_clock(vm_clock);
- int64_t diff = qemu_get_be64(f);
- int64_t alarm = now + diff;
+ int64_t now_tks = qemu_get_clock(vm_clock);
+ int64_t diff_tks = qemu_get_be64(f);
+ int64_t alarm_tks = now_tks + diff_tks;
- if (alarm <= now) {
+ if (alarm_tks <= now_tks) {
goldfish_device_set_irq(&s->dev, 0, 1);
s->armed = 0;
} else {
- qemu_mod_timer(s->timer, alarm);
+ qemu_mod_timer(s->timer, alarm_tks);
}
}
return 0;
@@ -77,35 +89,34 @@ static uint32_t goldfish_timer_read(void *opaque, target_phys_addr_t offset)
struct timer_state *s = (struct timer_state *)opaque;
switch(offset) {
case TIMER_TIME_LOW:
- s->now = muldiv64(qemu_get_clock(vm_clock), 1000000000, get_ticks_per_sec());
- return s->now;
+ s->now_ns = tks2ns(qemu_get_clock(vm_clock));
+ return s->now_ns;
case TIMER_TIME_HIGH:
- return s->now >> 32;
+ return s->now_ns >> 32;
default:
cpu_abort (cpu_single_env, "goldfish_timer_read: Bad offset %x\n", offset);
return 0;
}
}
-static void goldfish_timer_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+static void goldfish_timer_write(void *opaque, target_phys_addr_t offset, uint32_t value_ns)
{
struct timer_state *s = (struct timer_state *)opaque;
- int64_t alarm, now;
+ int64_t alarm_tks, now_tks;
switch(offset) {
case TIMER_ALARM_LOW:
- s->alarm_low = value;
- alarm = muldiv64(s->alarm_low | (int64_t)s->alarm_high << 32, get_ticks_per_sec(), 1000000000);
- now = qemu_get_clock(vm_clock);
- if (alarm <= now) {
+ s->alarm_low_ns = value_ns;
+ alarm_tks = ns2tks(s->alarm_low_ns | (int64_t)s->alarm_high_ns << 32);
+ now_tks = qemu_get_clock(vm_clock);
+ if (alarm_tks <= now_tks) {
goldfish_device_set_irq(&s->dev, 0, 1);
} else {
- qemu_mod_timer(s->timer, alarm);
+ qemu_mod_timer(s->timer, alarm_tks);
s->armed = 1;
}
break;
case TIMER_ALARM_HIGH:
- s->alarm_high = value;
- //printf("alarm_high %d\n", s->alarm_high);
+ s->alarm_high_ns = value_ns;
break;
case TIMER_CLEAR_ALARM:
qemu_del_timer(s->timer);