ia64/xen-unstable

view xen/arch/x86/hvm/pmtimer.c @ 15362:b4c16658ca30

hvm: Fix multiplication overflow in hvm/pmtimer.c

Too many ACPI events (SCI) are raised on hvm because of
multiplication overflow.

FREQUENCE_PMTIMER=3579545
(1000000000ULL << 32) / FREQUENCE_PMTIMER = 0xae9a7b1663a
pmt_cycles_until_flip =~ 0x80000000
0xae9a7b1663a*0x80000000 = overflow!!!

Signed-off-by: Kouya Shimura <kouya@jp.fujitsu.com>
author Keir Fraser <keir@xensource.com>
date Fri Jun 15 09:42:39 2007 +0100 (2007-06-15)
parents 7ba1e697beda
children 8ad38aaaeb89
line source
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 */
21 #include <asm/hvm/vpt.h>
22 #include <asm/hvm/io.h>
23 #include <asm/hvm/support.h>
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)
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)
35 /* The same in PM1a_EN */
36 #define TMR_EN (1 << 0)
37 #define PWRBTN_EN (1 << 5)
38 #define GBL_EN (1 << 8)
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)
46 /* SCI IRQ number (must match SCI_INT number in ACPI FADT in hvmloader) */
47 #define SCI_IRQ 9
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)
53 /* Dispatch SCIs based on the PM1a_STS and PM1a_EN registers */
54 static void pmt_update_sci(PMTState *s)
55 {
56 if ( s->pm.pm1a_en & s->pm.pm1a_sts & SCI_MASK )
57 hvm_isa_irq_assert(s->vcpu->domain, SCI_IRQ);
58 else
59 hvm_isa_irq_deassert(s->vcpu->domain, SCI_IRQ);
60 }
62 /* Set the correct value in the timer, accounting for time elapsed
63 * since the last time we did that. */
64 static void pmt_update_time(PMTState *s)
65 {
66 uint64_t curr_gtime;
67 uint32_t msb = s->pm.tmr_val & TMR_VAL_MSB;
69 /* Update the timer */
70 curr_gtime = hvm_get_guest_time(s->vcpu);
71 s->pm.tmr_val += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
72 s->pm.tmr_val &= TMR_VAL_MASK;
73 s->last_gtime = curr_gtime;
75 /* If the counter's MSB has changed, set the status bit */
76 if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
77 {
78 s->pm.pm1a_sts |= TMR_STS;
79 pmt_update_sci(s);
80 }
81 }
83 /* This function should be called soon after each time the MSB of the
84 * pmtimer register rolls over, to make sure we update the status
85 * registers and SCI at least once per rollover */
86 static void pmt_timer_callback(void *opaque)
87 {
88 PMTState *s = opaque;
89 uint32_t pmt_cycles_until_flip;
90 uint64_t time_until_flip;
92 /* Recalculate the timer and make sure we get an SCI if we need one */
93 pmt_update_time(s);
95 /* How close are we to the next MSB flip? */
96 pmt_cycles_until_flip = TMR_VAL_MSB - (s->pm.tmr_val & (TMR_VAL_MSB - 1));
98 /* Overall time between MSB flips */
99 time_until_flip = (1000000000ULL << 23) / FREQUENCE_PMTIMER;
101 /* Reduced appropriately */
102 time_until_flip = (time_until_flip * pmt_cycles_until_flip) >> 23;
104 /* Wake up again near the next bit-flip */
105 set_timer(&s->timer, NOW() + time_until_flip + MILLISECS(1));
106 }
109 /* Handle port I/O to the PM1a_STS and PM1a_EN registers */
110 static int handle_evt_io(ioreq_t *p)
111 {
112 struct vcpu *v = current;
113 PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
114 uint32_t addr, data, byte;
115 int i;
117 if ( p->dir == 0 ) /* Write */
118 {
119 /* Handle this I/O one byte at a time */
120 for ( i = p->size, addr = p->addr, data = p->data;
121 i > 0;
122 i--, addr++, data >>= 8 )
123 {
124 byte = data & 0xff;
125 switch(addr)
126 {
127 /* PM1a_STS register bits are write-to-clear */
128 case PM1a_STS_ADDR:
129 s->pm.pm1a_sts &= ~byte;
130 break;
131 case PM1a_STS_ADDR + 1:
132 s->pm.pm1a_sts &= ~(byte << 8);
133 break;
135 case PM1a_EN_ADDR:
136 s->pm.pm1a_en = (s->pm.pm1a_en & 0xff00) | byte;
137 break;
138 case PM1a_EN_ADDR + 1:
139 s->pm.pm1a_en = (s->pm.pm1a_en & 0xff) | (byte << 8);
140 break;
142 default:
143 gdprintk(XENLOG_WARNING,
144 "Bad ACPI PM register write: %"PRIu64
145 " bytes (%#"PRIx64") at %"PRIx64"\n",
146 p->size, p->data, p->addr);
147 }
148 }
149 /* Fix up the SCI state to match the new register state */
150 pmt_update_sci(s);
151 }
152 else /* Read */
153 {
154 data = s->pm.pm1a_sts | (((uint32_t) s->pm.pm1a_en) << 16);
155 data >>= 8 * (p->addr - PM1a_STS_ADDR);
156 if ( p->size == 1 ) data &= 0xff;
157 else if ( p->size == 2 ) data &= 0xffff;
158 p->data = data;
159 }
160 return 1;
161 }
164 /* Handle port I/O to the TMR_VAL register */
165 static int handle_pmt_io(ioreq_t *p)
166 {
167 struct vcpu *v = current;
168 PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
170 if (p->size != 4 ||
171 p->data_is_ptr ||
172 p->type != IOREQ_TYPE_PIO){
173 printk("HVM_PMT: wrong PM timer IO\n");
174 return 1;
175 }
177 if (p->dir == 0) { /* write */
178 /* PM_TMR_BLK is read-only */
179 return 1;
180 } else if (p->dir == 1) { /* read */
181 pmt_update_time(s);
182 p->data = s->pm.tmr_val;
183 return 1;
184 }
185 return 0;
186 }
188 static int pmtimer_save(struct domain *d, hvm_domain_context_t *h)
189 {
190 PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
191 uint32_t msb = s->pm.tmr_val & TMR_VAL_MSB;
192 uint32_t x;
194 /* Update the counter to the guest's current time. We always save
195 * with the domain paused, so the saved time should be after the
196 * last_gtime, but just in case, make sure we only go forwards */
197 x = ((s->vcpu->arch.hvm_vcpu.guest_time - s->last_gtime) * s->scale) >> 32;
198 if ( x < 1UL<<31 )
199 s->pm.tmr_val += x;
200 if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
201 s->pm.pm1a_sts |= TMR_STS;
202 /* No point in setting the SCI here because we'll already have saved the
203 * IRQ and *PIC state; we'll fix it up when we restore the domain */
205 return hvm_save_entry(PMTIMER, 0, h, &s->pm);
206 }
208 static int pmtimer_load(struct domain *d, hvm_domain_context_t *h)
209 {
210 PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
212 /* Reload the registers */
213 if ( hvm_load_entry(PMTIMER, h, &s->pm) )
214 return -EINVAL;
216 /* Calculate future counter values from now. */
217 s->last_gtime = hvm_get_guest_time(s->vcpu);
219 /* Set the SCI state from the registers */
220 pmt_update_sci(s);
222 return 0;
223 }
225 HVM_REGISTER_SAVE_RESTORE(PMTIMER, pmtimer_save, pmtimer_load,
226 1, HVMSR_PER_DOM);
229 void pmtimer_init(struct vcpu *v)
230 {
231 PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
233 s->pm.tmr_val = 0;
234 s->pm.pm1a_sts = 0;
235 s->pm.pm1a_en = 0;
237 s->scale = ((uint64_t)FREQUENCE_PMTIMER << 32) / ticks_per_sec(v);
238 s->vcpu = v;
240 /* Intercept port I/O (need two handlers because PM1a_CNT is between
241 * PM1a_EN and TMR_VAL and is handled by qemu) */
242 register_portio_handler(v->domain, TMR_VAL_ADDR, 4, handle_pmt_io);
243 register_portio_handler(v->domain, PM1a_STS_ADDR, 4, handle_evt_io);
245 /* Set up callback to fire SCIs when the MSB of TMR_VAL changes */
246 init_timer(&s->timer, pmt_timer_callback, s, v->processor);
247 pmt_timer_callback(s);
248 }
251 void pmtimer_deinit(struct domain *d)
252 {
253 PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
254 kill_timer(&s->timer);
255 }