debuggers.hg

view xen/arch/x86/hvm/hpet.c @ 16745:51aa2f884f64

hvm: hpet: Tidy up hpet_to_ns_limit calculation.
Suggested by Haitao Shan @ Intel.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jan 11 11:01:36 2008 +0000 (2008-01-11)
parents 1b2be7cf0b7b
children 209512f6d89c
line source
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, write to the Free Software Foundation, Inc., 59 Temple
17 * Place - Suite 330, Boston, MA 02111-1307 USA.
18 */
20 #include <asm/hvm/vpt.h>
21 #include <asm/hvm/io.h>
22 #include <asm/hvm/support.h>
23 #include <asm/current.h>
24 #include <xen/sched.h>
25 #include <xen/event.h>
27 #define HPET_BASE_ADDRESS 0xfed00000ULL
28 #define HPET_MMAP_SIZE 1024
29 #define S_TO_NS 1000000000ULL /* 1s = 10^9 ns */
30 #define S_TO_FS 1000000000000000ULL /* 1s = 10^15 fs */
32 /* Frequency_of_TSC / frequency_of_HPET = 32 */
33 #define TSC_PER_HPET_TICK 32
34 #define guest_time_hpet(v) (hvm_get_guest_time(v) / TSC_PER_HPET_TICK)
36 #define HPET_ID 0x000
37 #define HPET_PERIOD 0x004
38 #define HPET_CFG 0x010
39 #define HPET_STATUS 0x020
40 #define HPET_COUNTER 0x0f0
41 #define HPET_T0_CFG 0x100
42 #define HPET_T0_CMP 0x108
43 #define HPET_T0_ROUTE 0x110
44 #define HPET_T1_CFG 0x120
45 #define HPET_T1_CMP 0x128
46 #define HPET_T1_ROUTE 0x130
47 #define HPET_T2_CFG 0x140
48 #define HPET_T2_CMP 0x148
49 #define HPET_T2_ROUTE 0x150
50 #define HPET_T3_CFG 0x160
52 #define HPET_CFG_ENABLE 0x001
53 #define HPET_CFG_LEGACY 0x002
55 #define HPET_TN_INT_TYPE_LEVEL 0x002
56 #define HPET_TN_ENABLE 0x004
57 #define HPET_TN_PERIODIC 0x008
58 #define HPET_TN_PERIODIC_CAP 0x010
59 #define HPET_TN_SIZE_CAP 0x020
60 #define HPET_TN_SETVAL 0x040
61 #define HPET_TN_32BIT 0x100
62 #define HPET_TN_INT_ROUTE_MASK 0x3e00
63 #define HPET_TN_INT_ROUTE_SHIFT 9
64 #define HPET_TN_INT_ROUTE_CAP_SHIFT 32
65 #define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
67 /* can be routed to IOAPIC.redirect_table[23..20] */
68 #define HPET_TN_INT_ROUTE_CAP (0x00f00000ULL \
69 << HPET_TN_INT_ROUTE_CAP_SHIFT)
71 #define HPET_TN_INT_ROUTE_CAP_MASK (0xffffffffULL \
72 << HPET_TN_INT_ROUTE_CAP_SHIFT)
74 #define hpet_tick_to_ns(h, tick) \
75 ((s_time_t)((((tick) > (h)->hpet_to_ns_limit) ? \
76 ~0ULL : (tick) * (h)->hpet_to_ns_scale) >> 10))
78 #define timer_config(h, n) (h->hpet.timers[n].config)
79 #define timer_is_periodic(h, n) (timer_config(h, n) & HPET_TN_PERIODIC)
80 #define timer_is_32bit(h, n) (timer_config(h, n) & HPET_TN_32BIT)
81 #define hpet_enabled(h) (h->hpet.config & HPET_CFG_ENABLE)
82 #define timer_level(h, n) (timer_config(h, n) & HPET_TN_INT_TYPE_LEVEL)
84 #define timer_int_route(h, n) \
85 ((timer_config(h, n) & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT)
87 #define timer_int_route_cap(h, n) \
88 ((timer_config(h, n) & HPET_TN_INT_ROUTE_CAP_MASK) \
89 >> HPET_TN_INT_ROUTE_CAP_SHIFT)
91 #define hpet_time_after(a, b) ((int32_t)(b) - (int32_t)(a) < 0)
92 #define hpet_time_after64(a, b) ((int64_t)(b) - (int64_t)(a) < 0)
94 static inline uint64_t hpet_read64(HPETState *h, unsigned long addr)
95 {
96 addr &= ~7;
98 switch ( addr )
99 {
100 case HPET_ID:
101 return h->hpet.capability;
102 case HPET_CFG:
103 return h->hpet.config;
104 case HPET_STATUS:
105 return h->hpet.isr;
106 case HPET_COUNTER:
107 return h->hpet.mc64;
108 case HPET_T0_CFG:
109 case HPET_T1_CFG:
110 case HPET_T2_CFG:
111 return h->hpet.timers[(addr - HPET_T0_CFG) >> 5].config;
112 case HPET_T0_CMP:
113 case HPET_T1_CMP:
114 case HPET_T2_CMP:
115 return h->hpet.timers[(addr - HPET_T0_CMP) >> 5].cmp;
116 case HPET_T0_ROUTE:
117 case HPET_T1_ROUTE:
118 case HPET_T2_ROUTE:
119 return h->hpet.timers[(addr - HPET_T0_ROUTE) >> 5].fsb;
120 }
122 return 0;
123 }
125 static inline int hpet_check_access_length(
126 unsigned long addr, unsigned long len)
127 {
128 if ( (addr & (len - 1)) || (len > 8) )
129 {
130 /*
131 * According to ICH9 specification, unaligned accesses may result
132 * in unexpected behaviour or master abort, but should not crash/hang.
133 * Hence we read all-ones, drop writes, and log a warning.
134 */
135 gdprintk(XENLOG_WARNING, "HPET: access across register boundary: "
136 "%lx %lx\n", addr, len);
137 return -EINVAL;
138 }
140 return 0;
141 }
143 static inline uint64_t hpet_read_maincounter(HPETState *h)
144 {
145 ASSERT(spin_is_locked(&h->lock));
147 if ( hpet_enabled(h) )
148 return guest_time_hpet(h->vcpu) + h->mc_offset;
149 else
150 return h->hpet.mc64;
151 }
153 static unsigned long hpet_read(
154 struct vcpu *v, unsigned long addr, unsigned long length)
155 {
156 HPETState *h = &v->domain->arch.hvm_domain.pl_time.vhpet;
157 unsigned long result;
158 uint64_t val;
160 addr &= HPET_MMAP_SIZE-1;
162 if ( hpet_check_access_length(addr, length) != 0 )
163 return ~0UL;
165 spin_lock(&h->lock);
167 val = hpet_read64(h, addr);
168 if ( (addr & ~7) == HPET_COUNTER )
169 val = hpet_read_maincounter(h);
171 result = val;
172 if ( length != 8 )
173 result = (val >> ((addr & 7) * 8)) & ((1ULL << (length * 8)) - 1);
175 spin_unlock(&h->lock);
177 return result;
178 }
180 static void hpet_stop_timer(HPETState *h, unsigned int tn)
181 {
182 ASSERT(tn < HPET_TIMER_NUM);
183 ASSERT(spin_is_locked(&h->lock));
184 stop_timer(&h->timers[tn]);
185 }
187 /* the number of HPET tick that stands for
188 * 1/(2^10) second, namely, 0.9765625 milliseconds */
189 #define HPET_TINY_TIME_SPAN ((h->tsc_freq >> 10) / TSC_PER_HPET_TICK)
191 static void hpet_set_timer(HPETState *h, unsigned int tn)
192 {
193 uint64_t tn_cmp, cur_tick, diff;
195 ASSERT(tn < HPET_TIMER_NUM);
196 ASSERT(spin_is_locked(&h->lock));
198 if ( (tn == 0) && (h->hpet.config & HPET_CFG_LEGACY) )
199 {
200 /* HPET specification requires PIT shouldn't generate
201 * interrupts if LegacyReplacementRoute is set for timer0 */
202 PITState *pit = &h->vcpu->domain->arch.hvm_domain.pl_time.vpit;
203 pit_stop_channel0_irq(pit);
204 }
206 tn_cmp = h->hpet.timers[tn].cmp;
207 cur_tick = hpet_read_maincounter(h);
208 if ( timer_is_32bit(h, tn) )
209 {
210 tn_cmp = (uint32_t)tn_cmp;
211 cur_tick = (uint32_t)cur_tick;
212 }
214 diff = tn_cmp - cur_tick;
216 /*
217 * Detect time values set in the past. This is hard to do for 32-bit
218 * comparators as the timer does not have to be set that far in the future
219 * for the counter difference to wrap a 32-bit signed integer. We fudge
220 * by looking for a 'small' time value in the past.
221 */
222 if ( (int64_t)diff < 0 )
223 diff = (timer_is_32bit(h, tn) && (-diff > HPET_TINY_TIME_SPAN))
224 ? (uint32_t)diff : 0;
226 set_timer(&h->timers[tn], NOW() + hpet_tick_to_ns(h, diff));
227 }
229 static inline uint64_t hpet_fixup_reg(
230 uint64_t new, uint64_t old, uint64_t mask)
231 {
232 new &= mask;
233 new |= old & ~mask;
234 return new;
235 }
237 static void hpet_write(
238 struct vcpu *v, unsigned long addr,
239 unsigned long length, unsigned long val)
240 {
241 HPETState *h = &v->domain->arch.hvm_domain.pl_time.vhpet;
242 uint64_t old_val, new_val;
243 int tn, i;
245 addr &= HPET_MMAP_SIZE-1;
247 if ( hpet_check_access_length(addr, length) != 0 )
248 return;
250 spin_lock(&h->lock);
252 old_val = hpet_read64(h, addr);
253 if ( (addr & ~7) == HPET_COUNTER )
254 old_val = hpet_read_maincounter(h);
256 new_val = val;
257 if ( length != 8 )
258 new_val = hpet_fixup_reg(
259 new_val << (addr & 7) * 8, old_val,
260 ((1ULL << (length*8)) - 1) << ((addr & 7) * 8));
262 switch ( addr & ~7 )
263 {
264 case HPET_CFG:
265 h->hpet.config = hpet_fixup_reg(new_val, old_val, 0x3);
267 if ( !(old_val & HPET_CFG_ENABLE) && (new_val & HPET_CFG_ENABLE) )
268 {
269 /* Enable main counter and interrupt generation. */
270 h->mc_offset = h->hpet.mc64 - guest_time_hpet(h->vcpu);
271 for ( i = 0; i < HPET_TIMER_NUM; i++ )
272 hpet_set_timer(h, i);
273 }
274 else if ( (old_val & HPET_CFG_ENABLE) && !(new_val & HPET_CFG_ENABLE) )
275 {
276 /* Halt main counter and disable interrupt generation. */
277 h->hpet.mc64 = h->mc_offset + guest_time_hpet(h->vcpu);
278 for ( i = 0; i < HPET_TIMER_NUM; i++ )
279 hpet_stop_timer(h, i);
280 }
281 break;
283 case HPET_COUNTER:
284 if ( hpet_enabled(h) )
285 gdprintk(XENLOG_WARNING,
286 "HPET: writing main counter but it's not halted!\n");
287 h->hpet.mc64 = new_val;
288 break;
290 case HPET_T0_CFG:
291 case HPET_T1_CFG:
292 case HPET_T2_CFG:
293 tn = (addr - HPET_T0_CFG) >> 5;
295 h->hpet.timers[tn].config = hpet_fixup_reg(new_val, old_val, 0x3f4e);
297 if ( timer_level(h, tn) )
298 {
299 gdprintk(XENLOG_ERR,
300 "HPET: level triggered interrupt not supported now\n");
301 domain_crash(current->domain);
302 break;
303 }
305 if ( new_val & HPET_TN_32BIT )
306 {
307 h->hpet.timers[tn].cmp = (uint32_t)h->hpet.timers[tn].cmp;
308 h->hpet.period[tn] = (uint32_t)h->hpet.period[tn];
309 }
311 break;
313 case HPET_T0_CMP:
314 case HPET_T1_CMP:
315 case HPET_T2_CMP:
316 tn = (addr - HPET_T0_CMP) >> 5;
317 if ( timer_is_32bit(h, tn) )
318 new_val = (uint32_t)new_val;
319 if ( !timer_is_periodic(h, tn) ||
320 (h->hpet.timers[tn].config & HPET_TN_SETVAL) )
321 h->hpet.timers[tn].cmp = new_val;
322 else
323 {
324 /*
325 * Clamp period to reasonable min/max values:
326 * - minimum is 900us, same as timers controlled by vpt.c
327 * - maximum is to prevent overflow in time_after() calculations
328 */
329 if ( hpet_tick_to_ns(h, new_val) < MICROSECS(900) )
330 new_val = (MICROSECS(900) << 10) / h->hpet_to_ns_scale;
331 new_val &= (timer_is_32bit(h, tn) ? ~0u : ~0ull) >> 1;
332 h->hpet.period[tn] = new_val;
333 }
334 h->hpet.timers[tn].config &= ~HPET_TN_SETVAL;
335 if ( hpet_enabled(h) )
336 hpet_set_timer(h, tn);
337 break;
339 case HPET_T0_ROUTE:
340 case HPET_T1_ROUTE:
341 case HPET_T2_ROUTE:
342 tn = (addr - HPET_T0_ROUTE) >> 5;
343 h->hpet.timers[tn].fsb = new_val;
344 break;
346 default:
347 /* Ignore writes to unsupported and reserved registers. */
348 break;
349 }
351 spin_unlock(&h->lock);
352 }
354 static int hpet_range(struct vcpu *v, unsigned long addr)
355 {
356 return ((addr >= HPET_BASE_ADDRESS) &&
357 (addr < (HPET_BASE_ADDRESS + HPET_MMAP_SIZE)));
358 }
360 struct hvm_mmio_handler hpet_mmio_handler = {
361 .check_handler = hpet_range,
362 .read_handler = hpet_read,
363 .write_handler = hpet_write
364 };
366 static void hpet_route_interrupt(HPETState *h, unsigned int tn)
367 {
368 unsigned int tn_int_route = timer_int_route(h, tn);
369 struct domain *d = h->vcpu->domain;
371 ASSERT(spin_is_locked(&h->lock));
373 if ( (tn <= 1) && (h->hpet.config & HPET_CFG_LEGACY) )
374 {
375 /* if LegacyReplacementRoute bit is set, HPET specification requires
376 timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
377 timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. */
378 int isa_irq = (tn == 0) ? 0 : 8;
379 hvm_isa_irq_deassert(d, isa_irq);
380 hvm_isa_irq_assert(d, isa_irq);
381 return;
382 }
384 if ( !(timer_int_route_cap(h, tn) & (1U << tn_int_route)) )
385 {
386 gdprintk(XENLOG_ERR,
387 "HPET: timer%u: invalid interrupt route config\n", tn);
388 domain_crash(d);
389 return;
390 }
392 /* We support only edge-triggered interrupt. */
393 spin_lock(&d->arch.hvm_domain.irq_lock);
394 vioapic_irq_positive_edge(d, tn_int_route);
395 spin_unlock(&d->arch.hvm_domain.irq_lock);
396 }
398 static void hpet_timer_fn(void *opaque)
399 {
400 struct HPET_timer_fn_info *htfi = opaque;
401 HPETState *h = htfi->hs;
402 unsigned int tn = htfi->tn;
404 spin_lock(&h->lock);
406 if ( !hpet_enabled(h) )
407 {
408 spin_unlock(&h->lock);
409 return;
410 }
412 if ( timer_config(h, tn) & HPET_TN_ENABLE )
413 hpet_route_interrupt(h, tn);
415 if ( timer_is_periodic(h, tn) && (h->hpet.period[tn] != 0) )
416 {
417 uint64_t mc = hpet_read_maincounter(h), period = h->hpet.period[tn];
418 if ( timer_is_32bit(h, tn) )
419 {
420 while ( hpet_time_after(mc, h->hpet.timers[tn].cmp) )
421 h->hpet.timers[tn].cmp = (uint32_t)(
422 h->hpet.timers[tn].cmp + period);
423 }
424 else
425 {
426 while ( hpet_time_after64(mc, h->hpet.timers[tn].cmp) )
427 h->hpet.timers[tn].cmp += period;
428 }
429 set_timer(&h->timers[tn], NOW() + hpet_tick_to_ns(h, period));
430 }
432 spin_unlock(&h->lock);
433 }
435 void hpet_migrate_timers(struct vcpu *v)
436 {
437 struct HPETState *h = &v->domain->arch.hvm_domain.pl_time.vhpet;
438 int i;
440 if ( v != h->vcpu )
441 return;
443 for ( i = 0; i < HPET_TIMER_NUM; i++ )
444 migrate_timer(&h->timers[i], v->processor);
445 }
447 static int hpet_save(struct domain *d, hvm_domain_context_t *h)
448 {
449 HPETState *hp = &d->arch.hvm_domain.pl_time.vhpet;
450 int rc;
452 spin_lock(&hp->lock);
454 /* Write the proper value into the main counter */
455 hp->hpet.mc64 = hp->mc_offset + guest_time_hpet(hp->vcpu);
457 /* Save the HPET registers */
458 rc = _hvm_init_entry(h, HVM_SAVE_CODE(HPET), 0, HVM_SAVE_LENGTH(HPET));
459 if ( rc == 0 )
460 {
461 struct hvm_hw_hpet *rec = (struct hvm_hw_hpet *)&h->data[h->cur];
462 h->cur += HVM_SAVE_LENGTH(HPET);
463 memset(rec, 0, HVM_SAVE_LENGTH(HPET));
464 #define C(x) rec->x = hp->hpet.x
465 C(capability);
466 C(config);
467 C(isr);
468 C(mc64);
469 C(timers[0].config);
470 C(timers[0].cmp);
471 C(timers[0].fsb);
472 C(timers[1].config);
473 C(timers[1].cmp);
474 C(timers[1].fsb);
475 C(timers[2].config);
476 C(timers[2].cmp);
477 C(timers[2].fsb);
478 C(period[0]);
479 C(period[1]);
480 C(period[2]);
481 #undef C
482 }
484 spin_unlock(&hp->lock);
486 return rc;
487 }
489 static int hpet_load(struct domain *d, hvm_domain_context_t *h)
490 {
491 HPETState *hp = &d->arch.hvm_domain.pl_time.vhpet;
492 struct hvm_hw_hpet *rec;
493 int i;
495 spin_lock(&hp->lock);
497 /* Reload the HPET registers */
498 if ( _hvm_check_entry(h, HVM_SAVE_CODE(HPET), HVM_SAVE_LENGTH(HPET)) )
499 {
500 spin_unlock(&hp->lock);
501 return -EINVAL;
502 }
504 rec = (struct hvm_hw_hpet *)&h->data[h->cur];
505 h->cur += HVM_SAVE_LENGTH(HPET);
507 #define C(x) hp->hpet.x = rec->x
508 C(capability);
509 C(config);
510 C(isr);
511 C(mc64);
512 C(timers[0].config);
513 C(timers[0].cmp);
514 C(timers[0].fsb);
515 C(timers[1].config);
516 C(timers[1].cmp);
517 C(timers[1].fsb);
518 C(timers[2].config);
519 C(timers[2].cmp);
520 C(timers[2].fsb);
521 C(period[0]);
522 C(period[1]);
523 C(period[2]);
524 #undef C
526 /* Recalculate the offset between the main counter and guest time */
527 hp->mc_offset = hp->hpet.mc64 - guest_time_hpet(hp->vcpu);
529 /* Restart the timers */
530 for ( i = 0; i < HPET_TIMER_NUM; i++ )
531 if ( hpet_enabled(hp) )
532 hpet_set_timer(hp, i);
534 spin_unlock(&hp->lock);
536 return 0;
537 }
539 HVM_REGISTER_SAVE_RESTORE(HPET, hpet_save, hpet_load, 1, HVMSR_PER_DOM);
541 void hpet_init(struct vcpu *v)
542 {
543 HPETState *h = &v->domain->arch.hvm_domain.pl_time.vhpet;
544 int i;
546 memset(h, 0, sizeof(HPETState));
548 spin_lock_init(&h->lock);
550 h->vcpu = v;
551 h->tsc_freq = ticks_per_sec(v);
553 h->hpet_to_ns_scale = ((S_TO_NS * TSC_PER_HPET_TICK) << 10) / h->tsc_freq;
554 h->hpet_to_ns_limit = ~0ULL / h->hpet_to_ns_scale;
556 /* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */
557 h->hpet.capability = 0x8086A201ULL;
559 /* This is the number of femptoseconds per HPET tick. */
560 /* Here we define HPET's frequency to be 1/32 of the TSC's */
561 h->hpet.capability |= ((S_TO_FS*TSC_PER_HPET_TICK/h->tsc_freq) << 32);
563 for ( i = 0; i < HPET_TIMER_NUM; i++ )
564 {
565 h->hpet.timers[i].config =
566 HPET_TN_INT_ROUTE_CAP | HPET_TN_SIZE_CAP | HPET_TN_PERIODIC_CAP;
567 h->hpet.timers[i].cmp = ~0ULL;
568 h->timer_fn_info[i].hs = h;
569 h->timer_fn_info[i].tn = i;
570 init_timer(&h->timers[i], hpet_timer_fn, &h->timer_fn_info[i],
571 v->processor);
572 }
573 }
575 void hpet_deinit(struct domain *d)
576 {
577 int i;
578 HPETState *h = &d->arch.hvm_domain.pl_time.vhpet;
580 for ( i = 0; i < HPET_TIMER_NUM; i++ )
581 kill_timer(&h->timers[i]);
582 }