1 /*
2 * hpet.c: HPET emulation for HVM guests.
3 * Copyright (c) 2006, Intel Corporation.
4 * Copyright (c) 2006, Keir Fraser <keir@xensource.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <asm/hvm/vpt.h>
20 #include <asm/hvm/io.h>
21 #include <asm/hvm/support.h>
22 #include <asm/hvm/trace.h>
23 #include <asm/current.h>
24 #include <asm/hpet.h>
25 #include <xen/sched.h>
26 #include <xen/event.h>
27 #include <xen/trace.h>
28
29 #define domain_vhpet(x) (&(x)->arch.hvm_domain.pl_time->vhpet)
30 #define vcpu_vhpet(x) (domain_vhpet((x)->domain))
31 #define vhpet_domain(x) (container_of(x, struct pl_time, vhpet)->domain)
32 #define vhpet_vcpu(x) (pt_global_vcpu_target(vhpet_domain(x)))
33
34 #define HPET_BASE_ADDRESS 0xfed00000ULL
35 #define HPET_MMAP_SIZE 1024
36 #define S_TO_NS 1000000000ULL /* 1s = 10^9 ns */
37 #define S_TO_FS 1000000000000000ULL /* 1s = 10^15 fs */
38
39 /* Frequency_of_Xen_systeme_time / frequency_of_HPET = 16 */
40 #define STIME_PER_HPET_TICK 16
41 #define guest_time_hpet(hpet) \
42 (hvm_get_guest_time(vhpet_vcpu(hpet)) / STIME_PER_HPET_TICK)
43
44 #define HPET_TN_INT_ROUTE_CAP_SHIFT 32
45 #define HPET_TN_CFG_BITS_READONLY_OR_RESERVED (HPET_TN_RESERVED | \
46 HPET_TN_PERIODIC_CAP | HPET_TN_64BIT_CAP | HPET_TN_FSB_CAP)
47
48 /* can be routed to IOAPIC.redirect_table[23..20] */
49 #define HPET_TN_INT_ROUTE_CAP (0x00f00000ULL \
50 << HPET_TN_INT_ROUTE_CAP_SHIFT)
51
52 #define HPET_TN_INT_ROUTE_CAP_MASK (0xffffffffULL \
53 << HPET_TN_INT_ROUTE_CAP_SHIFT)
54
55 #define HPET_TN(reg, addr) (((addr) - HPET_Tn_##reg(0)) / \
56 (HPET_Tn_##reg(1) - HPET_Tn_##reg(0)))
57
58 #define hpet_tick_to_ns(h, tick) \
59 ((s_time_t)((((tick) > (h)->hpet_to_ns_limit) ? \
60 ~0ULL : (tick) * (h)->hpet_to_ns_scale) >> 10))
61
62 #define timer_config(h, n) (h->hpet.timers[n].config)
63 #define timer_enabled(h, n) (timer_config(h, n) & HPET_TN_ENABLE)
64 #define timer_is_periodic(h, n) (timer_config(h, n) & HPET_TN_PERIODIC)
65 #define timer_is_32bit(h, n) (timer_config(h, n) & HPET_TN_32BIT)
66 #define hpet_enabled(h) (h->hpet.config & HPET_CFG_ENABLE)
67 #define timer_level(h, n) (timer_config(h, n) & HPET_TN_LEVEL)
68
69 #define timer_int_route(h, n) \
70 ((timer_config(h, n) & HPET_TN_ROUTE) >> HPET_TN_ROUTE_SHIFT)
71
72 #define timer_int_route_cap(h, n) \
73 ((timer_config(h, n) & HPET_TN_INT_ROUTE_CAP_MASK) \
74 >> HPET_TN_INT_ROUTE_CAP_SHIFT)
75
hpet_read_maincounter(HPETState * h,uint64_t guest_time)76 static inline uint64_t hpet_read_maincounter(HPETState *h, uint64_t guest_time)
77 {
78 ASSERT(rw_is_locked(&h->lock));
79
80 if ( hpet_enabled(h) )
81 return guest_time + h->mc_offset;
82 else
83 return h->hpet.mc64;
84 }
85
hpet_get_comparator(HPETState * h,unsigned int tn,uint64_t guest_time)86 static uint64_t hpet_get_comparator(HPETState *h, unsigned int tn,
87 uint64_t guest_time)
88 {
89 uint64_t comparator;
90 uint64_t elapsed;
91
92 ASSERT(rw_is_write_locked(&h->lock));
93
94 comparator = h->hpet.comparator64[tn];
95 if ( hpet_enabled(h) && timer_is_periodic(h, tn) )
96 {
97 /* update comparator by number of periods elapsed since last update */
98 uint64_t period = h->hpet.period[tn];
99 if (period)
100 {
101 elapsed = hpet_read_maincounter(h, guest_time) - comparator;
102 if ( (int64_t)elapsed >= 0 )
103 {
104 comparator += ((elapsed + period) / period) * period;
105 h->hpet.comparator64[tn] = comparator;
106 }
107 }
108 }
109
110 /* truncate if timer is in 32 bit mode */
111 if ( timer_is_32bit(h, tn) )
112 comparator = (uint32_t)comparator;
113 h->hpet.timers[tn].cmp = comparator;
114 return comparator;
115 }
hpet_read64(HPETState * h,unsigned long addr,uint64_t guest_time)116 static inline uint64_t hpet_read64(HPETState *h, unsigned long addr,
117 uint64_t guest_time)
118 {
119 addr &= ~7;
120
121 switch ( addr )
122 {
123 case HPET_ID:
124 return h->hpet.capability;
125 case HPET_CFG:
126 return h->hpet.config;
127 case HPET_STATUS:
128 return h->hpet.isr;
129 case HPET_COUNTER:
130 return hpet_read_maincounter(h, guest_time);
131 case HPET_Tn_CFG(0):
132 case HPET_Tn_CFG(1):
133 case HPET_Tn_CFG(2):
134 return h->hpet.timers[HPET_TN(CFG, addr)].config;
135 case HPET_Tn_CMP(0):
136 case HPET_Tn_CMP(1):
137 case HPET_Tn_CMP(2):
138 return hpet_get_comparator(h, HPET_TN(CMP, addr), guest_time);
139 case HPET_Tn_ROUTE(0):
140 case HPET_Tn_ROUTE(1):
141 case HPET_Tn_ROUTE(2):
142 return h->hpet.timers[HPET_TN(ROUTE, addr)].fsb;
143 }
144
145 return 0;
146 }
147
hpet_check_access_length(unsigned long addr,unsigned long len)148 static inline int hpet_check_access_length(
149 unsigned long addr, unsigned long len)
150 {
151 if ( (addr & (len - 1)) || (len > 8) )
152 {
153 /*
154 * According to ICH9 specification, unaligned accesses may result
155 * in unexpected behaviour or master abort, but should not crash/hang.
156 * Hence we read all-ones, drop writes, and log a warning.
157 */
158 gdprintk(XENLOG_WARNING, "HPET: access across register boundary: "
159 "%lx %lx\n", addr, len);
160 return -EINVAL;
161 }
162
163 return 0;
164 }
165
hpet_read(struct vcpu * v,unsigned long addr,unsigned int length,unsigned long * pval)166 static int hpet_read(
167 struct vcpu *v, unsigned long addr, unsigned int length,
168 unsigned long *pval)
169 {
170 HPETState *h = vcpu_vhpet(v);
171 unsigned long result;
172 uint64_t val;
173
174 if ( !v->domain->arch.hvm_domain.params[HVM_PARAM_HPET_ENABLED] )
175 {
176 result = ~0ul;
177 goto out;
178 }
179
180 addr &= HPET_MMAP_SIZE-1;
181
182 if ( hpet_check_access_length(addr, length) != 0 )
183 {
184 result = ~0ul;
185 goto out;
186 }
187
188 result = addr < HPET_Tn_CMP(0) ||
189 ((addr - HPET_Tn_CMP(0)) % (HPET_Tn_CMP(1) - HPET_Tn_CMP(0))) > 7;
190 if ( result )
191 read_lock(&h->lock);
192 else
193 write_lock(&h->lock);
194
195 val = hpet_read64(h, addr, guest_time_hpet(h));
196
197 if ( result )
198 read_unlock(&h->lock);
199 else
200 write_unlock(&h->lock);
201
202 result = val;
203 if ( length != 8 )
204 result = (val >> ((addr & 7) * 8)) & ((1ULL << (length * 8)) - 1);
205
206 out:
207 *pval = result;
208 return X86EMUL_OKAY;
209 }
210
hpet_stop_timer(HPETState * h,unsigned int tn,uint64_t guest_time)211 static void hpet_stop_timer(HPETState *h, unsigned int tn,
212 uint64_t guest_time)
213 {
214 ASSERT(tn < HPET_TIMER_NUM);
215 ASSERT(rw_is_write_locked(&h->lock));
216 TRACE_1D(TRC_HVM_EMUL_HPET_STOP_TIMER, tn);
217 destroy_periodic_time(&h->pt[tn]);
218 /* read the comparator to get it updated so a read while stopped will
219 * return the expected value. */
220 hpet_get_comparator(h, tn, guest_time);
221 }
222
223 /* the number of HPET tick that stands for
224 * 1/(2^10) second, namely, 0.9765625 milliseconds */
225 #define HPET_TINY_TIME_SPAN ((h->stime_freq >> 10) / STIME_PER_HPET_TICK)
226
hpet_set_timer(HPETState * h,unsigned int tn,uint64_t guest_time)227 static void hpet_set_timer(HPETState *h, unsigned int tn,
228 uint64_t guest_time)
229 {
230 uint64_t tn_cmp, cur_tick, diff;
231 unsigned int irq;
232 unsigned int oneshot;
233
234 ASSERT(tn < HPET_TIMER_NUM);
235 ASSERT(rw_is_write_locked(&h->lock));
236
237 if ( (tn == 0) && (h->hpet.config & HPET_CFG_LEGACY) )
238 {
239 /* HPET specification requires PIT shouldn't generate
240 * interrupts if LegacyReplacementRoute is set for timer0 */
241 pit_stop_channel0_irq(&vhpet_domain(h)->arch.vpit);
242 }
243
244 if ( !timer_enabled(h, tn) )
245 return;
246
247 tn_cmp = hpet_get_comparator(h, tn, guest_time);
248 cur_tick = hpet_read_maincounter(h, guest_time);
249 if ( timer_is_32bit(h, tn) )
250 {
251 tn_cmp = (uint32_t)tn_cmp;
252 cur_tick = (uint32_t)cur_tick;
253 }
254
255 diff = tn_cmp - cur_tick;
256
257 /*
258 * Detect time values set in the past. This is hard to do for 32-bit
259 * comparators as the timer does not have to be set that far in the future
260 * for the counter difference to wrap a 32-bit signed integer. We fudge
261 * by looking for a 'small' time value in the past.
262 */
263 if ( (int64_t)diff < 0 )
264 diff = (timer_is_32bit(h, tn) && (-diff > HPET_TINY_TIME_SPAN))
265 ? (uint32_t)diff : 0;
266
267 if ( (tn <= 1) && (h->hpet.config & HPET_CFG_LEGACY) )
268 /* if LegacyReplacementRoute bit is set, HPET specification requires
269 timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
270 timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. */
271 irq = (tn == 0) ? 0 : 8;
272 else
273 irq = timer_int_route(h, tn);
274
275 /*
276 * diff is the time from now when the timer should fire, for a periodic
277 * timer we also need the period which may be different because time may
278 * have elapsed between the time the comparator was written and the timer
279 * being enabled (now).
280 */
281 oneshot = !timer_is_periodic(h, tn);
282 TRACE_2_LONG_4D(TRC_HVM_EMUL_HPET_START_TIMER, tn, irq,
283 TRC_PAR_LONG(hpet_tick_to_ns(h, diff)),
284 TRC_PAR_LONG(oneshot ? 0LL :
285 hpet_tick_to_ns(h, h->hpet.period[tn])));
286 create_periodic_time(vhpet_vcpu(h), &h->pt[tn],
287 hpet_tick_to_ns(h, diff),
288 oneshot ? 0 : hpet_tick_to_ns(h, h->hpet.period[tn]),
289 irq, NULL, NULL);
290 }
291
hpet_fixup_reg(uint64_t new,uint64_t old,uint64_t mask)292 static inline uint64_t hpet_fixup_reg(
293 uint64_t new, uint64_t old, uint64_t mask)
294 {
295 new &= mask;
296 new |= old & ~mask;
297 return new;
298 }
299
hpet_write(struct vcpu * v,unsigned long addr,unsigned int length,unsigned long val)300 static int hpet_write(
301 struct vcpu *v, unsigned long addr,
302 unsigned int length, unsigned long val)
303 {
304 HPETState *h = vcpu_vhpet(v);
305 uint64_t old_val, new_val;
306 uint64_t guest_time;
307 int tn, i;
308
309 /* Acculumate a bit mask of timers whos state is changed by this write. */
310 unsigned long start_timers = 0;
311 unsigned long stop_timers = 0;
312 #define set_stop_timer(n) (__set_bit((n), &stop_timers))
313 #define set_start_timer(n) (__set_bit((n), &start_timers))
314 #define set_restart_timer(n) (set_stop_timer(n),set_start_timer(n))
315
316 if ( !v->domain->arch.hvm_domain.params[HVM_PARAM_HPET_ENABLED] )
317 goto out;
318
319 addr &= HPET_MMAP_SIZE-1;
320
321 if ( hpet_check_access_length(addr, length) != 0 )
322 goto out;
323
324 write_lock(&h->lock);
325
326 guest_time = guest_time_hpet(h);
327 old_val = hpet_read64(h, addr, guest_time);
328 new_val = val;
329 if ( length != 8 )
330 new_val = hpet_fixup_reg(
331 new_val << (addr & 7) * 8, old_val,
332 ((1ULL << (length*8)) - 1) << ((addr & 7) * 8));
333
334 switch ( addr & ~7 )
335 {
336 case HPET_CFG:
337 h->hpet.config = hpet_fixup_reg(new_val, old_val, 0x3);
338
339 if ( !(old_val & HPET_CFG_ENABLE) && (new_val & HPET_CFG_ENABLE) )
340 {
341 /* Enable main counter and interrupt generation. */
342 h->mc_offset = h->hpet.mc64 - guest_time;
343 for ( i = 0; i < HPET_TIMER_NUM; i++ )
344 {
345 h->hpet.comparator64[i] =
346 h->hpet.timers[i].config & HPET_TN_32BIT ?
347 (uint32_t)h->hpet.timers[i].cmp :
348 h->hpet.timers[i].cmp;
349 if ( timer_enabled(h, i) )
350 set_start_timer(i);
351 }
352 }
353 else if ( (old_val & HPET_CFG_ENABLE) && !(new_val & HPET_CFG_ENABLE) )
354 {
355 /* Halt main counter and disable interrupt generation. */
356 h->hpet.mc64 = h->mc_offset + guest_time;
357 for ( i = 0; i < HPET_TIMER_NUM; i++ )
358 if ( timer_enabled(h, i) )
359 set_stop_timer(i);
360 }
361 break;
362
363 case HPET_COUNTER:
364 h->hpet.mc64 = new_val;
365 if ( hpet_enabled(h) )
366 {
367 gdprintk(XENLOG_WARNING,
368 "HPET: writing main counter but it's not halted!\n");
369 for ( i = 0; i < HPET_TIMER_NUM; i++ )
370 if ( timer_enabled(h, i) )
371 set_restart_timer(i);
372 }
373 break;
374
375 case HPET_Tn_CFG(0):
376 case HPET_Tn_CFG(1):
377 case HPET_Tn_CFG(2):
378 tn = HPET_TN(CFG, addr);
379
380 h->hpet.timers[tn].config = hpet_fixup_reg(new_val, old_val, 0x3f4e);
381
382 if ( timer_level(h, tn) )
383 {
384 gdprintk(XENLOG_ERR,
385 "HPET: level triggered interrupt not supported now\n");
386 domain_crash(current->domain);
387 break;
388 }
389
390 if ( new_val & HPET_TN_32BIT )
391 {
392 h->hpet.timers[tn].cmp = (uint32_t)h->hpet.timers[tn].cmp;
393 h->hpet.period[tn] = (uint32_t)h->hpet.period[tn];
394 }
395 if ( hpet_enabled(h) )
396 {
397 if ( new_val & HPET_TN_ENABLE )
398 {
399 if ( (new_val ^ old_val) & HPET_TN_PERIODIC )
400 /* timer is enabled but switching mode to/from periodic/
401 * one-shot, stop and restart the vpt timer to get it in
402 * the right mode. */
403 set_restart_timer(tn);
404 else if ( (new_val & HPET_TN_32BIT) &&
405 !(old_val & HPET_TN_32BIT) )
406 /* switching from 64 bit to 32 bit mode could cause timer
407 * next fire time, or period, to change. */
408 set_restart_timer(tn);
409 else if ( !(old_val & HPET_TN_ENABLE) )
410 /* transition from timer disabled to timer enabled. */
411 set_start_timer(tn);
412 }
413 else if ( old_val & HPET_TN_ENABLE )
414 /* transition from timer enabled to timer disabled. */
415 set_stop_timer(tn);
416 }
417 break;
418
419 case HPET_Tn_CMP(0):
420 case HPET_Tn_CMP(1):
421 case HPET_Tn_CMP(2):
422 tn = HPET_TN(CMP, addr);
423 if ( timer_is_periodic(h, tn) &&
424 !(h->hpet.timers[tn].config & HPET_TN_SETVAL) )
425 {
426 uint64_t max_period = (timer_is_32bit(h, tn) ? ~0u : ~0ull) >> 1;
427
428 /*
429 * Clamp period to reasonable min/max values:
430 * - minimum is 100us, same as timers controlled by vpt.c
431 * - maximum is to prevent overflow in time_after() calculations
432 */
433 if ( hpet_tick_to_ns(h, new_val) < MICROSECS(100) )
434 new_val = (MICROSECS(100) << 10) / h->hpet_to_ns_scale;
435 if ( new_val > max_period )
436 new_val = max_period;
437 h->hpet.period[tn] = new_val;
438 }
439 else
440 {
441 /*
442 * When SETVAL is one, software is able to "directly set
443 * a periodic timer's accumulator." That is, set the
444 * comparator without adjusting the period. Much the
445 * same as just setting the comparator on an enabled
446 * one-shot timer.
447 *
448 * This configuration bit clears when the comparator is
449 * written.
450 */
451 h->hpet.timers[tn].config &= ~HPET_TN_SETVAL;
452 h->hpet.comparator64[tn] = new_val;
453 /* truncate if timer is in 32 bit mode */
454 if ( timer_is_32bit(h, tn) )
455 new_val = (uint32_t)new_val;
456 h->hpet.timers[tn].cmp = new_val;
457 }
458 if ( hpet_enabled(h) && timer_enabled(h, tn) )
459 set_restart_timer(tn);
460 break;
461
462 case HPET_Tn_ROUTE(0):
463 case HPET_Tn_ROUTE(1):
464 case HPET_Tn_ROUTE(2):
465 tn = HPET_TN(ROUTE, addr);
466 h->hpet.timers[tn].fsb = new_val;
467 break;
468
469 default:
470 /* Ignore writes to unsupported and reserved registers. */
471 break;
472 }
473
474 /* stop/start timers whos state was changed by this write. */
475 while (stop_timers)
476 {
477 i = find_first_set_bit(stop_timers);
478 __clear_bit(i, &stop_timers);
479 hpet_stop_timer(h, i, guest_time);
480 }
481
482 while (start_timers)
483 {
484 i = find_first_set_bit(start_timers);
485 __clear_bit(i, &start_timers);
486 hpet_set_timer(h, i, guest_time);
487 }
488
489 #undef set_stop_timer
490 #undef set_start_timer
491 #undef set_restart_timer
492
493 write_unlock(&h->lock);
494
495 out:
496 return X86EMUL_OKAY;
497 }
498
hpet_range(struct vcpu * v,unsigned long addr)499 static int hpet_range(struct vcpu *v, unsigned long addr)
500 {
501 return ( (addr >= HPET_BASE_ADDRESS) &&
502 (addr < (HPET_BASE_ADDRESS + HPET_MMAP_SIZE)) );
503 }
504
505 static const struct hvm_mmio_ops hpet_mmio_ops = {
506 .check = hpet_range,
507 .read = hpet_read,
508 .write = hpet_write
509 };
510
511
hpet_save(struct domain * d,hvm_domain_context_t * h)512 static int hpet_save(struct domain *d, hvm_domain_context_t *h)
513 {
514 HPETState *hp = domain_vhpet(d);
515 struct vcpu *v = pt_global_vcpu_target(d);
516 int rc;
517 uint64_t guest_time;
518
519 if ( !has_vhpet(d) )
520 return 0;
521
522 write_lock(&hp->lock);
523 guest_time = (v->arch.hvm_vcpu.guest_time ?: hvm_get_guest_time(v)) /
524 STIME_PER_HPET_TICK;
525
526 /* Write the proper value into the main counter */
527 if ( hpet_enabled(hp) )
528 hp->hpet.mc64 = hp->mc_offset + guest_time;
529
530 /* Save the HPET registers */
531 rc = _hvm_init_entry(h, HVM_SAVE_CODE(HPET), 0, HVM_SAVE_LENGTH(HPET));
532 if ( rc == 0 )
533 {
534 struct hvm_hw_hpet *rec = (struct hvm_hw_hpet *)&h->data[h->cur];
535 h->cur += HVM_SAVE_LENGTH(HPET);
536 memset(rec, 0, HVM_SAVE_LENGTH(HPET));
537 #define C(x) rec->x = hp->hpet.x
538 C(capability);
539 C(config);
540 C(isr);
541 C(mc64);
542 C(timers[0].config);
543 C(timers[0].fsb);
544 C(timers[1].config);
545 C(timers[1].fsb);
546 C(timers[2].config);
547 C(timers[2].fsb);
548 C(period[0]);
549 C(period[1]);
550 C(period[2]);
551 #undef C
552 /*
553 * read the comparator to get it updated so hpet_save will
554 * return the expected value.
555 */
556 hpet_get_comparator(hp, 0, guest_time);
557 hpet_get_comparator(hp, 1, guest_time);
558 hpet_get_comparator(hp, 2, guest_time);
559 /*
560 * save the 64 bit comparator in the 64 bit timer[n].cmp
561 * field regardless of whether or not the timer is in 32 bit
562 * mode.
563 */
564 rec->timers[0].cmp = hp->hpet.comparator64[0];
565 rec->timers[1].cmp = hp->hpet.comparator64[1];
566 rec->timers[2].cmp = hp->hpet.comparator64[2];
567 }
568
569 write_unlock(&hp->lock);
570
571 return rc;
572 }
573
hpet_load(struct domain * d,hvm_domain_context_t * h)574 static int hpet_load(struct domain *d, hvm_domain_context_t *h)
575 {
576 HPETState *hp = domain_vhpet(d);
577 struct hvm_hw_hpet *rec;
578 uint64_t cmp;
579 uint64_t guest_time;
580 int i;
581
582 if ( !has_vhpet(d) )
583 return -ENODEV;
584
585 write_lock(&hp->lock);
586
587 /* Reload the HPET registers */
588 if ( _hvm_check_entry(h, HVM_SAVE_CODE(HPET), HVM_SAVE_LENGTH(HPET), 1) )
589 {
590 write_unlock(&hp->lock);
591 return -EINVAL;
592 }
593
594 rec = (struct hvm_hw_hpet *)&h->data[h->cur];
595 h->cur += HVM_SAVE_LENGTH(HPET);
596
597 #define C(x) hp->hpet.x = rec->x
598 C(capability);
599 C(config);
600 C(isr);
601 C(mc64);
602 /* The following define will generate a compiler error if HPET_TIMER_NUM
603 * changes. This indicates an incompatability with previous saved state. */
604 #define HPET_TIMER_NUM 3
605 for ( i = 0; i < HPET_TIMER_NUM; i++ )
606 {
607 C(timers[i].config);
608 C(timers[i].fsb);
609 C(period[i]);
610 /* restore the hidden 64 bit comparator and truncate the timer's
611 * visible comparator field if in 32 bit mode. */
612 cmp = rec->timers[i].cmp;
613 hp->hpet.comparator64[i] = cmp;
614 if ( timer_is_32bit(hp, i) )
615 cmp = (uint32_t)cmp;
616 hp->hpet.timers[i].cmp = cmp;
617 }
618 #undef C
619
620 /* Recalculate the offset between the main counter and guest time */
621 guest_time = guest_time_hpet(hp);
622 hp->mc_offset = hp->hpet.mc64 - guest_time;
623
624 /* restart all timers */
625
626 if ( hpet_enabled(hp) )
627 for ( i = 0; i < HPET_TIMER_NUM; i++ )
628 if ( timer_enabled(hp, i) )
629 hpet_set_timer(hp, i, guest_time);
630
631 write_unlock(&hp->lock);
632
633 return 0;
634 }
635
636 HVM_REGISTER_SAVE_RESTORE(HPET, hpet_save, hpet_load, 1, HVMSR_PER_DOM);
637
hpet_set(HPETState * h)638 static void hpet_set(HPETState *h)
639 {
640 int i;
641
642 memset(h, 0, sizeof(HPETState));
643
644 rwlock_init(&h->lock);
645
646 h->stime_freq = S_TO_NS;
647
648 h->hpet_to_ns_scale = ((S_TO_NS * STIME_PER_HPET_TICK) << 10) / h->stime_freq;
649 h->hpet_to_ns_limit = ~0ULL / h->hpet_to_ns_scale;
650
651 h->hpet.capability = 0x80860001ULL |
652 ((HPET_TIMER_NUM - 1) << HPET_ID_NUMBER_SHIFT) |
653 HPET_ID_64BIT | HPET_ID_LEGSUP;
654
655 /* This is the number of femptoseconds per HPET tick. */
656 /* Here we define HPET's frequency to be 1/16 of Xen system time */
657 h->hpet.capability |= ((S_TO_FS*STIME_PER_HPET_TICK/h->stime_freq) << 32);
658
659 for ( i = 0; i < HPET_TIMER_NUM; i++ )
660 {
661 h->hpet.timers[i].config =
662 HPET_TN_INT_ROUTE_CAP | HPET_TN_64BIT_CAP | HPET_TN_PERIODIC_CAP;
663 h->hpet.timers[i].cmp = ~0ULL;
664 h->hpet.comparator64[i] = ~0ULL;
665 h->pt[i].source = PTSRC_isa;
666 }
667 }
668
hpet_init(struct domain * d)669 void hpet_init(struct domain *d)
670 {
671 if ( !has_vhpet(d) )
672 return;
673
674 hpet_set(domain_vhpet(d));
675 register_mmio_handler(d, &hpet_mmio_ops);
676 d->arch.hvm_domain.params[HVM_PARAM_HPET_ENABLED] = 1;
677 }
678
hpet_deinit(struct domain * d)679 void hpet_deinit(struct domain *d)
680 {
681 int i;
682 HPETState *h = domain_vhpet(d);
683
684 if ( !has_vhpet(d) )
685 return;
686
687 write_lock(&h->lock);
688
689 if ( hpet_enabled(h) )
690 {
691 uint64_t guest_time = guest_time_hpet(h);
692
693 for ( i = 0; i < HPET_TIMER_NUM; i++ )
694 if ( timer_enabled(h, i) )
695 hpet_stop_timer(h, i, guest_time);
696 }
697
698 write_unlock(&h->lock);
699 }
700
hpet_reset(struct domain * d)701 void hpet_reset(struct domain *d)
702 {
703 if ( !has_vhpet(d) )
704 return;
705
706 hpet_deinit(d);
707 hpet_set(domain_vhpet(d));
708 }
709
710 /*
711 * Local variables:
712 * mode: C
713 * c-file-style: "BSD"
714 * c-basic-offset: 4
715 * indent-tabs-mode: nil
716 * End:
717 */
718