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