1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
|
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* hvm/pmtimer.c: emulation of the ACPI PM timer
*
* Copyright (c) 2007, XenSource inc.
* Copyright (c) 2006, Intel Corporation.
*/
#include <xen/sched.h>
#include <asm/hvm/vpt.h>
#include <asm/hvm/io.h>
#include <asm/hvm/save.h>
#include <asm/acpi.h> /* for hvm_acpi_power_button prototype */
#include <public/hvm/params.h>
/* Slightly more readable port I/O addresses for the registers we intercept */
#define PM1a_STS_ADDR_V0 (ACPI_PM1A_EVT_BLK_ADDRESS_V0)
#define PM1a_EN_ADDR_V0 (ACPI_PM1A_EVT_BLK_ADDRESS_V0 + 2)
#define TMR_VAL_ADDR_V0 (ACPI_PM_TMR_BLK_ADDRESS_V0)
#define PM1a_STS_ADDR_V1 (ACPI_PM1A_EVT_BLK_ADDRESS_V1)
#define PM1a_EN_ADDR_V1 (ACPI_PM1A_EVT_BLK_ADDRESS_V1 + 2)
#define TMR_VAL_ADDR_V1 (ACPI_PM_TMR_BLK_ADDRESS_V1)
/* The interesting bits of the PM1a_STS register */
#define TMR_STS (1 << 0)
#define GBL_STS (1 << 5)
#define PWRBTN_STS (1 << 8)
#define SLPBTN_STS (1 << 9)
/* The same in PM1a_EN */
#define TMR_EN (1 << 0)
#define GBL_EN (1 << 5)
#define PWRBTN_EN (1 << 8)
#define SLPBTN_EN (1 << 9)
/* Mask of bits in PM1a_STS that can generate an SCI. */
#define SCI_MASK (TMR_STS|PWRBTN_STS|SLPBTN_STS|GBL_STS)
/* SCI IRQ number (must match SCI_INT number in ACPI FADT in hvmloader) */
#define SCI_IRQ 9
/* We provide a 32-bit counter (must match the TMR_VAL_EXT bit in the FADT) */
#define TMR_VAL_MASK (0xffffffff)
#define TMR_VAL_MSB (0x80000000)
/* Dispatch SCIs based on the PM1a_STS and PM1a_EN registers */
static void pmt_update_sci(PMTState *s)
{
struct hvm_hw_acpi *acpi = &s->vcpu->domain->arch.hvm.acpi;
ASSERT(spin_is_locked(&s->lock));
if ( acpi->pm1a_en & acpi->pm1a_sts & SCI_MASK )
hvm_isa_irq_assert(s->vcpu->domain, SCI_IRQ, NULL);
else
hvm_isa_irq_deassert(s->vcpu->domain, SCI_IRQ);
}
void hvm_acpi_power_button(struct domain *d)
{
PMTState *s = &d->arch.hvm.pl_time->vpmt;
if ( !has_vpm(d) )
return;
spin_lock(&s->lock);
d->arch.hvm.acpi.pm1a_sts |= PWRBTN_STS;
pmt_update_sci(s);
spin_unlock(&s->lock);
}
void hvm_acpi_sleep_button(struct domain *d)
{
PMTState *s = &d->arch.hvm.pl_time->vpmt;
if ( !has_vpm(d) )
return;
spin_lock(&s->lock);
d->arch.hvm.acpi.pm1a_sts |= SLPBTN_STS;
pmt_update_sci(s);
spin_unlock(&s->lock);
}
/* Set the correct value in the timer, accounting for time elapsed
* since the last time we did that. */
static void pmt_update_time(PMTState *s)
{
uint64_t curr_gtime, tmp;
struct hvm_hw_acpi *acpi = &s->vcpu->domain->arch.hvm.acpi;
uint32_t tmr_val = acpi->tmr_val, msb = tmr_val & TMR_VAL_MSB;
ASSERT(spin_is_locked(&s->lock));
/* Update the timer */
curr_gtime = hvm_get_guest_time(s->vcpu);
tmp = ((curr_gtime - s->last_gtime) * s->scale) + s->not_accounted;
s->not_accounted = (uint32_t)tmp;
tmr_val += tmp >> 32;
tmr_val &= TMR_VAL_MASK;
s->last_gtime = curr_gtime;
/* Update timer value atomically wrt lock-free reads in handle_pmt_io(). */
write_atomic(&acpi->tmr_val, tmr_val);
/* If the counter's MSB has changed, set the status bit */
if ( (tmr_val & TMR_VAL_MSB) != msb )
{
acpi->pm1a_sts |= TMR_STS;
pmt_update_sci(s);
}
}
/* This function should be called soon after each time the MSB of the
* pmtimer register rolls over, to make sure we update the status
* registers and SCI at least once per rollover */
static void cf_check pmt_timer_callback(void *opaque)
{
PMTState *s = opaque;
uint32_t pmt_cycles_until_flip;
uint64_t time_until_flip;
spin_lock(&s->lock);
/* Recalculate the timer and make sure we get an SCI if we need one */
pmt_update_time(s);
/* How close are we to the next MSB flip? */
pmt_cycles_until_flip = TMR_VAL_MSB -
(s->vcpu->domain->arch.hvm.acpi.tmr_val & (TMR_VAL_MSB - 1));
/* Overall time between MSB flips */
time_until_flip = (1000000000ULL << 23) / FREQUENCE_PMTIMER;
/* Reduced appropriately */
time_until_flip = (time_until_flip * pmt_cycles_until_flip) >> 23;
/* Wake up again near the next bit-flip */
set_timer(&s->timer, NOW() + time_until_flip + MILLISECS(1));
spin_unlock(&s->lock);
}
/* Handle port I/O to the PM1a_STS and PM1a_EN registers */
static int cf_check handle_evt_io(
int dir, unsigned int port, unsigned int bytes, uint32_t *val)
{
struct vcpu *v = current;
struct hvm_hw_acpi *acpi = &v->domain->arch.hvm.acpi;
PMTState *s = &v->domain->arch.hvm.pl_time->vpmt;
uint32_t addr, data, byte;
int i;
addr = port -
((v->domain->arch.hvm.params[
HVM_PARAM_ACPI_IOPORTS_LOCATION] == 0) ?
PM1a_STS_ADDR_V0 : PM1a_STS_ADDR_V1);
spin_lock(&s->lock);
if ( dir == IOREQ_WRITE )
{
/* Handle this I/O one byte at a time */
for ( i = bytes, data = *val;
i > 0;
i--, addr++, data >>= 8 )
{
byte = data & 0xff;
switch ( addr )
{
/* PM1a_STS register bits are write-to-clear */
case 0 /* PM1a_STS_ADDR */:
acpi->pm1a_sts &= ~byte;
break;
case 1 /* PM1a_STS_ADDR + 1 */:
acpi->pm1a_sts &= ~(byte << 8);
break;
case 2 /* PM1a_EN_ADDR */:
acpi->pm1a_en = (acpi->pm1a_en & 0xff00) | byte;
break;
case 3 /* PM1a_EN_ADDR + 1 */:
acpi->pm1a_en = (acpi->pm1a_en & 0xff) | (byte << 8);
break;
default:
gdprintk(XENLOG_WARNING,
"Bad ACPI PM register write: %x bytes (%x) at %x\n",
bytes, *val, port);
}
}
/* Fix up the SCI state to match the new register state */
pmt_update_sci(s);
}
else /* p->dir == IOREQ_READ */
{
data = acpi->pm1a_sts | ((uint32_t)acpi->pm1a_en << 16);
data >>= 8 * addr;
if ( bytes == 1 ) data &= 0xff;
else if ( bytes == 2 ) data &= 0xffff;
*val = data;
}
spin_unlock(&s->lock);
return X86EMUL_OKAY;
}
/* Handle port I/O to the TMR_VAL register */
static int cf_check handle_pmt_io(
int dir, unsigned int port, unsigned int bytes, uint32_t *val)
{
struct vcpu *v = current;
struct hvm_hw_acpi *acpi = &v->domain->arch.hvm.acpi;
PMTState *s = &v->domain->arch.hvm.pl_time->vpmt;
if ( bytes != 4 || dir != IOREQ_READ )
{
gdprintk(XENLOG_WARNING, "HVM_PMT bad access\n");
*val = ~0;
}
else if ( spin_trylock(&s->lock) )
{
/* We hold the lock: update timer value and return it. */
pmt_update_time(s);
*val = acpi->tmr_val;
spin_unlock(&s->lock);
}
else
{
/*
* Someone else is updating the timer: rather than do the work
* again ourselves, wait for them to finish and then steal their
* updated value with a lock-free atomic read.
*/
spin_barrier(&s->lock);
*val = read_atomic(&acpi->tmr_val);
}
return X86EMUL_OKAY;
}
static int cf_check acpi_save(struct vcpu *v, hvm_domain_context_t *h)
{
struct domain *d = v->domain;
struct hvm_hw_acpi *acpi = &d->arch.hvm.acpi;
PMTState *s = &d->arch.hvm.pl_time->vpmt;
uint32_t x, msb = acpi->tmr_val & TMR_VAL_MSB;
int rc;
if ( !has_vpm(d) )
return 0;
spin_lock(&s->lock);
/*
* Update the counter to the guest's current time. Make sure it only
* goes forwards.
*/
x = (((s->vcpu->arch.hvm.guest_time ?: hvm_get_guest_time(s->vcpu)) -
s->last_gtime) * s->scale) >> 32;
if ( x < 1UL<<31 )
acpi->tmr_val += x;
if ( (acpi->tmr_val & TMR_VAL_MSB) != msb )
acpi->pm1a_sts |= TMR_STS;
/* No point in setting the SCI here because we'll already have saved the
* IRQ and *PIC state; we'll fix it up when we restore the domain */
rc = hvm_save_entry(PMTIMER, 0, h, acpi);
spin_unlock(&s->lock);
return rc;
}
static int cf_check acpi_load(struct domain *d, hvm_domain_context_t *h)
{
struct hvm_hw_acpi *acpi = &d->arch.hvm.acpi;
PMTState *s = &d->arch.hvm.pl_time->vpmt;
if ( !has_vpm(d) )
return -ENODEV;
spin_lock(&s->lock);
/* Reload the registers */
if ( hvm_load_entry(PMTIMER, h, acpi) )
{
spin_unlock(&s->lock);
return -EINVAL;
}
/* Calculate future counter values from now. */
s->last_gtime = hvm_get_guest_time(s->vcpu);
s->not_accounted = 0;
/* Set the SCI state from the registers */
pmt_update_sci(s);
spin_unlock(&s->lock);
return 0;
}
HVM_REGISTER_SAVE_RESTORE(PMTIMER, acpi_save, acpi_load,
1, HVMSR_PER_DOM);
int pmtimer_change_ioport(struct domain *d, uint64_t version)
{
uint64_t old_version;
if ( !has_vpm(d) )
return -ENODEV;
/* Check that version is changing. */
old_version = d->arch.hvm.params[HVM_PARAM_ACPI_IOPORTS_LOCATION];
if ( version == old_version )
return 0;
/* Only allow changes between versions 0 and 1. */
if ( (version ^ old_version) != 1 )
return -EINVAL;
if ( version == 1 )
{
/* Moving from version 0 to version 1. */
relocate_portio_handler(d, TMR_VAL_ADDR_V0, TMR_VAL_ADDR_V1, 4);
relocate_portio_handler(d, PM1a_STS_ADDR_V0, PM1a_STS_ADDR_V1, 4);
}
else
{
/* Moving from version 1 to version 0. */
relocate_portio_handler(d, TMR_VAL_ADDR_V1, TMR_VAL_ADDR_V0, 4);
relocate_portio_handler(d, PM1a_STS_ADDR_V1, PM1a_STS_ADDR_V0, 4);
}
return 0;
}
void pmtimer_init(struct vcpu *v)
{
PMTState *s = &v->domain->arch.hvm.pl_time->vpmt;
if ( !has_vpm(v->domain) )
return;
spin_lock_init(&s->lock);
s->scale = ((uint64_t)FREQUENCE_PMTIMER << 32) / SYSTEM_TIME_HZ;
s->not_accounted = 0;
s->vcpu = v;
/* Intercept port I/O (need two handlers because PM1a_CNT is between
* PM1a_EN and TMR_VAL and is handled by qemu) */
register_portio_handler(v->domain, TMR_VAL_ADDR_V0, 4, handle_pmt_io);
register_portio_handler(v->domain, PM1a_STS_ADDR_V0, 4, handle_evt_io);
/* Set up callback to fire SCIs when the MSB of TMR_VAL changes */
init_timer(&s->timer, pmt_timer_callback, s, v->processor);
pmt_timer_callback(s);
}
void pmtimer_deinit(struct domain *d)
{
PMTState *s = &d->arch.hvm.pl_time->vpmt;
if ( !has_vpm(d) || !d->arch.hvm.pl_time || !s->vcpu )
return;
kill_timer(&s->timer);
}
void pmtimer_reset(struct domain *d)
{
if ( !has_vpm(d) )
return;
/* Reset the counter. */
d->arch.hvm.acpi.tmr_val = 0;
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/
|