1
2
3
4
5
6
7
8#include "qemu/osdep.h"
9#include "hw/hw.h"
10#include "qemu/timer.h"
11#include "hw/ptimer.h"
12#include "qemu/host-utils.h"
13#include "sysemu/replay.h"
14#include "sysemu/qtest.h"
15
16#define DELTA_ADJUST 1
17#define DELTA_NO_ADJUST -1
18
19struct ptimer_state
20{
21 uint8_t enabled;
22 uint64_t limit;
23 uint64_t delta;
24 uint32_t period_frac;
25 int64_t period;
26 int64_t last_event;
27 int64_t next_event;
28 uint8_t policy_mask;
29 QEMUBH *bh;
30 QEMUTimer *timer;
31};
32
33
34static void ptimer_trigger(ptimer_state *s)
35{
36 if (s->bh) {
37 replay_bh_schedule_event(s->bh);
38 }
39}
40
41static void ptimer_reload(ptimer_state *s, int delta_adjust)
42{
43 uint32_t period_frac = s->period_frac;
44 uint64_t period = s->period;
45 uint64_t delta = s->delta;
46
47 if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
48 ptimer_trigger(s);
49 }
50
51 if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) {
52 delta = s->delta = s->limit;
53 }
54
55 if (s->period == 0) {
56 if (!qtest_enabled()) {
57 fprintf(stderr, "Timer with period zero, disabling\n");
58 }
59 timer_del(s->timer);
60 s->enabled = 0;
61 return;
62 }
63
64 if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
65 if (delta_adjust != DELTA_NO_ADJUST) {
66 delta += delta_adjust;
67 }
68 }
69
70 if (delta == 0 && (s->policy_mask & PTIMER_POLICY_CONTINUOUS_TRIGGER)) {
71 if (s->enabled == 1 && s->limit == 0) {
72 delta = 1;
73 }
74 }
75
76 if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
77 if (delta_adjust != DELTA_NO_ADJUST) {
78 delta = 1;
79 }
80 }
81
82 if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) {
83 if (s->enabled == 1 && s->limit != 0) {
84 delta = 1;
85 }
86 }
87
88 if (delta == 0) {
89 if (!qtest_enabled()) {
90 fprintf(stderr, "Timer with delta zero, disabling\n");
91 }
92 timer_del(s->timer);
93 s->enabled = 0;
94 return;
95 }
96
97
98
99
100
101
102
103
104
105
106 if (s->enabled == 1 && (delta * period < 10000) && !use_icount) {
107 period = 10000 / delta;
108 period_frac = 0;
109 }
110
111 s->last_event = s->next_event;
112 s->next_event = s->last_event + delta * period;
113 if (period_frac) {
114 s->next_event += ((int64_t)period_frac * delta) >> 32;
115 }
116 timer_mod(s->timer, s->next_event);
117}
118
119static void ptimer_tick(void *opaque)
120{
121 ptimer_state *s = (ptimer_state *)opaque;
122 bool trigger = true;
123
124 if (s->enabled == 2) {
125 s->delta = 0;
126 s->enabled = 0;
127 } else {
128 int delta_adjust = DELTA_ADJUST;
129
130 if (s->delta == 0 || s->limit == 0) {
131
132
133
134
135 delta_adjust = DELTA_NO_ADJUST;
136 }
137
138 if (!(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
139
140
141 trigger = (delta_adjust == DELTA_ADJUST);
142 }
143
144 s->delta = s->limit;
145
146 ptimer_reload(s, delta_adjust);
147 }
148
149 if (trigger) {
150 ptimer_trigger(s);
151 }
152}
153
154uint64_t ptimer_get_count(ptimer_state *s)
155{
156 uint64_t counter;
157
158 if (s->enabled && s->delta != 0) {
159 int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
160 int64_t next = s->next_event;
161 int64_t last = s->last_event;
162 bool expired = (now - next >= 0);
163 bool oneshot = (s->enabled == 2);
164
165
166 if (expired) {
167
168
169 counter = 0;
170 } else {
171 uint64_t rem;
172 uint64_t div;
173 int clz1, clz2;
174 int shift;
175 uint32_t period_frac = s->period_frac;
176 uint64_t period = s->period;
177
178 if (!oneshot && (s->delta * period < 10000) && !use_icount) {
179 period = 10000 / s->delta;
180 period_frac = 0;
181 }
182
183
184
185
186
187
188
189
190
191
192
193 rem = next - now;
194 div = period;
195
196 clz1 = clz64(rem);
197 clz2 = clz64(div);
198 shift = clz1 < clz2 ? clz1 : clz2;
199
200 rem <<= shift;
201 div <<= shift;
202 if (shift >= 32) {
203 div |= ((uint64_t)period_frac << (shift - 32));
204 } else {
205 if (shift != 0)
206 div |= (period_frac >> (32 - shift));
207
208
209 if ((uint32_t)(period_frac << shift))
210 div += 1;
211 }
212 counter = rem / div;
213
214 if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
215
216
217 if (!oneshot && s->delta == s->limit) {
218 if (now == last) {
219
220
221
222 if (counter == s->limit + DELTA_ADJUST) {
223 return 0;
224 }
225 } else if (counter == s->limit) {
226
227
228
229 return 0;
230 }
231 }
232 }
233 }
234
235 if (s->policy_mask & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) {
236
237
238
239 if (now != last) {
240 counter += 1;
241 }
242 }
243 } else {
244 counter = s->delta;
245 }
246 return counter;
247}
248
249void ptimer_set_count(ptimer_state *s, uint64_t count)
250{
251 s->delta = count;
252 if (s->enabled) {
253 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
254 ptimer_reload(s, 0);
255 }
256}
257
258void ptimer_run(ptimer_state *s, int oneshot)
259{
260 bool was_disabled = !s->enabled;
261
262 if (was_disabled && s->period == 0) {
263 if (!qtest_enabled()) {
264 fprintf(stderr, "Timer with period zero, disabling\n");
265 }
266 return;
267 }
268 s->enabled = oneshot ? 2 : 1;
269 if (was_disabled) {
270 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
271 ptimer_reload(s, 0);
272 }
273}
274
275
276
277void ptimer_stop(ptimer_state *s)
278{
279 if (!s->enabled)
280 return;
281
282 s->delta = ptimer_get_count(s);
283 timer_del(s->timer);
284 s->enabled = 0;
285}
286
287
288void ptimer_set_period(ptimer_state *s, int64_t period)
289{
290 s->delta = ptimer_get_count(s);
291 s->period = period;
292 s->period_frac = 0;
293 if (s->enabled) {
294 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
295 ptimer_reload(s, 0);
296 }
297}
298
299
300void ptimer_set_freq(ptimer_state *s, uint32_t freq)
301{
302 s->delta = ptimer_get_count(s);
303 s->period = 1000000000ll / freq;
304 s->period_frac = (1000000000ll << 32) / freq;
305 if (s->enabled) {
306 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
307 ptimer_reload(s, 0);
308 }
309}
310
311
312
313void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
314{
315
316
317
318
319
320
321
322
323
324 if (!use_icount && limit * s->period < 10000 && s->period) {
325 limit = 10000 / s->period;
326 }
327
328 s->limit = limit;
329 if (reload)
330 s->delta = limit;
331 if (s->enabled && reload) {
332 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
333 ptimer_reload(s, 0);
334 }
335}
336
337uint64_t ptimer_get_limit(ptimer_state *s)
338{
339 return s->limit;
340}
341
342const VMStateDescription vmstate_ptimer = {
343 .name = "ptimer",
344 .version_id = 1,
345 .minimum_version_id = 1,
346 .fields = (VMStateField[]) {
347 VMSTATE_UINT8(enabled, ptimer_state),
348 VMSTATE_UINT64(limit, ptimer_state),
349 VMSTATE_UINT64(delta, ptimer_state),
350 VMSTATE_UINT32(period_frac, ptimer_state),
351 VMSTATE_INT64(period, ptimer_state),
352 VMSTATE_INT64(last_event, ptimer_state),
353 VMSTATE_INT64(next_event, ptimer_state),
354 VMSTATE_TIMER_PTR(timer, ptimer_state),
355 VMSTATE_END_OF_LIST()
356 }
357};
358
359ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask)
360{
361 ptimer_state *s;
362
363 s = (ptimer_state *)g_malloc0(sizeof(ptimer_state));
364 s->bh = bh;
365 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s);
366 s->policy_mask = policy_mask;
367 return s;
368}
369