Coverage Report

Created: 2017-10-25 09:10

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