All of lore.kernel.org
 help / color / mirror / Atom feed
diff for duplicates of <53874719.5070604@imgtec.com>

diff --git a/a/content_digest b/N1/content_digest
index f9be802..6cdf46b 100644
--- a/a/content_digest
+++ b/N1/content_digest
@@ -6,9 +6,9 @@
  "To\0Paolo Bonzini <pbonzini@redhat.com>\0"
  "Cc\0Andreas Herrmann <andreas.herrmann@caviumnetworks.com>"
   Gleb Natapov <gleb@kernel.org>
-  <kvm@vger.kernel.org>
+  kvm@vger.kernel.org
   Ralf Baechle <ralf@linux-mips.org>
-  <linux-mips@linux-mips.org>
+  linux-mips@linux-mips.org
   David Daney <david.daney@cavium.com>
  " Sanjay Lal <sanjayl@kymasys.com>\0"
  "\01:1\0"
@@ -38,4 +38,4 @@
  "fn\00001-timer-Add-cpu_get_clock_at.patch\0"
  "b\0"
 
-73ed7cdee6840b69f362dd1d82d2b56d498699a60134fde2fd7f322a5c8b0a0c
+abbe3d0f7b340e3c956b9a7ae3559718962293cc2e1f463a9e92f5217aca1341

diff --git a/a/2.txt b/N2/2.txt
index 8b13789..01e126e 100644
--- a/a/2.txt
+++ b/N2/2.txt
@@ -1 +1,75 @@
+>From 1438f5eb5e05eb5ef8b90a6d52cfe73e67da254f Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Tue, 20 May 2014 00:52:08 +0100
+Subject: [PATCH 1/2] timer: Add cpu_get_clock_at()
 
+Add a new cpu_get_clock_at() which gets the VM time at a particular
+specified monotonic time rather than the current monotonic time. This is
+to allow for operations that may need both the current monotonic time
+and the VM time based on that, such as to allow synchronisation with a
+KVM CPU clock.
+
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+---
+ cpus.c               | 23 +++++++++++++++++++++--
+ include/qemu/timer.h |  1 +
+ 2 files changed, 22 insertions(+), 2 deletions(-)
+
+diff --git a/cpus.c b/cpus.c
+index dd7ac13..ac906a3 100644
+--- a/cpus.c
++++ b/cpus.c
+@@ -186,18 +186,37 @@ int64_t cpu_get_ticks(void)
+     return ticks;
+ }
+ 
+-static int64_t cpu_get_clock_locked(void)
++static int64_t cpu_get_clock_at_locked(int64_t now)
+ {
+     int64_t ticks;
+ 
+     ticks = timers_state.cpu_clock_offset;
+     if (timers_state.cpu_ticks_enabled) {
+-        ticks += get_clock();
++        ticks += now;
+     }
+ 
+     return ticks;
+ }
+ 
++static int64_t cpu_get_clock_locked(void)
++{
++    return cpu_get_clock_at_locked(get_clock());
++}
++
++/* return the host CPU monotonic timer and handle stop/restart */
++int64_t cpu_get_clock_at(int64_t now)
++{
++    int64_t ti;
++    unsigned start;
++
++    do {
++        start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
++        ti = cpu_get_clock_at_locked(now);
++    } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
++
++    return ti;
++}
++
+ /* return the host CPU monotonic timer and handle stop/restart */
+ int64_t cpu_get_clock(void)
+ {
+diff --git a/include/qemu/timer.h b/include/qemu/timer.h
+index 7f9a074..5b2e8c2 100644
+--- a/include/qemu/timer.h
++++ b/include/qemu/timer.h
+@@ -745,6 +745,7 @@ static inline int64_t get_clock(void)
+ /* icount */
+ int64_t cpu_get_icount(void);
+ int64_t cpu_get_clock(void);
++int64_t cpu_get_clock_at(int64_t now);
+ 
+ /*******************************************/
+ /* host CPU ticks (if available) */
+-- 
+1.9.3
diff --git a/N2/3.hdr b/N2/3.hdr
new file mode 100644
index 0000000..0b39d49
--- /dev/null
+++ b/N2/3.hdr
@@ -0,0 +1,5 @@
+Content-Type: text/x-patch;
+	name="0002-target-mips-kvm-Save-restore-KVM-timer-state.patch"
+Content-Transfer-Encoding: 7bit
+Content-Disposition: attachment;
+	filename="0002-target-mips-kvm-Save-restore-KVM-timer-state.patch"
diff --git a/N2/3.txt b/N2/3.txt
new file mode 100644
index 0000000..de08959
--- /dev/null
+++ b/N2/3.txt
@@ -0,0 +1,345 @@
+>From 22c61769ec31cb222c5291c88bd6a658e23862f5 Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Thu, 29 May 2014 14:36:20 +0100
+Subject: [PATCH 2/2] target-mips: kvm: Save/restore KVM timer state
+
+Save and restore KVM timer state properly. When it's saved (or VM clock
+stopped) the VM clock is also recorded. When it's restored (or VM clock
+restarted) it is resumed with the stored count at the saved VM clock
+translated into monotonic time, i.e. current time - elapsed VM
+nanoseconds. This therefore behaves correctly after the VM clock has
+been stopped.
+
+E.g.
+sync state to QEMU
+    takes snapshot of Count, Cause and VM time(now)
+sync state back to KVM
+    restores Count, Cause at translated VM time (unchanged)
+    time continues from the same Count without losing time or interrupts
+
+Or:
+stop vm clock
+    takes snapshot of Count, Cause and VM time
+
+restart vm clock
+    restores Count, Cause at now - (vm time(now) - saved vm time)
+    time continues from the same Count but at a later monotonic time
+    depending on how long the vm clock was stopped
+
+The VM time at which the timer was stopped (count_save_time) is
+saved/loaded by cpu_save and cpu_load so that it can be restarted
+without losing time relative to the VM clock.
+
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+---
+ target-mips/cpu.h     |   3 +-
+ target-mips/kvm.c     | 196 +++++++++++++++++++++++++++++++++++++++++++++++---
+ target-mips/machine.c |   6 ++
+ 3 files changed, 195 insertions(+), 10 deletions(-)
+
+diff --git a/target-mips/cpu.h b/target-mips/cpu.h
+index 67bf441..02a8da1 100644
+--- a/target-mips/cpu.h
++++ b/target-mips/cpu.h
+@@ -495,6 +495,7 @@ struct CPUMIPSState {
+     const mips_def_t *cpu_model;
+     void *irq[8];
+     QEMUTimer *timer; /* Internal timer */
++    int64_t count_save_time;
+ };
+ 
+ #include "cpu-qom.h"
+@@ -526,7 +527,7 @@ void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf);
+ extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env);
+ extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env);
+ 
+-#define CPU_SAVE_VERSION 4
++#define CPU_SAVE_VERSION 5
+ 
+ /* MMU modes definitions. We carefully match the indices with our
+    hflags layout. */
+diff --git a/target-mips/kvm.c b/target-mips/kvm.c
+index 8765205..ee2dd1c 100644
+--- a/target-mips/kvm.c
++++ b/target-mips/kvm.c
+@@ -33,6 +33,8 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+     KVM_CAP_LAST_INFO
+ };
+ 
++static void kvm_mips_update_state(void *opaque, int running, RunState state);
++
+ unsigned long kvm_arch_vcpu_id(CPUState *cs)
+ {
+     return cs->cpu_index;
+@@ -50,6 +52,9 @@ int kvm_arch_init(KVMState *s)
+ int kvm_arch_init_vcpu(CPUState *cs)
+ {
+     int ret = 0;
++
++    qemu_add_vm_change_state_handler(kvm_mips_update_state, cs);
++
+     DPRINTF("%s\n", __func__);
+     return ret;
+ }
+@@ -224,6 +229,17 @@ int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level)
+ #define KVM_REG_MIPS_CP0_XCONTEXT	MIPS_CP0_64(20, 0)
+ #define KVM_REG_MIPS_CP0_ERROREPC	MIPS_CP0_64(30, 0)
+ 
++/* CP0_Count control */
++#define KVM_REG_MIPS_COUNT_CTL          (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
++                                         0x20000 | 0)
++#define KVM_REG_MIPS_COUNT_CTL_DC       0x00000001      /* master disable */
++/* CP0_Count resume monotonic nanoseconds */
++#define KVM_REG_MIPS_COUNT_RESUME       (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
++                                         0x20000 | 1)
++/* CP0_Count rate in Hz */
++#define KVM_REG_MIPS_COUNT_HZ           (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
++                                         0x20000 | 2)
++
+ static inline int kvm_mips_put_one_reg(CPUState *cs, uint64_t reg_id,
+                                        int32_t *addr)
+ {
+@@ -248,6 +264,17 @@ static inline int kvm_mips_put_one_ulreg(CPUState *cs, uint64_t reg_id,
+     return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg);
+ }
+ 
++static inline int kvm_mips_put_one_reg64(CPUState *cs, uint64_t reg_id,
++                                         uint64_t *addr)
++{
++    struct kvm_one_reg cp0reg = {
++        .id = reg_id,
++        .addr = (uintptr_t)addr
++    };
++
++    return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg);
++}
++
+ static inline int kvm_mips_get_one_reg(CPUState *cs, uint64_t reg_id,
+                                        int32_t *addr)
+ {
+@@ -286,6 +313,151 @@ static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64 reg_id,
+     return ret;
+ }
+ 
++static inline int kvm_mips_get_one_reg64(CPUState *cs, uint64 reg_id,
++                                         uint64_t *addr)
++{
++    int ret;
++    struct kvm_one_reg cp0reg = {
++        .id = reg_id,
++        .addr = (uintptr_t)addr
++    };
++
++    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);
++    return ret;
++}
++
++/*
++ * We freeze the KVM timer when either the VM clock is stopped or the state is
++ * saved (the state is dirty).
++ */
++
++/*
++ * Save the state of the KVM timer when VM clock is stopped or state is synced
++ * to QEMU.
++ */
++static int kvm_mips_save_count(CPUState *cs)
++{
++    MIPSCPU *cpu = MIPS_CPU(cs);
++    CPUMIPSState *env = &cpu->env;
++    uint64_t count_ctl, count_resume;
++    int ret;
++
++    /* freeze KVM timer */
++    ret = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
++    if (ret < 0) {
++        return ret;
++    }
++    if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) {
++        count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC;
++        ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
++        if (ret < 0) {
++            return ret;
++        }
++    }
++
++    /* read CP0_Cause */
++    ret = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CAUSE, &env->CP0_Cause);
++    if (ret < 0) {
++        return ret;
++    }
++
++    /* read CP0_Count */
++    ret = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count);
++    if (ret < 0) {
++        return ret;
++    }
++
++    /* read COUNT_RESUME */
++    ret = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_RESUME,
++                                 &count_resume);
++    if (ret < 0) {
++        return ret;
++    }
++
++    /* translate monotonic COUNT_RESUME to VM time */
++    env->count_save_time = cpu_get_clock_at(count_resume);
++
++    return ret;
++}
++
++/*
++ * Restore the state of the KVM timer when VM clock is restarted or state is
++ * synced to KVM.
++ */
++static int kvm_mips_restore_count(CPUState *cs)
++{
++    MIPSCPU *cpu = MIPS_CPU(cs);
++    CPUMIPSState *env = &cpu->env;
++    uint64_t count_ctl, count_resume;
++    int64_t now = get_clock();
++    int ret = 0;
++
++    /* check the timer is frozen */
++    ret = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
++    if (ret < 0) {
++        return ret;
++    }
++    if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) {
++        /* freeze timer (sets COUNT_RESUME for us) */
++        count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC;
++        ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
++        if (ret < 0) {
++            return ret;
++        }
++    } else {
++        /* find time to resume the saved timer at */
++        now = get_clock();
++        count_resume = now - (cpu_get_clock_at(now) - env->count_save_time);
++        ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_RESUME,
++                                     &count_resume);
++        if (ret < 0) {
++            return ret;
++        }
++    }
++
++    /* load CP0_Cause */
++    ret = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_CAUSE, &env->CP0_Cause);
++    if (ret < 0) {
++        return ret;
++    }
++
++    /* load CP0_Count */
++    ret = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count);
++    if (ret < 0) {
++        return ret;
++    }
++
++    /* resume KVM timer */
++    count_ctl &= ~KVM_REG_MIPS_COUNT_CTL_DC;
++    ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
++    return ret;
++}
++
++/*
++ * Handle the VM clock being started or stopped
++ */
++static void kvm_mips_update_state(void *opaque, int running, RunState state)
++{
++    CPUState *cs = opaque;
++    int ret;
++
++    /*
++     * If state is already dirty (synced to QEMU) then the KVM timer state is
++     * already saved and can be restored when it is synced back to KVM.
++     */
++    if (!cs->kvm_vcpu_dirty) {
++        if (running) {
++            ret = kvm_mips_restore_count(cs);
++        } else {
++            ret = kvm_mips_save_count(cs);
++        }
++        if (ret < 0) {
++            fprintf(stderr, "Failed update count (running=%d)\n",
++                    running);
++        }
++    }
++}
++
+ static int kvm_mips_put_cp0_registers(CPUState *cs, int level)
+ {
+     MIPSCPU *cpu = MIPS_CPU(cs);
+@@ -322,15 +494,16 @@ static int kvm_mips_put_cp0_registers(CPUState *cs, int level)
+         fprintf(stderr, "Failed to put BADVADDR\n");
+         return ret;
+     }
+-    if (level != KVM_PUT_RUNTIME_STATE) {
+-        /* don't write guest count during runtime or the clock might drift */
+-        ret |= kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_COUNT,
+-                                    &env->CP0_Count);
++
++    /* If VM clock stopped then state will be restored when it is restarted */
++    if (runstate_is_running()) {
++        ret = kvm_mips_restore_count(cs);
+         if (ret < 0) {
+-            fprintf(stderr, "Failed to put COMPARE\n");
++            fprintf(stderr, "Failed to put COUNT\n");
+             return ret;
+         }
+     }
++
+     ret |= kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_ENTRYHI,
+                                   &env->CP0_EntryHi);
+     if (ret < 0) {
+@@ -397,10 +570,6 @@ static int kvm_mips_get_cp0_registers(CPUState *cs)
+     if (ret < 0) {
+         return ret;
+     }
+-    ret |= kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count);
+-    if (ret < 0) {
+-        return ret;
+-    }
+     ret |= kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_ENTRYHI,
+                                   &env->CP0_EntryHi);
+     if (ret < 0) {
+@@ -419,6 +588,15 @@ static int kvm_mips_get_cp0_registers(CPUState *cs)
+     if (ret < 0) {
+         return ret;
+     }
++
++    /* If VM clock stopped then state was already saved when it was stopped */
++    if (runstate_is_running()) {
++        ret = kvm_mips_save_count(cs);
++        if (ret < 0) {
++            return ret;
++        }
++    }
++
+     ret |= kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_EPC, &env->CP0_EPC);
+     if (ret < 0) {
+         return ret;
+diff --git a/target-mips/machine.c b/target-mips/machine.c
+index 0f36c9e..4916b13 100644
+--- a/target-mips/machine.c
++++ b/target-mips/machine.c
+@@ -150,6 +150,8 @@ void cpu_save(QEMUFile *f, void *opaque)
+         save_tc(f, &env->tcs[i]);
+     for (i = 0; i < MIPS_FPU_MAX; i++)
+         save_fpu(f, &env->fpus[i]);
++
++    qemu_put_sbe64s(f, &env->count_save_time);
+ }
+ 
+ static void load_tc(QEMUFile *f, TCState *tc, int version_id)
+@@ -310,5 +312,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
+ 
+     /* XXX: ensure compatibility for halted bit ? */
+     tlb_flush(CPU(cpu), 1);
++
++    if (version_id >= 5) {
++        qemu_get_sbe64s(f, &env->count_save_time);
++    }
+     return 0;
+ }
+-- 
+1.9.3
diff --git a/a/content_digest b/N2/content_digest
index f9be802..b182606 100644
--- a/a/content_digest
+++ b/N2/content_digest
@@ -37,5 +37,428 @@
  "\01:2\0"
  "fn\00001-timer-Add-cpu_get_clock_at.patch\0"
  "b\0"
+ ">From 1438f5eb5e05eb5ef8b90a6d52cfe73e67da254f Mon Sep 17 00:00:00 2001\n"
+ "From: James Hogan <james.hogan@imgtec.com>\n"
+ "Date: Tue, 20 May 2014 00:52:08 +0100\n"
+ "Subject: [PATCH 1/2] timer: Add cpu_get_clock_at()\n"
+ "\n"
+ "Add a new cpu_get_clock_at() which gets the VM time at a particular\n"
+ "specified monotonic time rather than the current monotonic time. This is\n"
+ "to allow for operations that may need both the current monotonic time\n"
+ "and the VM time based on that, such as to allow synchronisation with a\n"
+ "KVM CPU clock.\n"
+ "\n"
+ "Signed-off-by: James Hogan <james.hogan@imgtec.com>\n"
+ "---\n"
+ " cpus.c               | 23 +++++++++++++++++++++--\n"
+ " include/qemu/timer.h |  1 +\n"
+ " 2 files changed, 22 insertions(+), 2 deletions(-)\n"
+ "\n"
+ "diff --git a/cpus.c b/cpus.c\n"
+ "index dd7ac13..ac906a3 100644\n"
+ "--- a/cpus.c\n"
+ "+++ b/cpus.c\n"
+ "@@ -186,18 +186,37 @@ int64_t cpu_get_ticks(void)\n"
+ "     return ticks;\n"
+ " }\n"
+ " \n"
+ "-static int64_t cpu_get_clock_locked(void)\n"
+ "+static int64_t cpu_get_clock_at_locked(int64_t now)\n"
+ " {\n"
+ "     int64_t ticks;\n"
+ " \n"
+ "     ticks = timers_state.cpu_clock_offset;\n"
+ "     if (timers_state.cpu_ticks_enabled) {\n"
+ "-        ticks += get_clock();\n"
+ "+        ticks += now;\n"
+ "     }\n"
+ " \n"
+ "     return ticks;\n"
+ " }\n"
+ " \n"
+ "+static int64_t cpu_get_clock_locked(void)\n"
+ "+{\n"
+ "+    return cpu_get_clock_at_locked(get_clock());\n"
+ "+}\n"
+ "+\n"
+ "+/* return the host CPU monotonic timer and handle stop/restart */\n"
+ "+int64_t cpu_get_clock_at(int64_t now)\n"
+ "+{\n"
+ "+    int64_t ti;\n"
+ "+    unsigned start;\n"
+ "+\n"
+ "+    do {\n"
+ "+        start = seqlock_read_begin(&timers_state.vm_clock_seqlock);\n"
+ "+        ti = cpu_get_clock_at_locked(now);\n"
+ "+    } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));\n"
+ "+\n"
+ "+    return ti;\n"
+ "+}\n"
+ "+\n"
+ " /* return the host CPU monotonic timer and handle stop/restart */\n"
+ " int64_t cpu_get_clock(void)\n"
+ " {\n"
+ "diff --git a/include/qemu/timer.h b/include/qemu/timer.h\n"
+ "index 7f9a074..5b2e8c2 100644\n"
+ "--- a/include/qemu/timer.h\n"
+ "+++ b/include/qemu/timer.h\n"
+ "@@ -745,6 +745,7 @@ static inline int64_t get_clock(void)\n"
+ " /* icount */\n"
+ " int64_t cpu_get_icount(void);\n"
+ " int64_t cpu_get_clock(void);\n"
+ "+int64_t cpu_get_clock_at(int64_t now);\n"
+ " \n"
+ " /*******************************************/\n"
+ " /* host CPU ticks (if available) */\n"
+ "-- \n"
+ 1.9.3
+ "\01:3\0"
+ "fn\00002-target-mips-kvm-Save-restore-KVM-timer-state.patch\0"
+ "b\0"
+ ">From 22c61769ec31cb222c5291c88bd6a658e23862f5 Mon Sep 17 00:00:00 2001\n"
+ "From: James Hogan <james.hogan@imgtec.com>\n"
+ "Date: Thu, 29 May 2014 14:36:20 +0100\n"
+ "Subject: [PATCH 2/2] target-mips: kvm: Save/restore KVM timer state\n"
+ "\n"
+ "Save and restore KVM timer state properly. When it's saved (or VM clock\n"
+ "stopped) the VM clock is also recorded. When it's restored (or VM clock\n"
+ "restarted) it is resumed with the stored count at the saved VM clock\n"
+ "translated into monotonic time, i.e. current time - elapsed VM\n"
+ "nanoseconds. This therefore behaves correctly after the VM clock has\n"
+ "been stopped.\n"
+ "\n"
+ "E.g.\n"
+ "sync state to QEMU\n"
+ "    takes snapshot of Count, Cause and VM time(now)\n"
+ "sync state back to KVM\n"
+ "    restores Count, Cause at translated VM time (unchanged)\n"
+ "    time continues from the same Count without losing time or interrupts\n"
+ "\n"
+ "Or:\n"
+ "stop vm clock\n"
+ "    takes snapshot of Count, Cause and VM time\n"
+ "\n"
+ "restart vm clock\n"
+ "    restores Count, Cause at now - (vm time(now) - saved vm time)\n"
+ "    time continues from the same Count but at a later monotonic time\n"
+ "    depending on how long the vm clock was stopped\n"
+ "\n"
+ "The VM time at which the timer was stopped (count_save_time) is\n"
+ "saved/loaded by cpu_save and cpu_load so that it can be restarted\n"
+ "without losing time relative to the VM clock.\n"
+ "\n"
+ "Signed-off-by: James Hogan <james.hogan@imgtec.com>\n"
+ "---\n"
+ " target-mips/cpu.h     |   3 +-\n"
+ " target-mips/kvm.c     | 196 +++++++++++++++++++++++++++++++++++++++++++++++---\n"
+ " target-mips/machine.c |   6 ++\n"
+ " 3 files changed, 195 insertions(+), 10 deletions(-)\n"
+ "\n"
+ "diff --git a/target-mips/cpu.h b/target-mips/cpu.h\n"
+ "index 67bf441..02a8da1 100644\n"
+ "--- a/target-mips/cpu.h\n"
+ "+++ b/target-mips/cpu.h\n"
+ "@@ -495,6 +495,7 @@ struct CPUMIPSState {\n"
+ "     const mips_def_t *cpu_model;\n"
+ "     void *irq[8];\n"
+ "     QEMUTimer *timer; /* Internal timer */\n"
+ "+    int64_t count_save_time;\n"
+ " };\n"
+ " \n"
+ " #include \"cpu-qom.h\"\n"
+ "@@ -526,7 +527,7 @@ void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf);\n"
+ " extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env);\n"
+ " extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env);\n"
+ " \n"
+ "-#define CPU_SAVE_VERSION 4\n"
+ "+#define CPU_SAVE_VERSION 5\n"
+ " \n"
+ " /* MMU modes definitions. We carefully match the indices with our\n"
+ "    hflags layout. */\n"
+ "diff --git a/target-mips/kvm.c b/target-mips/kvm.c\n"
+ "index 8765205..ee2dd1c 100644\n"
+ "--- a/target-mips/kvm.c\n"
+ "+++ b/target-mips/kvm.c\n"
+ "@@ -33,6 +33,8 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {\n"
+ "     KVM_CAP_LAST_INFO\n"
+ " };\n"
+ " \n"
+ "+static void kvm_mips_update_state(void *opaque, int running, RunState state);\n"
+ "+\n"
+ " unsigned long kvm_arch_vcpu_id(CPUState *cs)\n"
+ " {\n"
+ "     return cs->cpu_index;\n"
+ "@@ -50,6 +52,9 @@ int kvm_arch_init(KVMState *s)\n"
+ " int kvm_arch_init_vcpu(CPUState *cs)\n"
+ " {\n"
+ "     int ret = 0;\n"
+ "+\n"
+ "+    qemu_add_vm_change_state_handler(kvm_mips_update_state, cs);\n"
+ "+\n"
+ "     DPRINTF(\"%s\\n\", __func__);\n"
+ "     return ret;\n"
+ " }\n"
+ "@@ -224,6 +229,17 @@ int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level)\n"
+ " #define KVM_REG_MIPS_CP0_XCONTEXT\tMIPS_CP0_64(20, 0)\n"
+ " #define KVM_REG_MIPS_CP0_ERROREPC\tMIPS_CP0_64(30, 0)\n"
+ " \n"
+ "+/* CP0_Count control */\n"
+ "+#define KVM_REG_MIPS_COUNT_CTL          (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \\\n"
+ "+                                         0x20000 | 0)\n"
+ "+#define KVM_REG_MIPS_COUNT_CTL_DC       0x00000001      /* master disable */\n"
+ "+/* CP0_Count resume monotonic nanoseconds */\n"
+ "+#define KVM_REG_MIPS_COUNT_RESUME       (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \\\n"
+ "+                                         0x20000 | 1)\n"
+ "+/* CP0_Count rate in Hz */\n"
+ "+#define KVM_REG_MIPS_COUNT_HZ           (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \\\n"
+ "+                                         0x20000 | 2)\n"
+ "+\n"
+ " static inline int kvm_mips_put_one_reg(CPUState *cs, uint64_t reg_id,\n"
+ "                                        int32_t *addr)\n"
+ " {\n"
+ "@@ -248,6 +264,17 @@ static inline int kvm_mips_put_one_ulreg(CPUState *cs, uint64_t reg_id,\n"
+ "     return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg);\n"
+ " }\n"
+ " \n"
+ "+static inline int kvm_mips_put_one_reg64(CPUState *cs, uint64_t reg_id,\n"
+ "+                                         uint64_t *addr)\n"
+ "+{\n"
+ "+    struct kvm_one_reg cp0reg = {\n"
+ "+        .id = reg_id,\n"
+ "+        .addr = (uintptr_t)addr\n"
+ "+    };\n"
+ "+\n"
+ "+    return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg);\n"
+ "+}\n"
+ "+\n"
+ " static inline int kvm_mips_get_one_reg(CPUState *cs, uint64_t reg_id,\n"
+ "                                        int32_t *addr)\n"
+ " {\n"
+ "@@ -286,6 +313,151 @@ static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64 reg_id,\n"
+ "     return ret;\n"
+ " }\n"
+ " \n"
+ "+static inline int kvm_mips_get_one_reg64(CPUState *cs, uint64 reg_id,\n"
+ "+                                         uint64_t *addr)\n"
+ "+{\n"
+ "+    int ret;\n"
+ "+    struct kvm_one_reg cp0reg = {\n"
+ "+        .id = reg_id,\n"
+ "+        .addr = (uintptr_t)addr\n"
+ "+    };\n"
+ "+\n"
+ "+    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);\n"
+ "+    return ret;\n"
+ "+}\n"
+ "+\n"
+ "+/*\n"
+ "+ * We freeze the KVM timer when either the VM clock is stopped or the state is\n"
+ "+ * saved (the state is dirty).\n"
+ "+ */\n"
+ "+\n"
+ "+/*\n"
+ "+ * Save the state of the KVM timer when VM clock is stopped or state is synced\n"
+ "+ * to QEMU.\n"
+ "+ */\n"
+ "+static int kvm_mips_save_count(CPUState *cs)\n"
+ "+{\n"
+ "+    MIPSCPU *cpu = MIPS_CPU(cs);\n"
+ "+    CPUMIPSState *env = &cpu->env;\n"
+ "+    uint64_t count_ctl, count_resume;\n"
+ "+    int ret;\n"
+ "+\n"
+ "+    /* freeze KVM timer */\n"
+ "+    ret = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);\n"
+ "+    if (ret < 0) {\n"
+ "+        return ret;\n"
+ "+    }\n"
+ "+    if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) {\n"
+ "+        count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC;\n"
+ "+        ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);\n"
+ "+        if (ret < 0) {\n"
+ "+            return ret;\n"
+ "+        }\n"
+ "+    }\n"
+ "+\n"
+ "+    /* read CP0_Cause */\n"
+ "+    ret = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CAUSE, &env->CP0_Cause);\n"
+ "+    if (ret < 0) {\n"
+ "+        return ret;\n"
+ "+    }\n"
+ "+\n"
+ "+    /* read CP0_Count */\n"
+ "+    ret = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count);\n"
+ "+    if (ret < 0) {\n"
+ "+        return ret;\n"
+ "+    }\n"
+ "+\n"
+ "+    /* read COUNT_RESUME */\n"
+ "+    ret = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_RESUME,\n"
+ "+                                 &count_resume);\n"
+ "+    if (ret < 0) {\n"
+ "+        return ret;\n"
+ "+    }\n"
+ "+\n"
+ "+    /* translate monotonic COUNT_RESUME to VM time */\n"
+ "+    env->count_save_time = cpu_get_clock_at(count_resume);\n"
+ "+\n"
+ "+    return ret;\n"
+ "+}\n"
+ "+\n"
+ "+/*\n"
+ "+ * Restore the state of the KVM timer when VM clock is restarted or state is\n"
+ "+ * synced to KVM.\n"
+ "+ */\n"
+ "+static int kvm_mips_restore_count(CPUState *cs)\n"
+ "+{\n"
+ "+    MIPSCPU *cpu = MIPS_CPU(cs);\n"
+ "+    CPUMIPSState *env = &cpu->env;\n"
+ "+    uint64_t count_ctl, count_resume;\n"
+ "+    int64_t now = get_clock();\n"
+ "+    int ret = 0;\n"
+ "+\n"
+ "+    /* check the timer is frozen */\n"
+ "+    ret = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);\n"
+ "+    if (ret < 0) {\n"
+ "+        return ret;\n"
+ "+    }\n"
+ "+    if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) {\n"
+ "+        /* freeze timer (sets COUNT_RESUME for us) */\n"
+ "+        count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC;\n"
+ "+        ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);\n"
+ "+        if (ret < 0) {\n"
+ "+            return ret;\n"
+ "+        }\n"
+ "+    } else {\n"
+ "+        /* find time to resume the saved timer at */\n"
+ "+        now = get_clock();\n"
+ "+        count_resume = now - (cpu_get_clock_at(now) - env->count_save_time);\n"
+ "+        ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_RESUME,\n"
+ "+                                     &count_resume);\n"
+ "+        if (ret < 0) {\n"
+ "+            return ret;\n"
+ "+        }\n"
+ "+    }\n"
+ "+\n"
+ "+    /* load CP0_Cause */\n"
+ "+    ret = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_CAUSE, &env->CP0_Cause);\n"
+ "+    if (ret < 0) {\n"
+ "+        return ret;\n"
+ "+    }\n"
+ "+\n"
+ "+    /* load CP0_Count */\n"
+ "+    ret = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count);\n"
+ "+    if (ret < 0) {\n"
+ "+        return ret;\n"
+ "+    }\n"
+ "+\n"
+ "+    /* resume KVM timer */\n"
+ "+    count_ctl &= ~KVM_REG_MIPS_COUNT_CTL_DC;\n"
+ "+    ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);\n"
+ "+    return ret;\n"
+ "+}\n"
+ "+\n"
+ "+/*\n"
+ "+ * Handle the VM clock being started or stopped\n"
+ "+ */\n"
+ "+static void kvm_mips_update_state(void *opaque, int running, RunState state)\n"
+ "+{\n"
+ "+    CPUState *cs = opaque;\n"
+ "+    int ret;\n"
+ "+\n"
+ "+    /*\n"
+ "+     * If state is already dirty (synced to QEMU) then the KVM timer state is\n"
+ "+     * already saved and can be restored when it is synced back to KVM.\n"
+ "+     */\n"
+ "+    if (!cs->kvm_vcpu_dirty) {\n"
+ "+        if (running) {\n"
+ "+            ret = kvm_mips_restore_count(cs);\n"
+ "+        } else {\n"
+ "+            ret = kvm_mips_save_count(cs);\n"
+ "+        }\n"
+ "+        if (ret < 0) {\n"
+ "+            fprintf(stderr, \"Failed update count (running=%d)\\n\",\n"
+ "+                    running);\n"
+ "+        }\n"
+ "+    }\n"
+ "+}\n"
+ "+\n"
+ " static int kvm_mips_put_cp0_registers(CPUState *cs, int level)\n"
+ " {\n"
+ "     MIPSCPU *cpu = MIPS_CPU(cs);\n"
+ "@@ -322,15 +494,16 @@ static int kvm_mips_put_cp0_registers(CPUState *cs, int level)\n"
+ "         fprintf(stderr, \"Failed to put BADVADDR\\n\");\n"
+ "         return ret;\n"
+ "     }\n"
+ "-    if (level != KVM_PUT_RUNTIME_STATE) {\n"
+ "-        /* don't write guest count during runtime or the clock might drift */\n"
+ "-        ret |= kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_COUNT,\n"
+ "-                                    &env->CP0_Count);\n"
+ "+\n"
+ "+    /* If VM clock stopped then state will be restored when it is restarted */\n"
+ "+    if (runstate_is_running()) {\n"
+ "+        ret = kvm_mips_restore_count(cs);\n"
+ "         if (ret < 0) {\n"
+ "-            fprintf(stderr, \"Failed to put COMPARE\\n\");\n"
+ "+            fprintf(stderr, \"Failed to put COUNT\\n\");\n"
+ "             return ret;\n"
+ "         }\n"
+ "     }\n"
+ "+\n"
+ "     ret |= kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_ENTRYHI,\n"
+ "                                   &env->CP0_EntryHi);\n"
+ "     if (ret < 0) {\n"
+ "@@ -397,10 +570,6 @@ static int kvm_mips_get_cp0_registers(CPUState *cs)\n"
+ "     if (ret < 0) {\n"
+ "         return ret;\n"
+ "     }\n"
+ "-    ret |= kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count);\n"
+ "-    if (ret < 0) {\n"
+ "-        return ret;\n"
+ "-    }\n"
+ "     ret |= kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_ENTRYHI,\n"
+ "                                   &env->CP0_EntryHi);\n"
+ "     if (ret < 0) {\n"
+ "@@ -419,6 +588,15 @@ static int kvm_mips_get_cp0_registers(CPUState *cs)\n"
+ "     if (ret < 0) {\n"
+ "         return ret;\n"
+ "     }\n"
+ "+\n"
+ "+    /* If VM clock stopped then state was already saved when it was stopped */\n"
+ "+    if (runstate_is_running()) {\n"
+ "+        ret = kvm_mips_save_count(cs);\n"
+ "+        if (ret < 0) {\n"
+ "+            return ret;\n"
+ "+        }\n"
+ "+    }\n"
+ "+\n"
+ "     ret |= kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_EPC, &env->CP0_EPC);\n"
+ "     if (ret < 0) {\n"
+ "         return ret;\n"
+ "diff --git a/target-mips/machine.c b/target-mips/machine.c\n"
+ "index 0f36c9e..4916b13 100644\n"
+ "--- a/target-mips/machine.c\n"
+ "+++ b/target-mips/machine.c\n"
+ "@@ -150,6 +150,8 @@ void cpu_save(QEMUFile *f, void *opaque)\n"
+ "         save_tc(f, &env->tcs[i]);\n"
+ "     for (i = 0; i < MIPS_FPU_MAX; i++)\n"
+ "         save_fpu(f, &env->fpus[i]);\n"
+ "+\n"
+ "+    qemu_put_sbe64s(f, &env->count_save_time);\n"
+ " }\n"
+ " \n"
+ " static void load_tc(QEMUFile *f, TCState *tc, int version_id)\n"
+ "@@ -310,5 +312,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)\n"
+ " \n"
+ "     /* XXX: ensure compatibility for halted bit ? */\n"
+ "     tlb_flush(CPU(cpu), 1);\n"
+ "+\n"
+ "+    if (version_id >= 5) {\n"
+ "+        qemu_get_sbe64s(f, &env->count_save_time);\n"
+ "+    }\n"
+ "     return 0;\n"
+ " }\n"
+ "-- \n"
+ 1.9.3
 
-73ed7cdee6840b69f362dd1d82d2b56d498699a60134fde2fd7f322a5c8b0a0c
+0bf7b602beea1679ed1a85fa43bbb487ec7257befad5cc4cd79f5fc5e6c4b076

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.