ia64/xen-unstable

view xen/arch/ia64/vmx/vacpi.c @ 16171:e7d7a4adf357

[IA64] vti domain save/restore: implement hvm_save/load. work in progress.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
author Alex Williamson <alex.williamson@hp.com>
date Sun Oct 21 14:39:07 2007 -0600 (2007-10-21)
parents b658296982ee
children 428679ca60d8
line source
1 /*
2 * vacpi.c: emulation of the ACPI
3 * based on x86 hvm/pmtimer.c
4 *
5 * Copyright (c) 2007, FUJITSU LIMITED
6 * Kouya Shimura <kouya at jp fujitsu com>
7 * Copyright (c) 2007 VA Linux Systems Japan K.K
8 * Isaku Yamahata <yamahata at valinux co jp>
9 * SMP support
10 * save/restore support
11 *
12 * Copyright (c) 2007, XenSource inc.
13 * Copyright (c) 2006, Intel Corporation.
14 *
15 * This program is free software; you can redistribute it and/or modify it
16 * under the terms and conditions of the GNU General Public License,
17 * version 2, as published by the Free Software Foundation.
18 *
19 * This program is distributed in the hope it will be useful, but WITHOUT
20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22 * more details.
23 *
24 * You should have received a copy of the GNU General Public License along with
25 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
26 * Place - Suite 330, Boston, MA 02111-1307 USA.
27 */
29 #include <asm/vmx_vcpu.h>
30 #include <asm/vmx.h>
31 #include <asm/hvm/vacpi.h>
32 #include <asm/hvm/support.h>
33 #include <public/hvm/save.h>
35 /* The interesting bits of the PM1a_STS register */
36 #define TMR_STS (1 << 0)
37 #define PWRBTN_STS (1 << 5)
38 #define GBL_STS (1 << 8)
40 /* The same in PM1a_EN */
41 #define TMR_EN (1 << 0)
42 #define PWRBTN_EN (1 << 5)
43 #define GBL_EN (1 << 8)
45 /* Mask of bits in PM1a_STS that can generate an SCI. Although the ACPI
46 * spec lists other bits, the PIIX4, which we are emulating, only
47 * supports these three. For now, we only use TMR_STS; in future we
48 * will let qemu set the other bits */
49 #define SCI_MASK (TMR_STS|PWRBTN_STS|GBL_STS)
51 /* SCI IRQ number (must match SCI_INT number in ACPI FADT in hvmloader) */
52 #define SCI_IRQ 9
54 /* We provide a 32-bit counter (must match the TMR_VAL_EXT bit in the FADT) */
55 #define TMR_VAL_MASK (0xffffffff)
56 #define TMR_VAL_MSB (0x80000000)
58 /*
59 * Locking order: vacpi->lock => viosapic->lock
60 * pmt_update_sci() => viosapic_set_irq() => viosapic->lock
61 */
62 /* Dispatch SCIs based on the PM1a_STS and PM1a_EN registers */
63 static void pmt_update_sci(struct domain *d, struct vacpi *s)
64 {
65 ASSERT(spin_is_locked(&s->lock));
66 if (s->regs.pm1a_en & s->regs.pm1a_sts & SCI_MASK)
67 viosapic_set_irq(d, SCI_IRQ, 1); /* Assert */
68 else
69 viosapic_set_irq(d, SCI_IRQ, 0);
70 }
72 /* Set the correct value in the timer, accounting for time elapsed
73 * since the last time we did that. */
74 static void pmt_update_time(struct domain *d)
75 {
76 struct vacpi *s = &d->arch.hvm_domain.vacpi;
77 s_time_t curr_gtime;
78 unsigned long delta;
79 uint32_t msb = s->regs.tmr_val & TMR_VAL_MSB;
81 ASSERT(spin_is_locked(&s->lock));
83 /* Update the timer */
84 curr_gtime = NOW();
85 delta = curr_gtime - s->last_gtime;
86 delta = ((delta >> 8) * ((FREQUENCE_PMTIMER << 32) / SECONDS(1))) >> 24;
87 s->regs.tmr_val += delta;
88 s->regs.tmr_val &= TMR_VAL_MASK;
89 s->last_gtime = curr_gtime;
91 /* If the counter's MSB has changed, set the status bit */
92 if ((s->regs.tmr_val & TMR_VAL_MSB) != msb) {
93 s->regs.pm1a_sts |= TMR_STS;
94 pmt_update_sci(d, s);
95 }
96 }
98 /* This function should be called soon after each time the MSB of the
99 * pmtimer register rolls over, to make sure we update the status
100 * registers and SCI at least once per rollover */
101 static void pmt_timer_callback(void *opaque)
102 {
103 struct domain *d = opaque;
104 struct vacpi *s = &d->arch.hvm_domain.vacpi;
105 uint64_t cycles, time_flip;
107 spin_lock(&s->lock);
109 /* Recalculate the timer and make sure we get an SCI if we need one */
110 pmt_update_time(d);
112 /* How close are we to the next MSB flip? */
113 cycles = TMR_VAL_MSB - (s->regs.tmr_val & (TMR_VAL_MSB - 1));
115 /* Overall time between MSB flips */
116 time_flip = (((SECONDS(1) << 23) / FREQUENCE_PMTIMER) * cycles) >> 23;
118 /* Wake up again near the next bit-flip */
119 set_timer(&s->timer, NOW() + time_flip + MILLISECS(1));
121 spin_unlock(&s->lock);
122 }
124 int vacpi_intercept(ioreq_t * iop, u64 * val)
125 {
126 struct domain *d = current->domain;
127 struct vacpi *s = &d->arch.hvm_domain.vacpi;
128 uint64_t addr_off = iop->addr - ACPI_PM1A_EVT_BLK_ADDRESS;
130 if (addr_off < 4) { /* Access to PM1a_STS and PM1a_EN registers */
131 void *p = (void *)&s->regs.evt_blk + addr_off;
133 spin_lock(&s->lock);
134 if (iop->dir == 1) { /* Read */
135 if (iop->size == 1)
136 *val = *(uint8_t *) p;
137 else if (iop->size == 2)
138 *val = *(uint16_t *) p;
139 else if (iop->size == 4)
140 *val = *(uint32_t *) p;
141 else
142 panic_domain(NULL, "wrong ACPI "
143 "PM1A_EVT_BLK access\n");
144 } else { /* Write */
145 uint8_t *sp = (uint8_t *) & iop->data;
146 int i;
148 for (i = 0; i < iop->size; i++, addr_off++, p++, sp++) {
149 if (addr_off < 2) /* PM1a_STS */
150 /* write-to-clear */
151 *(uint8_t *) p &= ~*sp;
152 else /* PM1a_EN */
153 *(uint8_t *) p = *sp;
154 }
155 /* Fix the SCI state to match the new register state */
156 pmt_update_sci(d, s);
157 }
158 spin_unlock(&s->lock);
160 iop->state = STATE_IORESP_READY;
161 vmx_io_assist(current);
162 return 1;
163 }
165 if (iop->addr == ACPI_PM_TMR_BLK_ADDRESS) {
166 if (iop->size != 4)
167 panic_domain(NULL, "wrong ACPI PM timer access\n");
168 if (iop->dir == 1) { /* Read */
169 spin_lock(&s->lock);
170 pmt_update_time(d);
171 *val = s->regs.tmr_val;
172 spin_unlock(&s->lock);
173 }
174 /* PM_TMR_BLK is read-only */
175 iop->state = STATE_IORESP_READY;
176 vmx_io_assist(current);
177 return 1;
178 }
180 return 0;
181 }
183 void vacpi_init(struct domain *d)
184 {
185 struct vacpi *s = &d->arch.hvm_domain.vacpi;
187 spin_lock_init(&s->lock);
189 s->regs.tmr_val = 0;
190 s->regs.evt_blk = 0;
191 s->last_gtime = NOW();
193 /* Set up callback to fire SCIs when the MSB of TMR_VAL changes */
194 init_timer(&s->timer, pmt_timer_callback, d, first_cpu(cpu_online_map));
195 pmt_timer_callback(d);
196 }
198 void vacpi_relinquish_resources(struct domain *d)
199 {
200 struct vacpi *s = &d->arch.hvm_domain.vacpi;
201 kill_timer(&s->timer);
202 }
204 // stolen from xen/arch/x86/hvm/pmtimer.c
205 static int vacpi_save(struct domain *d, hvm_domain_context_t *h)
206 {
207 struct vacpi *s = &d->arch.hvm_domain.vacpi;
208 unsigned long delta;
209 uint32_t msb = s->regs.tmr_val & TMR_VAL_MSB;
210 struct hvm_hw_ia64_vacpi vacpi_save;
211 int rc;
213 stop_timer(&s->timer); //XXX
215 spin_lock(&s->lock);
217 /* Update the counter to the guest's current time. We always save
218 * with the domain paused, so the saved time should be after the
219 * last_gtime, but just in case, make sure we only go forwards */
221 //XXX NOW() should be the time that domais paused
222 delta = NOW() - s->last_gtime;
223 delta = ((delta >> 8) * ((FREQUENCE_PMTIMER << 32) / SECONDS(1))) >> 24;
224 if ( delta < 1UL<<31 )
225 s->regs.tmr_val += delta;
226 if ( (s->regs.tmr_val & TMR_VAL_MSB) != msb )
227 s->regs.pm1a_sts |= TMR_STS;
228 /* No point in setting the SCI here because we'll already have saved the
229 * IRQ and *PIC state; we'll fix it up when we restore the domain */
231 vacpi_save.regs = s->regs;
232 rc = hvm_save_entry(VACPI, 0, h, &vacpi_save);
234 spin_unlock(&s->lock);
236 pmt_timer_callback(d);//XXX This might change the domain state.
237 return 0;
238 }
240 static int vacpi_load(struct domain *d, hvm_domain_context_t *h)
241 {
242 struct vacpi *s = &d->arch.hvm_domain.vacpi;
243 struct hvm_hw_ia64_vacpi vacpi_load;
245 /* Reload the registers */
246 if ( hvm_load_entry(VACPI, h, &vacpi_load) )
247 return -EINVAL;
249 stop_timer(&s->timer);//XXX
251 spin_lock(&s->lock);
253 s->regs = vacpi_load.regs;
255 /* Calculate future counter values from now. */
256 //XXX NOW(); last_gtime should be set when domain is unpaused
257 s->last_gtime = NOW();
259 /* Set the SCI state from the registers */
260 pmt_update_sci(d, s);
262 spin_unlock(&s->lock);
264 pmt_timer_callback(d);//XXX
265 return 0;
266 }
268 HVM_REGISTER_SAVE_RESTORE(VACPI, vacpi_save, vacpi_load, 1, HVMSR_PER_DOM);