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