Add a separate guest constant timer, preserve its remaining ticks across VM
exits, and provide TVAL and TCFG helpers for accesses to the guest CSR bank.

Signed-off-by: SignKirigami <[email protected]>
Signed-off-by: Hengyu Yu <[email protected]>
---
 target/loongarch/cpu.c                |  2 +
 target/loongarch/internals.h          |  6 ++-
 target/loongarch/tcg/constant_timer.c | 67 ++++++++++++++++++++++-----
 target/loongarch/tcg/csr_helper.c     | 22 ++++++++-
 target/loongarch/tcg/helper.h         |  2 +
 5 files changed, 84 insertions(+), 15 deletions(-)

diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index b12e8944de..ff36294aa5 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -795,6 +795,8 @@ static void loongarch_cpu_init(Object *obj)
 #ifdef CONFIG_TCG
     timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL,
                   &loongarch_constant_timer_cb, cpu);
+    timer_init_ns(&cpu->guest_timer, QEMU_CLOCK_VIRTUAL,
+                  &loongarch_constant_timer_cb_guest, cpu);
 #endif
 #endif
 }
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index a23b0d3d58..8ae3507c71 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -36,10 +36,12 @@ void loongarch_cpu_set_irq_impl(void *opaque, int irq, int 
level,
                                 bool guest);
 
 void loongarch_constant_timer_cb(void *opaque);
+void loongarch_constant_timer_cb_guest(void *opaque);
 uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu);
-uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu);
+uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu, bool guest);
+void cpu_loongarch_set_guest_timer(LoongArchCPU *cpu, bool on);
 void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
-                                               uint64_t value);
+                                               uint64_t value, bool guest);
 bool loongarch_cpu_has_work(CPUState *cs);
 bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env, bool guest);
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/tcg/constant_timer.c 
b/target/loongarch/tcg/constant_timer.c
index f56e76d482..a341f48489 100644
--- a/target/loongarch/tcg/constant_timer.c
+++ b/target/loongarch/tcg/constant_timer.c
@@ -20,47 +20,92 @@ uint64_t 
cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu)
     return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD;
 }
 
-uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu)
+uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu, bool guest)
 {
+    CPULoongArchState *env = &cpu->env;
     uint64_t now, expire;
+    CPUSysState *sys = get_sys(env, guest);
+
+    if (guest && env_vm_level(env) != LOONGARCH_VM_LEVEL_GUEST) {
+        return sys->CSR_TVAL;
+    }
 
     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    expire = timer_expire_time_ns(&cpu->timer);
+    expire = timer_expire_time_ns(guest ? &cpu->guest_timer : &cpu->timer);
 
     return (expire - now) / TIMER_PERIOD;
 }
 
 void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
-                                               uint64_t value)
+                                               uint64_t value, bool guest)
 {
     CPULoongArchState *env = &cpu->env;
-    CPUSysState *sys = env_sys(env);
     uint64_t now, next;
+    CPUSysState *sys = get_sys(env, guest);
+    QEMUTimer *timer = guest ? &cpu->guest_timer : &cpu->timer;
 
     sys->CSR_TCFG = value;
+    if (guest && env_vm_level(env) != LOONGARCH_VM_LEVEL_GUEST) {
+        return;
+    }
+
     if (value & CONSTANT_TIMER_ENABLE) {
         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
         next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
-        timer_mod(&cpu->timer, next);
+        timer_mod(timer, next);
     } else {
-        timer_del(&cpu->timer);
+        timer_del(timer);
     }
 }
 
-void loongarch_constant_timer_cb(void *opaque)
+void cpu_loongarch_set_guest_timer(LoongArchCPU *cpu, bool on)
 {
-    LoongArchCPU *cpu  = opaque;
     CPULoongArchState *env = &cpu->env;
-    CPUSysState *sys = env_sys(env);
+    CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
+    uint64_t now, next, ticks;
+
+    if (!(guest->CSR_TCFG & CONSTANT_TIMER_ENABLE)) {
+        return;
+    }
+
+    if (on) {
+        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        ticks = guest->CSR_TVAL ? guest->CSR_TVAL :
+                                  (guest->CSR_TCFG & CONSTANT_TIMER_TICK_MASK);
+        guest->CSR_TVAL = 0;
+        next = now + ticks * TIMER_PERIOD;
+        timer_mod(&cpu->guest_timer, next);
+    } else {
+        guest->CSR_TVAL = cpu_loongarch_get_constant_timer_ticks(cpu, true);
+        timer_del(&cpu->guest_timer);
+    }
+}
+
+static void loongarch_constant_timer_cb_impl(void *opaque, bool guest)
+{
+    LoongArchCPU *cpu = opaque;
+    CPULoongArchState *env = &cpu->env;
+    CPUSysState *sys = get_sys(env, guest);
+    QEMUTimer *timer = guest ? &cpu->guest_timer : &cpu->timer;
     uint64_t now, next;
 
     if (FIELD_EX64(sys->CSR_TCFG, CSR_TCFG, PERIODIC)) {
         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
         next = now + (sys->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
-        timer_mod(&cpu->timer, next);
+        timer_mod(timer, next);
     } else {
         sys->CSR_TCFG = FIELD_DP64(sys->CSR_TCFG, CSR_TCFG, EN, 0);
     }
 
-    loongarch_cpu_set_irq(opaque, IRQ_TIMER, 1);
+    loongarch_cpu_set_irq_impl(opaque, IRQ_TIMER, 1, guest);
+}
+
+void loongarch_constant_timer_cb(void *opaque)
+{
+    loongarch_constant_timer_cb_impl(opaque, false);
+}
+
+void loongarch_constant_timer_cb_guest(void *opaque)
+{
+    loongarch_constant_timer_cb_impl(opaque, true);
 }
diff --git a/target/loongarch/tcg/csr_helper.c 
b/target/loongarch/tcg/csr_helper.c
index 7dc33bc180..fa75508244 100644
--- a/target/loongarch/tcg/csr_helper.c
+++ b/target/loongarch/tcg/csr_helper.c
@@ -74,7 +74,14 @@ target_ulong helper_csrrd_tval(CPULoongArchState *env)
 {
     LoongArchCPU *cpu = env_archcpu(env);
 
-    return cpu_loongarch_get_constant_timer_ticks(cpu);
+    return cpu_loongarch_get_constant_timer_ticks(cpu, false);
+}
+
+target_ulong helper_gcsrrd_tval(CPULoongArchState *env)
+{
+    LoongArchCPU *cpu = env_archcpu(env);
+
+    return cpu_loongarch_get_constant_timer_ticks(cpu, true);
 }
 
 target_ulong helper_csrrd_msgir(CPULoongArchState *env)
@@ -129,7 +136,18 @@ target_ulong helper_csrwr_tcfg(CPULoongArchState *env, 
target_ulong val)
     CPUSysState *sys = env_sys(env);
     int64_t old_v = sys->CSR_TCFG;
 
-    cpu_loongarch_store_constant_timer_config(cpu, val);
+    cpu_loongarch_store_constant_timer_config(cpu, val, false);
+
+    return old_v;
+}
+
+target_ulong helper_gcsrwr_tcfg(CPULoongArchState *env, target_ulong val)
+{
+    LoongArchCPU *cpu = env_archcpu(env);
+    CPUSysState *guest = get_sys(env, LOONGARCH_VM_LEVEL_GUEST);
+    int64_t old_v = guest->CSR_TCFG;
+
+    cpu_loongarch_store_constant_timer_config(cpu, val, true);
 
     return old_v;
 }
diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h
index 8a6c62f116..a003266827 100644
--- a/target/loongarch/tcg/helper.h
+++ b/target/loongarch/tcg/helper.h
@@ -100,11 +100,13 @@ DEF_HELPER_1(rdtime_d, i64, env)
 DEF_HELPER_1(csrrd_pgd, i64, env)
 DEF_HELPER_1(csrrd_cpuid, i64, env)
 DEF_HELPER_1(csrrd_tval, i64, env)
+DEF_HELPER_1(gcsrrd_tval, i64, env)
 DEF_HELPER_1(csrrd_msgir, i64, env)
 DEF_HELPER_2(csrwr_stlbps, i64, env, tl)
 DEF_HELPER_2(csrwr_estat, i64, env, tl)
 DEF_HELPER_2(csrwr_asid, i64, env, tl)
 DEF_HELPER_2(csrwr_tcfg, i64, env, tl)
+DEF_HELPER_2(gcsrwr_tcfg, i64, env, tl)
 DEF_HELPER_2(csrwr_ticlr, i64, env, tl)
 DEF_HELPER_2(csrwr_pwcl, i64, env, tl)
 DEF_HELPER_2(csrwr_pwch, i64, env, tl)
-- 
2.55.0


Reply via email to