summary refs log tree commit diff stats
path: root/hw/apic_common.c
diff options
context:
space:
mode:
authorJan Kiszka <jan.kiszka@siemens.com>2011-10-16 12:19:12 +0200
committerJan Kiszka <jan.kiszka@siemens.com>2012-01-19 12:14:40 +0100
commit7a380ca350f84b5b99391da20a2b4ea505b0524d (patch)
treea47120ebc693433afa1a09d9ef217bb8cc777556 /hw/apic_common.c
parentdae01685280cef9b70ade9167340b5373eada9e8 (diff)
downloadfocaccia-qemu-7a380ca350f84b5b99391da20a2b4ea505b0524d.tar.gz
focaccia-qemu-7a380ca350f84b5b99391da20a2b4ea505b0524d.zip
apic: Open-code timer save/restore
To enable migration between accelerated and non-accelerated APIC models,
we will need to handle the timer saving and restoring specially and can
no longer rely on the automatics of VMSTATE_TIMER. Specifically,
accelerated model will not start any QEMUTimer.

This patch therefore factors out the generic bits into apic_next_timer
and use a post-load callback to implemented model-specific logic.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Diffstat (limited to 'hw/apic_common.c')
-rw-r--r--hw/apic_common.c54
1 files changed, 52 insertions, 2 deletions
diff --git a/hw/apic_common.c b/hw/apic_common.c
index eef977ff1c..e05369caab 100644
--- a/hw/apic_common.c
+++ b/hw/apic_common.c
@@ -93,6 +93,39 @@ void apic_deliver_nmi(DeviceState *d)
     info->external_nmi(s);
 }
 
+bool apic_next_timer(APICCommonState *s, int64_t current_time)
+{
+    int64_t d;
+
+    /* We need to store the timer state separately to support APIC
+     * implementations that maintain a non-QEMU timer, e.g. inside the
+     * host kernel. This open-coded state allows us to migrate between
+     * both models. */
+    s->timer_expiry = -1;
+
+    if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED) {
+        return false;
+    }
+
+    d = (current_time - s->initial_count_load_time) >> s->count_shift;
+
+    if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+        if (!s->initial_count) {
+            return false;
+        }
+        d = ((d / ((uint64_t)s->initial_count + 1)) + 1) *
+            ((uint64_t)s->initial_count + 1);
+    } else {
+        if (d >= s->initial_count) {
+            return false;
+        }
+        d = (uint64_t)s->initial_count + 1;
+    }
+    s->next_time = s->initial_count_load_time + (d << s->count_shift);
+    s->timer_expiry = s->next_time;
+    return true;
+}
+
 void apic_init_reset(DeviceState *d)
 {
     APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
@@ -120,7 +153,10 @@ void apic_init_reset(DeviceState *d)
     s->next_time = 0;
     s->wait_for_sipi = 1;
 
-    qemu_del_timer(s->timer);
+    if (s->timer) {
+        qemu_del_timer(s->timer);
+    }
+    s->timer_expiry = -1;
 }
 
 static void apic_reset_common(DeviceState *d)
@@ -203,12 +239,25 @@ static int apic_init_common(SysBusDevice *dev)
     return 0;
 }
 
+static int apic_dispatch_post_load(void *opaque, int version_id)
+{
+    APICCommonState *s = opaque;
+    APICCommonInfo *info =
+        DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info);
+
+    if (info->post_load) {
+        info->post_load(s);
+    }
+    return 0;
+}
+
 static const VMStateDescription vmstate_apic_common = {
     .name = "apic",
     .version_id = 3,
     .minimum_version_id = 3,
     .minimum_version_id_old = 1,
     .load_state_old = apic_load_old,
+    .post_load = apic_dispatch_post_load,
     .fields = (VMStateField[]) {
         VMSTATE_UINT32(apicbase, APICCommonState),
         VMSTATE_UINT8(id, APICCommonState),
@@ -228,7 +277,8 @@ static const VMStateDescription vmstate_apic_common = {
         VMSTATE_UINT32(initial_count, APICCommonState),
         VMSTATE_INT64(initial_count_load_time, APICCommonState),
         VMSTATE_INT64(next_time, APICCommonState),
-        VMSTATE_TIMER(timer, APICCommonState),
+        VMSTATE_INT64(timer_expiry,
+                      APICCommonState), /* open-coded timer state */
         VMSTATE_END_OF_LIST()
     }
 };