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