ia64/xen-unstable

view xen/arch/x86/hvm/i8254.c @ 16603:4553bc1087d9

hvm: Reduce vpt.c dependencies on external timer details.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Dec 12 15:41:20 2007 +0000 (2007-12-12)
parents ca1085e8ab82
children 9862217f3c34
line source
1 /*
2 * QEMU 8253/8254 interval timer emulation
3 *
4 * Copyright (c) 2003-2004 Fabrice Bellard
5 * Copyright (c) 2006 Intel Corperation
6 * Copyright (c) 2007 Keir Fraser, XenSource Inc.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
27 #include <xen/config.h>
28 #include <xen/types.h>
29 #include <xen/mm.h>
30 #include <xen/xmalloc.h>
31 #include <xen/lib.h>
32 #include <xen/errno.h>
33 #include <xen/sched.h>
34 #include <asm/hvm/hvm.h>
35 #include <asm/hvm/io.h>
36 #include <asm/hvm/support.h>
37 #include <asm/hvm/vpt.h>
38 #include <asm/current.h>
40 #define domain_vpit(d) (&(d)->arch.hvm_domain.pl_time.vpit)
41 #define vcpu_vpit(vcpu) (domain_vpit((vcpu)->domain))
42 #define vpit_domain(pit) (container_of((pit), struct domain, \
43 arch.hvm_domain.pl_time.vpit))
44 #define vpit_vcpu(pit) (vpit_domain(pit)->vcpu[0])
46 #define RW_STATE_LSB 1
47 #define RW_STATE_MSB 2
48 #define RW_STATE_WORD0 3
49 #define RW_STATE_WORD1 4
51 static int handle_pit_io(ioreq_t *p);
52 static int handle_speaker_io(ioreq_t *p);
54 /* Compute with 96 bit intermediate result: (a*b)/c */
55 static uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
56 {
57 union {
58 uint64_t ll;
59 struct {
60 #ifdef WORDS_BIGENDIAN
61 uint32_t high, low;
62 #else
63 uint32_t low, high;
64 #endif
65 } l;
66 } u, res;
67 uint64_t rl, rh;
69 u.ll = a;
70 rl = (uint64_t)u.l.low * (uint64_t)b;
71 rh = (uint64_t)u.l.high * (uint64_t)b;
72 rh += (rl >> 32);
73 res.l.high = rh / c;
74 res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
75 return res.ll;
76 }
78 static int pit_get_count(PITState *pit, int channel)
79 {
80 uint64_t d;
81 int counter;
82 struct hvm_hw_pit_channel *c = &pit->hw.channels[channel];
83 struct vcpu *v = vpit_vcpu(pit);
85 ASSERT(spin_is_locked(&pit->lock));
87 d = muldiv64(hvm_get_guest_time(v) - pit->count_load_time[channel],
88 PIT_FREQ, ticks_per_sec(v));
90 switch ( c->mode )
91 {
92 case 0:
93 case 1:
94 case 4:
95 case 5:
96 counter = (c->count - d) & 0xffff;
97 break;
98 case 3:
99 /* XXX: may be incorrect for odd counts */
100 counter = c->count - ((2 * d) % c->count);
101 break;
102 default:
103 counter = c->count - (d % c->count);
104 break;
105 }
106 return counter;
107 }
109 static int pit_get_out(PITState *pit, int channel)
110 {
111 struct hvm_hw_pit_channel *s = &pit->hw.channels[channel];
112 uint64_t d;
113 int out;
114 struct vcpu *v = vpit_vcpu(pit);
116 ASSERT(spin_is_locked(&pit->lock));
118 d = muldiv64(hvm_get_guest_time(v) - pit->count_load_time[channel],
119 PIT_FREQ, ticks_per_sec(v));
121 switch ( s->mode )
122 {
123 default:
124 case 0:
125 out = (d >= s->count);
126 break;
127 case 1:
128 out = (d < s->count);
129 break;
130 case 2:
131 out = (((d % s->count) == 0) && (d != 0));
132 break;
133 case 3:
134 out = ((d % s->count) < ((s->count + 1) >> 1));
135 break;
136 case 4:
137 case 5:
138 out = (d == s->count);
139 break;
140 }
142 return out;
143 }
145 static void pit_set_gate(PITState *pit, int channel, int val)
146 {
147 struct hvm_hw_pit_channel *s = &pit->hw.channels[channel];
148 struct vcpu *v = vpit_vcpu(pit);
150 ASSERT(spin_is_locked(&pit->lock));
152 switch ( s->mode )
153 {
154 default:
155 case 0:
156 case 4:
157 /* XXX: just disable/enable counting */
158 break;
159 case 1:
160 case 5:
161 case 2:
162 case 3:
163 /* Restart counting on rising edge. */
164 if ( s->gate < val )
165 pit->count_load_time[channel] = hvm_get_guest_time(v);
166 break;
167 }
169 s->gate = val;
170 }
172 int pit_get_gate(PITState *pit, int channel)
173 {
174 ASSERT(spin_is_locked(&pit->lock));
175 return pit->hw.channels[channel].gate;
176 }
178 static void pit_time_fired(struct vcpu *v, void *priv)
179 {
180 uint64_t *count_load_time = priv;
181 *count_load_time = hvm_get_guest_time(v);
182 }
184 static void pit_load_count(PITState *pit, int channel, int val)
185 {
186 u32 period;
187 struct hvm_hw_pit_channel *s = &pit->hw.channels[channel];
188 struct vcpu *v = vpit_vcpu(pit);
190 ASSERT(spin_is_locked(&pit->lock));
192 if ( val == 0 )
193 val = 0x10000;
195 if ( v == NULL )
196 rdtscll(pit->count_load_time[channel]);
197 else
198 pit->count_load_time[channel] = hvm_get_guest_time(v);
199 s->count = val;
200 period = DIV_ROUND((val * 1000000000ULL), PIT_FREQ);
202 if ( (v == NULL) || !is_hvm_vcpu(v) || (channel != 0) )
203 return;
205 switch ( s->mode )
206 {
207 case 2:
208 /* Periodic timer. */
209 create_periodic_time(v, &pit->pt0, period, 0, 0, pit_time_fired,
210 &pit->count_load_time[channel]);
211 break;
212 case 1:
213 /* One-shot timer. */
214 create_periodic_time(v, &pit->pt0, period, 0, 1, pit_time_fired,
215 &pit->count_load_time[channel]);
216 break;
217 default:
218 destroy_periodic_time(&pit->pt0);
219 break;
220 }
221 }
223 static void pit_latch_count(PITState *pit, int channel)
224 {
225 struct hvm_hw_pit_channel *c = &pit->hw.channels[channel];
227 ASSERT(spin_is_locked(&pit->lock));
229 if ( !c->count_latched )
230 {
231 c->latched_count = pit_get_count(pit, channel);
232 c->count_latched = c->rw_mode;
233 }
234 }
236 static void pit_latch_status(PITState *pit, int channel)
237 {
238 struct hvm_hw_pit_channel *c = &pit->hw.channels[channel];
240 ASSERT(spin_is_locked(&pit->lock));
242 if ( !c->status_latched )
243 {
244 /* TODO: Return NULL COUNT (bit 6). */
245 c->status = ((pit_get_out(pit, channel) << 7) |
246 (c->rw_mode << 4) |
247 (c->mode << 1) |
248 c->bcd);
249 c->status_latched = 1;
250 }
251 }
253 static void pit_ioport_write(struct PITState *pit, uint32_t addr, uint32_t val)
254 {
255 int channel, access;
256 struct hvm_hw_pit_channel *s;
258 val &= 0xff;
259 addr &= 3;
261 spin_lock(&pit->lock);
263 if ( addr == 3 )
264 {
265 channel = val >> 6;
266 if ( channel == 3 )
267 {
268 /* Read-Back Command. */
269 for ( channel = 0; channel < 3; channel++ )
270 {
271 s = &pit->hw.channels[channel];
272 if ( val & (2 << channel) )
273 {
274 if ( !(val & 0x20) )
275 pit_latch_count(pit, channel);
276 if ( !(val & 0x10) )
277 pit_latch_status(pit, channel);
278 }
279 }
280 }
281 else
282 {
283 /* Select Counter <channel>. */
284 s = &pit->hw.channels[channel];
285 access = (val >> 4) & 3;
286 if ( access == 0 )
287 {
288 pit_latch_count(pit, channel);
289 }
290 else
291 {
292 s->rw_mode = access;
293 s->read_state = access;
294 s->write_state = access;
295 s->mode = (val >> 1) & 7;
296 if ( s->mode > 5 )
297 s->mode -= 4;
298 s->bcd = val & 1;
299 /* XXX: update irq timer ? */
300 }
301 }
302 }
303 else
304 {
305 /* Write Count. */
306 s = &pit->hw.channels[addr];
307 switch ( s->write_state )
308 {
309 default:
310 case RW_STATE_LSB:
311 pit_load_count(pit, addr, val);
312 break;
313 case RW_STATE_MSB:
314 pit_load_count(pit, addr, val << 8);
315 break;
316 case RW_STATE_WORD0:
317 s->write_latch = val;
318 s->write_state = RW_STATE_WORD1;
319 break;
320 case RW_STATE_WORD1:
321 pit_load_count(pit, addr, s->write_latch | (val << 8));
322 s->write_state = RW_STATE_WORD0;
323 break;
324 }
325 }
327 spin_unlock(&pit->lock);
328 }
330 static uint32_t pit_ioport_read(struct PITState *pit, uint32_t addr)
331 {
332 int ret, count;
333 struct hvm_hw_pit_channel *s;
335 addr &= 3;
336 s = &pit->hw.channels[addr];
338 spin_lock(&pit->lock);
340 if ( s->status_latched )
341 {
342 s->status_latched = 0;
343 ret = s->status;
344 }
345 else if ( s->count_latched )
346 {
347 switch ( s->count_latched )
348 {
349 default:
350 case RW_STATE_LSB:
351 ret = s->latched_count & 0xff;
352 s->count_latched = 0;
353 break;
354 case RW_STATE_MSB:
355 ret = s->latched_count >> 8;
356 s->count_latched = 0;
357 break;
358 case RW_STATE_WORD0:
359 ret = s->latched_count & 0xff;
360 s->count_latched = RW_STATE_MSB;
361 break;
362 }
363 }
364 else
365 {
366 switch ( s->read_state )
367 {
368 default:
369 case RW_STATE_LSB:
370 count = pit_get_count(pit, addr);
371 ret = count & 0xff;
372 break;
373 case RW_STATE_MSB:
374 count = pit_get_count(pit, addr);
375 ret = (count >> 8) & 0xff;
376 break;
377 case RW_STATE_WORD0:
378 count = pit_get_count(pit, addr);
379 ret = count & 0xff;
380 s->read_state = RW_STATE_WORD1;
381 break;
382 case RW_STATE_WORD1:
383 count = pit_get_count(pit, addr);
384 ret = (count >> 8) & 0xff;
385 s->read_state = RW_STATE_WORD0;
386 break;
387 }
388 }
390 spin_unlock(&pit->lock);
392 return ret;
393 }
395 void pit_stop_channel0_irq(PITState *pit)
396 {
397 spin_lock(&pit->lock);
398 destroy_periodic_time(&pit->pt0);
399 spin_unlock(&pit->lock);
400 }
402 #ifdef HVM_DEBUG_SUSPEND
403 static void pit_info(PITState *pit)
404 {
405 struct hvm_hw_pit_channel *s;
406 struct periodic_time *pt;
407 int i;
409 for ( i = 0; i < 3; i++ )
410 {
411 printk("*****pit channel %d's state:*****\n", i);
412 s = &pit->hw.channels[i];
413 printk("pit 0x%x.\n", s->count);
414 printk("pit 0x%x.\n", s->latched_count);
415 printk("pit 0x%x.\n", s->count_latched);
416 printk("pit 0x%x.\n", s->status_latched);
417 printk("pit 0x%x.\n", s->status);
418 printk("pit 0x%x.\n", s->read_state);
419 printk("pit 0x%x.\n", s->write_state);
420 printk("pit 0x%x.\n", s->write_latch);
421 printk("pit 0x%x.\n", s->rw_mode);
422 printk("pit 0x%x.\n", s->mode);
423 printk("pit 0x%x.\n", s->bcd);
424 printk("pit 0x%x.\n", s->gate);
425 printk("pit %"PRId64"\n", pit->count_load_time[i]);
427 }
429 pt = &pit->pt0;
430 printk("pit channel 0 periodic timer:\n", i);
431 printk("pt %d.\n", pt->enabled);
432 printk("pt %d.\n", pt->one_shot);
433 printk("pt %d.\n", pt->irq);
434 printk("pt %d.\n", pt->first_injected);
435 printk("pt %d.\n", pt->pending_intr_nr);
436 printk("pt %d.\n", pt->period);
437 printk("pt %"PRId64"\n", pt->period_cycles);
438 printk("pt %"PRId64"\n", pt->last_plt_gtime);
439 }
440 #else
441 static void pit_info(PITState *pit)
442 {
443 }
444 #endif
446 static int pit_save(struct domain *d, hvm_domain_context_t *h)
447 {
448 PITState *pit = domain_vpit(d);
449 int rc;
451 spin_lock(&pit->lock);
453 pit_info(pit);
455 /* Save the PIT hardware state */
456 rc = hvm_save_entry(PIT, 0, h, &pit->hw);
458 spin_unlock(&pit->lock);
460 return rc;
461 }
463 static int pit_load(struct domain *d, hvm_domain_context_t *h)
464 {
465 PITState *pit = domain_vpit(d);
466 int i;
468 spin_lock(&pit->lock);
470 /* Restore the PIT hardware state */
471 if ( hvm_load_entry(PIT, h, &pit->hw) )
472 {
473 spin_unlock(&pit->lock);
474 return 1;
475 }
477 /* Recreate platform timers from hardware state. There will be some
478 * time jitter here, but the wall-clock will have jumped massively, so
479 * we hope the guest can handle it. */
480 pit->pt0.last_plt_gtime = hvm_get_guest_time(d->vcpu[0]);
481 for ( i = 0; i < 3; i++ )
482 pit_load_count(pit, i, pit->hw.channels[i].count);
484 pit_info(pit);
486 spin_unlock(&pit->lock);
488 return 0;
489 }
491 HVM_REGISTER_SAVE_RESTORE(PIT, pit_save, pit_load, 1, HVMSR_PER_DOM);
493 void pit_init(struct vcpu *v, unsigned long cpu_khz)
494 {
495 PITState *pit = vcpu_vpit(v);
496 struct hvm_hw_pit_channel *s;
497 int i;
499 spin_lock_init(&pit->lock);
501 /* Some sub-functions assert that they are called with the lock held. */
502 spin_lock(&pit->lock);
504 pit->pt0.source = PTSRC_isa;
506 register_portio_handler(v->domain, PIT_BASE, 4, handle_pit_io);
507 register_portio_handler(v->domain, 0x61, 1, handle_speaker_io);
508 ticks_per_sec(v) = cpu_khz * (int64_t)1000;
510 for ( i = 0; i < 3; i++ )
511 {
512 s = &pit->hw.channels[i];
513 s->mode = 0xff; /* the init mode */
514 s->gate = (i != 2);
515 pit_load_count(pit, i, 0);
516 }
518 spin_unlock(&pit->lock);
519 }
521 void pit_deinit(struct domain *d)
522 {
523 PITState *pit = domain_vpit(d);
524 destroy_periodic_time(&pit->pt0);
525 }
527 /* the intercept action for PIT DM retval:0--not handled; 1--handled */
528 static int handle_pit_io(ioreq_t *p)
529 {
530 struct PITState *vpit = vcpu_vpit(current);
532 if ( (p->size != 1) || p->data_is_ptr || (p->type != IOREQ_TYPE_PIO) )
533 {
534 gdprintk(XENLOG_WARNING, "PIT bad access\n");
535 return 1;
536 }
538 if ( p->dir == IOREQ_WRITE )
539 {
540 pit_ioport_write(vpit, p->addr, p->data);
541 }
542 else
543 {
544 if ( (p->addr & 3) != 3 )
545 p->data = pit_ioport_read(vpit, p->addr);
546 else
547 gdprintk(XENLOG_WARNING, "PIT: read A1:A0=3!\n");
548 }
550 return 1;
551 }
553 static void speaker_ioport_write(
554 struct PITState *pit, uint32_t addr, uint32_t val)
555 {
556 pit->hw.speaker_data_on = (val >> 1) & 1;
557 pit_set_gate(pit, 2, val & 1);
558 }
560 static uint32_t speaker_ioport_read(
561 struct PITState *pit, uint32_t addr)
562 {
563 /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */
564 unsigned int refresh_clock = ((unsigned int)NOW() >> 14) & 1;
565 return ((pit->hw.speaker_data_on << 1) | pit_get_gate(pit, 2) |
566 (pit_get_out(pit, 2) << 5) | (refresh_clock << 4));
567 }
569 static int handle_speaker_io(ioreq_t *p)
570 {
571 struct PITState *vpit = vcpu_vpit(current);
573 if ( (p->size != 1) || p->data_is_ptr || (p->type != IOREQ_TYPE_PIO) )
574 {
575 gdprintk(XENLOG_WARNING, "PIT_SPEAKER bad access\n");
576 return 1;
577 }
579 spin_lock(&vpit->lock);
581 if ( p->dir == IOREQ_WRITE )
582 speaker_ioport_write(vpit, p->addr, p->data);
583 else
584 p->data = speaker_ioport_read(vpit, p->addr);
586 spin_unlock(&vpit->lock);
588 return 1;
589 }
591 int pv_pit_handler(int port, int data, int write)
592 {
593 ioreq_t ioreq = {
594 .size = 1,
595 .type = IOREQ_TYPE_PIO,
596 .addr = port,
597 .dir = write ? IOREQ_WRITE : IOREQ_READ,
598 .data = data
599 };
601 if ( (current->domain->domain_id == 0) && dom0_pit_access(&ioreq) )
602 /* nothing to do */;
603 else if ( port == 0x61 )
604 handle_speaker_io(&ioreq);
605 else
606 handle_pit_io(&ioreq);
608 return !write ? ioreq.data : 0;
609 }