1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include "hw/hw.h"
24#include "hw/mips/cpudevs.h"
25#include "qemu/timer.h"
26#include "sysemu/kvm.h"
27
28#define TIMER_FREQ 100 * 1000 * 1000
29
30
31uint32_t cpu_mips_get_random (CPUMIPSState *env)
32{
33 static uint32_t lfsr = 1;
34 static uint32_t prev_idx = 0;
35 uint32_t idx;
36
37 do {
38 lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u);
39 idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired;
40 } while (idx == prev_idx);
41 prev_idx = idx;
42 return idx;
43}
44
45
46static void cpu_mips_timer_update(CPUMIPSState *env)
47{
48 uint64_t now, next;
49 uint32_t wait;
50
51 now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
52 wait = env->CP0_Compare - env->CP0_Count -
53 (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
54 next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
55 timer_mod(env->timer, next);
56}
57
58
59static void cpu_mips_timer_expire(CPUMIPSState *env)
60{
61 cpu_mips_timer_update(env);
62 if (env->insn_flags & ISA_MIPS32R2) {
63 env->CP0_Cause |= 1 << CP0Ca_TI;
64 }
65 qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
66}
67
68uint32_t cpu_mips_get_count (CPUMIPSState *env)
69{
70 if (env->CP0_Cause & (1 << CP0Ca_DC)) {
71 return env->CP0_Count;
72 } else {
73 uint64_t now;
74
75 now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
76 if (timer_pending(env->timer)
77 && timer_expired(env->timer, now)) {
78
79 cpu_mips_timer_expire(env);
80 }
81
82 return env->CP0_Count +
83 (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
84 }
85}
86
87void cpu_mips_store_count (CPUMIPSState *env, uint32_t count)
88{
89
90
91
92
93
94 if (env->CP0_Cause & (1 << CP0Ca_DC) || !env->timer)
95 env->CP0_Count = count;
96 else {
97
98 env->CP0_Count =
99 count - (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
100 TIMER_FREQ, get_ticks_per_sec());
101
102 cpu_mips_timer_update(env);
103 }
104}
105
106void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value)
107{
108 env->CP0_Compare = value;
109 if (!(env->CP0_Cause & (1 << CP0Ca_DC)))
110 cpu_mips_timer_update(env);
111 if (env->insn_flags & ISA_MIPS32R2)
112 env->CP0_Cause &= ~(1 << CP0Ca_TI);
113 qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
114}
115
116void cpu_mips_start_count(CPUMIPSState *env)
117{
118 cpu_mips_store_count(env, env->CP0_Count);
119}
120
121void cpu_mips_stop_count(CPUMIPSState *env)
122{
123
124 env->CP0_Count += (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
125 TIMER_FREQ, get_ticks_per_sec());
126}
127
128static void mips_timer_cb (void *opaque)
129{
130 CPUMIPSState *env;
131
132 env = opaque;
133#if 0
134 qemu_log("%s\n", __func__);
135#endif
136
137 if (env->CP0_Cause & (1 << CP0Ca_DC))
138 return;
139
140
141
142
143 env->CP0_Count++;
144 cpu_mips_timer_expire(env);
145 env->CP0_Count--;
146}
147
148void cpu_mips_clock_init (CPUMIPSState *env)
149{
150
151
152
153
154 if (!kvm_enabled()) {
155 env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb, env);
156 }
157}
158