source: trunk/packages/xen-3.1/xen-3.1/xen/arch/x86/hvm/pmtimer.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 8.0 KB
Line 
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
25/* Slightly more readable port I/O addresses for the registers we intercept */
26#define PM1a_STS_ADDR (ACPI_PM1A_EVT_BLK_ADDRESS)
27#define PM1a_EN_ADDR  (ACPI_PM1A_EVT_BLK_ADDRESS + 2)
28#define TMR_VAL_ADDR  (ACPI_PM_TMR_BLK_ADDRESS)
29
30/* The interesting bits of the PM1a_STS register */
31#define TMR_STS    (1 << 0)
32#define PWRBTN_STS (1 << 5)
33#define GBL_STS    (1 << 8)
34
35/* The same in PM1a_EN */
36#define TMR_EN     (1 << 0)
37#define PWRBTN_EN  (1 << 5)
38#define GBL_EN     (1 << 8)
39
40/* Mask of bits in PM1a_STS that can generate an SCI.  Although the ACPI
41 * spec lists other bits, the PIIX4, which we are emulating, only
42 * supports these three.  For now, we only use TMR_STS; in future we
43 * will let qemu set the other bits */
44#define SCI_MASK (TMR_STS|PWRBTN_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
54/* Dispatch SCIs based on the PM1a_STS and PM1a_EN registers */
55static void pmt_update_sci(PMTState *s)
56{
57    if ( s->pm.pm1a_en & s->pm.pm1a_sts & SCI_MASK )
58        hvm_isa_irq_assert(s->vcpu->domain, SCI_IRQ);
59    else
60        hvm_isa_irq_deassert(s->vcpu->domain, SCI_IRQ);
61}
62
63/* Set the correct value in the timer, accounting for time elapsed
64 * since the last time we did that. */
65static void pmt_update_time(PMTState *s)
66{
67    uint64_t curr_gtime;
68    uint32_t msb = s->pm.tmr_val & TMR_VAL_MSB;
69   
70    /* Update the timer */
71    curr_gtime = hvm_get_guest_time(s->vcpu);
72    s->pm.tmr_val += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
73    s->pm.tmr_val &= TMR_VAL_MASK;
74    s->last_gtime = curr_gtime;
75   
76    /* If the counter's MSB has changed, set the status bit */
77    if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
78    {
79        s->pm.pm1a_sts |= TMR_STS;
80        pmt_update_sci(s);
81    }
82}
83
84/* This function should be called soon after each time the MSB of the
85 * pmtimer register rolls over, to make sure we update the status
86 * registers and SCI at least once per rollover */
87static void pmt_timer_callback(void *opaque)
88{
89    PMTState *s = opaque;
90    uint32_t pmt_cycles_until_flip;
91    uint64_t time_until_flip;
92   
93    /* Recalculate the timer and make sure we get an SCI if we need one */
94    pmt_update_time(s);
95   
96    /* How close are we to the next MSB flip? */
97    pmt_cycles_until_flip = TMR_VAL_MSB - (s->pm.tmr_val & (TMR_VAL_MSB - 1));
98   
99    /* Overall time between MSB flips */
100    time_until_flip = (1000000000ULL << 31) / FREQUENCE_PMTIMER;
101   
102    /* Reduced appropriately */
103    time_until_flip = (time_until_flip * pmt_cycles_until_flip) / (1ULL<<31);
104   
105    /* Wake up again near the next bit-flip */
106    set_timer(&s->timer, NOW() + time_until_flip + MILLISECS(1));
107}
108
109
110/* Handle port I/O to the PM1a_STS and PM1a_EN registers */
111static int handle_evt_io(ioreq_t *p)
112{
113    struct vcpu *v = current;
114    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
115    uint32_t addr, data, byte;
116    int i;
117
118    if ( p->dir == 0 ) /* Write */
119    {
120        /* Handle this I/O one byte at a time */
121        for ( i = p->size, addr = p->addr, data = p->data;
122              i > 0;
123              i--, addr++, data >>= 8 )
124        {
125            byte = data & 0xff;
126            switch(addr) 
127            {
128                /* PM1a_STS register bits are write-to-clear */
129            case PM1a_STS_ADDR:
130                s->pm.pm1a_sts &= ~byte;
131                break;
132            case PM1a_STS_ADDR + 1:
133                s->pm.pm1a_sts &= ~(byte << 8);
134                break;
135               
136            case PM1a_EN_ADDR:
137                s->pm.pm1a_en = (s->pm.pm1a_en & 0xff00) | byte;
138                break;
139            case PM1a_EN_ADDR + 1:
140                s->pm.pm1a_en = (s->pm.pm1a_en & 0xff) | (byte << 8);
141                break;
142               
143            default:
144                gdprintk(XENLOG_WARNING, 
145                         "Bad ACPI PM register write: %"PRIu64
146                         " bytes (%#"PRIx64") at %"PRIx64"\n", 
147                         p->size, p->data, p->addr);
148            }
149        }
150        /* Fix up the SCI state to match the new register state */
151        pmt_update_sci(s);
152    }
153    else /* Read */
154    {
155        data = s->pm.pm1a_sts | (((uint32_t) s->pm.pm1a_en) << 16);
156        data >>= 8 * (p->addr - PM1a_STS_ADDR);
157        if ( p->size == 1 ) data &= 0xff;
158        else if ( p->size == 2 ) data &= 0xffff;
159        p->data = data;
160    }
161    return 1;
162}
163
164
165/* Handle port I/O to the TMR_VAL register */
166static int handle_pmt_io(ioreq_t *p)
167{
168    struct vcpu *v = current;
169    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
170
171    if (p->size != 4 ||
172        p->data_is_ptr ||
173        p->type != IOREQ_TYPE_PIO){
174        printk("HVM_PMT: wrong PM timer IO\n");
175        return 1;
176    }
177   
178    if (p->dir == 0) { /* write */
179        /* PM_TMR_BLK is read-only */
180        return 1;
181    } else if (p->dir == 1) { /* read */
182        pmt_update_time(s);
183        p->data = s->pm.tmr_val;
184        return 1;
185    }
186    return 0;
187}
188
189static int pmtimer_save(struct domain *d, hvm_domain_context_t *h)
190{
191    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
192    uint32_t msb = s->pm.tmr_val & TMR_VAL_MSB;
193    uint32_t x;
194
195    /* Update the counter to the guest's current time.  We always save
196     * with the domain paused, so the saved time should be after the
197     * last_gtime, but just in case, make sure we only go forwards */
198    x = ((s->vcpu->arch.hvm_vcpu.guest_time - s->last_gtime) * s->scale) >> 32;
199    if ( x < 1UL<<31 )
200        s->pm.tmr_val += x;
201    if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
202        s->pm.pm1a_sts |= TMR_STS;
203    /* No point in setting the SCI here because we'll already have saved the
204     * IRQ and *PIC state; we'll fix it up when we restore the domain */
205
206    return hvm_save_entry(PMTIMER, 0, h, &s->pm);
207}
208
209static int pmtimer_load(struct domain *d, hvm_domain_context_t *h)
210{
211    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
212
213    /* Reload the registers */
214    if ( hvm_load_entry(PMTIMER, h, &s->pm) )
215        return -EINVAL;
216
217    /* Calculate future counter values from now. */
218    s->last_gtime = hvm_get_guest_time(s->vcpu);
219
220    /* Set the SCI state from the registers */ 
221    pmt_update_sci(s);
222   
223    return 0;
224}
225
226HVM_REGISTER_SAVE_RESTORE(PMTIMER, pmtimer_save, pmtimer_load, 
227                          1, HVMSR_PER_DOM);
228
229
230void pmtimer_init(struct vcpu *v)
231{
232    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
233
234    s->pm.tmr_val = 0;
235    s->pm.pm1a_sts = 0;
236    s->pm.pm1a_en = 0;
237
238    s->scale = ((uint64_t)FREQUENCE_PMTIMER << 32) / ticks_per_sec(v);
239    s->vcpu = v;
240
241    /* Intercept port I/O (need two handlers because PM1a_CNT is between
242     * PM1a_EN and TMR_VAL and is handled by qemu) */
243    register_portio_handler(v->domain, TMR_VAL_ADDR, 4, handle_pmt_io);
244    register_portio_handler(v->domain, PM1a_STS_ADDR, 4, handle_evt_io);
245
246    /* Set up callback to fire SCIs when the MSB of TMR_VAL changes */
247    init_timer(&s->timer, pmt_timer_callback, s, v->processor);
248    pmt_timer_callback(s);
249}
250
251
252void pmtimer_deinit(struct domain *d)
253{
254    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
255    kill_timer(&s->timer);
256}
Note: See TracBrowser for help on using the repository browser.