~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to xen/arch/x86/hvm/pmtimer.c

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * hvm/pmtimer.c: emulation of the ACPI PM timer 
 
3
 *
 
4
 * Copyright (c) 2007, XenSource inc.
 
5
 * Copyright (c) 2006, Intel Corporation.
 
6
 *
 
7
 * This program is free software; you can redistribute it and/or modify it
 
8
 * under the terms and conditions of the GNU General Public License,
 
9
 * version 2, as published by the Free Software Foundation.
 
10
 *
 
11
 * This program is distributed in the hope it will be useful, but WITHOUT
 
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 
14
 * more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License along with
 
17
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
18
 * Place - Suite 330, Boston, MA 02111-1307 USA.
 
19
 */
 
20
 
 
21
#include <asm/hvm/vpt.h>
 
22
#include <asm/hvm/io.h>
 
23
#include <asm/hvm/support.h>
 
24
#include <asm/acpi.h> /* for hvm_acpi_power_button prototype */
 
25
 
 
26
/* Slightly more readable port I/O addresses for the registers we intercept */
 
27
#define PM1a_STS_ADDR (ACPI_PM1A_EVT_BLK_ADDRESS)
 
28
#define PM1a_EN_ADDR  (ACPI_PM1A_EVT_BLK_ADDRESS + 2)
 
29
#define TMR_VAL_ADDR  (ACPI_PM_TMR_BLK_ADDRESS)
 
30
 
 
31
/* The interesting bits of the PM1a_STS register */
 
32
#define TMR_STS    (1 << 0)
 
33
#define GBL_STS    (1 << 5)
 
34
#define PWRBTN_STS (1 << 8)
 
35
#define SLPBTN_STS (1 << 9)
 
36
 
 
37
/* The same in PM1a_EN */
 
38
#define TMR_EN     (1 << 0)
 
39
#define GBL_EN     (1 << 5)
 
40
#define PWRBTN_EN  (1 << 8)
 
41
#define SLPBTN_EN  (1 << 9)
 
42
 
 
43
/* Mask of bits in PM1a_STS that can generate an SCI. */
 
44
#define SCI_MASK (TMR_STS|PWRBTN_STS|SLPBTN_STS|GBL_STS) 
 
45
 
 
46
/* SCI IRQ number (must match SCI_INT number in ACPI FADT in hvmloader) */
 
47
#define SCI_IRQ 9
 
48
 
 
49
/* We provide a 32-bit counter (must match the TMR_VAL_EXT bit in the FADT) */
 
50
#define TMR_VAL_MASK  (0xffffffff)
 
51
#define TMR_VAL_MSB   (0x80000000)
 
52
 
 
53
/* Dispatch SCIs based on the PM1a_STS and PM1a_EN registers */
 
54
static void pmt_update_sci(PMTState *s)
 
55
{
 
56
    ASSERT(spin_is_locked(&s->lock));
 
57
 
 
58
    if ( s->pm.pm1a_en & s->pm.pm1a_sts & SCI_MASK )
 
59
        hvm_isa_irq_assert(s->vcpu->domain, SCI_IRQ);
 
60
    else
 
61
        hvm_isa_irq_deassert(s->vcpu->domain, SCI_IRQ);
 
62
}
 
63
 
 
64
void hvm_acpi_power_button(struct domain *d)
 
65
{
 
66
    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
 
67
    spin_lock(&s->lock);
 
68
    s->pm.pm1a_sts |= PWRBTN_STS;
 
69
    pmt_update_sci(s);
 
70
    spin_unlock(&s->lock);
 
71
}
 
72
 
 
73
void hvm_acpi_sleep_button(struct domain *d)
 
74
{
 
75
    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
 
76
    spin_lock(&s->lock);
 
77
    s->pm.pm1a_sts |= SLPBTN_STS;
 
78
    pmt_update_sci(s);
 
79
    spin_unlock(&s->lock);
 
80
}
 
81
 
 
82
/* Set the correct value in the timer, accounting for time elapsed
 
83
 * since the last time we did that. */
 
84
static void pmt_update_time(PMTState *s)
 
85
{
 
86
    uint64_t curr_gtime;
 
87
    uint32_t msb = s->pm.tmr_val & TMR_VAL_MSB;
 
88
    
 
89
    ASSERT(spin_is_locked(&s->lock));
 
90
 
 
91
    /* Update the timer */
 
92
    curr_gtime = hvm_get_guest_time(s->vcpu);
 
93
    s->pm.tmr_val += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
 
94
    s->pm.tmr_val &= TMR_VAL_MASK;
 
95
    s->last_gtime = curr_gtime;
 
96
    
 
97
    /* If the counter's MSB has changed, set the status bit */
 
98
    if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
 
99
    {
 
100
        s->pm.pm1a_sts |= TMR_STS;
 
101
        pmt_update_sci(s);
 
102
    }
 
103
}
 
104
 
 
105
/* This function should be called soon after each time the MSB of the
 
106
 * pmtimer register rolls over, to make sure we update the status
 
107
 * registers and SCI at least once per rollover */
 
108
static void pmt_timer_callback(void *opaque)
 
109
{
 
110
    PMTState *s = opaque;
 
111
    uint32_t pmt_cycles_until_flip;
 
112
    uint64_t time_until_flip;
 
113
 
 
114
    spin_lock(&s->lock);
 
115
 
 
116
    /* Recalculate the timer and make sure we get an SCI if we need one */
 
117
    pmt_update_time(s);
 
118
 
 
119
    /* How close are we to the next MSB flip? */
 
120
    pmt_cycles_until_flip = TMR_VAL_MSB - (s->pm.tmr_val & (TMR_VAL_MSB - 1));
 
121
 
 
122
    /* Overall time between MSB flips */
 
123
    time_until_flip = (1000000000ULL << 23) / FREQUENCE_PMTIMER;
 
124
 
 
125
    /* Reduced appropriately */
 
126
    time_until_flip = (time_until_flip * pmt_cycles_until_flip) >> 23;
 
127
 
 
128
    /* Wake up again near the next bit-flip */
 
129
    set_timer(&s->timer, NOW() + time_until_flip + MILLISECS(1));
 
130
 
 
131
    spin_unlock(&s->lock);
 
132
}
 
133
 
 
134
/* Handle port I/O to the PM1a_STS and PM1a_EN registers */
 
135
static int handle_evt_io(
 
136
    int dir, uint32_t port, uint32_t bytes, uint32_t *val)
 
137
{
 
138
    struct vcpu *v = current;
 
139
    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
 
140
    uint32_t addr, data, byte;
 
141
    int i;
 
142
 
 
143
    spin_lock(&s->lock);
 
144
 
 
145
    if ( dir == IOREQ_WRITE )
 
146
    {
 
147
        /* Handle this I/O one byte at a time */
 
148
        for ( i = bytes, addr = port, data = *val;
 
149
              i > 0;
 
150
              i--, addr++, data >>= 8 )
 
151
        {
 
152
            byte = data & 0xff;
 
153
            switch ( addr )
 
154
            {
 
155
                /* PM1a_STS register bits are write-to-clear */
 
156
            case PM1a_STS_ADDR:
 
157
                s->pm.pm1a_sts &= ~byte;
 
158
                break;
 
159
            case PM1a_STS_ADDR + 1:
 
160
                s->pm.pm1a_sts &= ~(byte << 8);
 
161
                break;
 
162
                
 
163
            case PM1a_EN_ADDR:
 
164
                s->pm.pm1a_en = (s->pm.pm1a_en & 0xff00) | byte;
 
165
                break;
 
166
            case PM1a_EN_ADDR + 1:
 
167
                s->pm.pm1a_en = (s->pm.pm1a_en & 0xff) | (byte << 8);
 
168
                break;
 
169
                
 
170
            default:
 
171
                gdprintk(XENLOG_WARNING, 
 
172
                         "Bad ACPI PM register write: %x bytes (%x) at %x\n", 
 
173
                         bytes, *val, port);
 
174
            }
 
175
        }
 
176
        /* Fix up the SCI state to match the new register state */
 
177
        pmt_update_sci(s);
 
178
    }
 
179
    else /* p->dir == IOREQ_READ */
 
180
    {
 
181
        data = s->pm.pm1a_sts | (((uint32_t) s->pm.pm1a_en) << 16);
 
182
        data >>= 8 * (port - PM1a_STS_ADDR);
 
183
        if ( bytes == 1 ) data &= 0xff;
 
184
        else if ( bytes == 2 ) data &= 0xffff;
 
185
        *val = data;
 
186
    }
 
187
 
 
188
    spin_unlock(&s->lock);
 
189
 
 
190
    return X86EMUL_OKAY;
 
191
}
 
192
 
 
193
 
 
194
/* Handle port I/O to the TMR_VAL register */
 
195
static int handle_pmt_io(
 
196
    int dir, uint32_t port, uint32_t bytes, uint32_t *val)
 
197
{
 
198
    struct vcpu *v = current;
 
199
    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
 
200
 
 
201
    if ( bytes != 4 )
 
202
    {
 
203
        gdprintk(XENLOG_WARNING, "HVM_PMT bad access\n");
 
204
        return X86EMUL_OKAY;
 
205
    }
 
206
    
 
207
    if ( dir == IOREQ_READ )
 
208
    {
 
209
        spin_lock(&s->lock);
 
210
        pmt_update_time(s);
 
211
        *val = s->pm.tmr_val;
 
212
        spin_unlock(&s->lock);
 
213
        return X86EMUL_OKAY;
 
214
    }
 
215
 
 
216
    return X86EMUL_UNHANDLEABLE;
 
217
}
 
218
 
 
219
static int pmtimer_save(struct domain *d, hvm_domain_context_t *h)
 
220
{
 
221
    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
 
222
    uint32_t x, msb = s->pm.tmr_val & TMR_VAL_MSB;
 
223
    int rc;
 
224
 
 
225
    spin_lock(&s->lock);
 
226
 
 
227
    /* Update the counter to the guest's current time.  We always save
 
228
     * with the domain paused, so the saved time should be after the
 
229
     * last_gtime, but just in case, make sure we only go forwards */
 
230
    x = ((s->vcpu->arch.hvm_vcpu.guest_time - s->last_gtime) * s->scale) >> 32;
 
231
    if ( x < 1UL<<31 )
 
232
        s->pm.tmr_val += x;
 
233
    if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
 
234
        s->pm.pm1a_sts |= TMR_STS;
 
235
    /* No point in setting the SCI here because we'll already have saved the 
 
236
     * IRQ and *PIC state; we'll fix it up when we restore the domain */
 
237
 
 
238
    rc = hvm_save_entry(PMTIMER, 0, h, &s->pm);
 
239
 
 
240
    spin_unlock(&s->lock);
 
241
 
 
242
    return rc;
 
243
}
 
244
 
 
245
static int pmtimer_load(struct domain *d, hvm_domain_context_t *h)
 
246
{
 
247
    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
 
248
 
 
249
    spin_lock(&s->lock);
 
250
 
 
251
    /* Reload the registers */
 
252
    if ( hvm_load_entry(PMTIMER, h, &s->pm) )
 
253
    {
 
254
        spin_unlock(&s->lock);
 
255
        return -EINVAL;
 
256
    }
 
257
 
 
258
    /* Calculate future counter values from now. */
 
259
    s->last_gtime = hvm_get_guest_time(s->vcpu);
 
260
 
 
261
    /* Set the SCI state from the registers */ 
 
262
    pmt_update_sci(s);
 
263
 
 
264
    spin_unlock(&s->lock);
 
265
    
 
266
    return 0;
 
267
}
 
268
 
 
269
HVM_REGISTER_SAVE_RESTORE(PMTIMER, pmtimer_save, pmtimer_load, 
 
270
                          1, HVMSR_PER_DOM);
 
271
 
 
272
void pmtimer_init(struct vcpu *v)
 
273
{
 
274
    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
 
275
 
 
276
    spin_lock_init(&s->lock);
 
277
 
 
278
    s->scale = ((uint64_t)FREQUENCE_PMTIMER << 32) / SYSTEM_TIME_HZ;
 
279
    s->vcpu = v;
 
280
 
 
281
    /* Intercept port I/O (need two handlers because PM1a_CNT is between
 
282
     * PM1a_EN and TMR_VAL and is handled by qemu) */
 
283
    register_portio_handler(v->domain, TMR_VAL_ADDR, 4, handle_pmt_io);
 
284
    register_portio_handler(v->domain, PM1a_STS_ADDR, 4, handle_evt_io);
 
285
 
 
286
    /* Set up callback to fire SCIs when the MSB of TMR_VAL changes */
 
287
    init_timer(&s->timer, pmt_timer_callback, s, v->processor);
 
288
    pmt_timer_callback(s);
 
289
}
 
290
 
 
291
 
 
292
void pmtimer_deinit(struct domain *d)
 
293
{
 
294
    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
 
295
    kill_timer(&s->timer);
 
296
}
 
297
 
 
298
void pmtimer_reset(struct domain *d)
 
299
{
 
300
    /* Reset the counter. */
 
301
    d->arch.hvm_domain.pl_time.vpmt.pm.tmr_val = 0;
 
302
}