ia64/xen-unstable

view xen/arch/x86/hvm/i8254.c @ 9781:3ff86698f394

Fix 64-bit Xen build after new PIT (i8254) code.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Wed Apr 19 22:23:30 2006 +0100 (2006-04-19)
parents 5765497cf75e
children d0a632bea419
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 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25 /* Edwin Zhai <edwin.zhai@intel.com>
26 * Ported to xen:
27 * use actimer for intr generation;
28 * move speaker io access to hypervisor;
29 * use new method for counter/intrs calculation
30 */
32 #include <xen/config.h>
33 #include <xen/types.h>
34 #include <xen/mm.h>
35 #include <xen/xmalloc.h>
36 #include <xen/lib.h>
37 #include <xen/errno.h>
38 #include <xen/sched.h>
39 #include <asm/hvm/hvm.h>
40 #include <asm/hvm/io.h>
41 #include <asm/hvm/support.h>
42 #include <asm/hvm/vpit.h>
43 #include <asm/current.h>
45 /*#define DEBUG_PIT*/
47 #define RW_STATE_LSB 1
48 #define RW_STATE_MSB 2
49 #define RW_STATE_WORD0 3
50 #define RW_STATE_WORD1 4
52 #ifndef NSEC_PER_SEC
53 #define NSEC_PER_SEC (1000000000ULL)
54 #endif
56 #ifndef TIMER_SLOP
57 #define TIMER_SLOP (50*1000) /* ns */
58 #endif
60 static void pit_irq_timer_update(PITChannelState *s, s64 current_time);
62 s_time_t hvm_get_clock(void)
63 {
64 /* TODO: add pause/unpause support */
65 return NOW();
66 }
68 static int pit_get_count(PITChannelState *s)
69 {
70 u64 d;
71 u64 counter;
73 d = hvm_get_clock() - s->count_load_time;
74 switch(s->mode) {
75 case 0:
76 case 1:
77 case 4:
78 case 5:
79 counter = (s->period - d) & 0xffff;
80 break;
81 case 3:
82 /* XXX: may be incorrect for odd counts */
83 counter = s->period - ((2 * d) % s->period);
84 break;
85 default:
86 /* mod 2 counter handle */
87 d = hvm_get_clock() - s->hvm_time->count_point;
88 d += s->hvm_time->count_advance;
89 counter = s->period - (d % s->period);
90 break;
91 }
92 /* change from ns to pit counter */
93 counter = DIV_ROUND( (counter * PIT_FREQ), NSEC_PER_SEC);
94 return counter;
95 }
97 /* get pit output bit */
98 static int pit_get_out1(PITChannelState *s, s64 current_time)
99 {
100 u64 d;
101 int out;
103 d = current_time - s->count_load_time;
104 switch(s->mode) {
105 default:
106 case 0:
107 out = (d >= s->period);
108 break;
109 case 1:
110 out = (d < s->period);
111 break;
112 case 2:
113 /* mod2 out is no meaning, since intr are generated in background */
114 if ((d % s->period) == 0 && d != 0)
115 out = 1;
116 else
117 out = 0;
118 break;
119 case 3:
120 out = (d % s->period) < ((s->period + 1) >> 1);
121 break;
122 case 4:
123 case 5:
124 out = (d == s->period);
125 break;
126 }
127 return out;
128 }
130 int pit_get_out(hvm_virpit *pit, int channel, s64 current_time)
131 {
132 PITChannelState *s = &pit->channels[channel];
133 return pit_get_out1(s, current_time);
134 }
136 static __inline__ s64 missed_ticks(PITChannelState *s, s64 current_time)
137 {
138 struct hvm_time_info *hvm_time = s->hvm_time;
139 /* ticks from current time(expected time) to NOW */
140 int missed_ticks;
141 /* current_time is expected time for next intr, check if it's true
142 * (actimer has a TIMER_SLOP in advance)
143 */
144 s64 missed_time = hvm_get_clock() + TIMER_SLOP - current_time;
146 if (missed_time >= 0) {
147 missed_ticks = missed_time/(s_time_t)s->period + 1;
148 hvm_time->pending_intr_nr += missed_ticks;
149 s->next_transition_time = current_time + (missed_ticks ) * s->period;
150 } else
151 printk("HVM_PIT:missed ticks < 0 \n");
153 return s->next_transition_time;
154 }
156 /* only rearm the actimer when return value > 0
157 * -2: init state
158 * -1: the mode has expired
159 * 0: current VCPU is not running
160 * >0: the next fired time
161 */
162 s64 pit_get_next_transition_time(PITChannelState *s,
163 s64 current_time)
164 {
165 s64 d, next_time, base;
166 int period2;
167 struct hvm_time_info *hvm_time = s->hvm_time;
169 d = current_time - s->count_load_time;
170 switch(s->mode) {
171 default:
172 case 0:
173 case 1:
174 if (d < s->period)
175 next_time = s->period;
176 else
177 return -1;
178 break;
179 case 2:
180 if (test_bit(_VCPUF_running, &(hvm_time->vcpu->vcpu_flags)) )
181 next_time = missed_ticks(s, current_time);
182 else
183 return 0;
184 break;
185 case 3:
186 base = (d / s->period) * s->period;
187 period2 = ((s->period + 1) >> 1);
188 if ((d - base) < period2)
189 next_time = base + period2;
190 else
191 next_time = base + s->period;
192 break;
193 case 4:
194 case 5:
195 if (d < s->period)
196 next_time = s->period;
197 else if (d == s->period)
198 next_time = s->period + 1;
199 else
200 return -1;
201 break;
202 case 0xff:
203 return -2; /* for init state */
204 break;
205 }
206 /* XXX: better solution: use a clock at PIT_FREQ Hz */
207 if (next_time <= current_time){
208 #ifdef DEBUG_PIT
209 printk("HVM_PIT:next_time <= current_time. next=0x%llx, current=0x%llx!\n",next_time, current_time);
210 #endif
211 next_time = current_time + 1;
212 }
213 return next_time;
214 }
216 /* val must be 0 or 1 */
217 void pit_set_gate(hvm_virpit *pit, int channel, int val)
218 {
219 PITChannelState *s = &pit->channels[channel];
221 switch(s->mode) {
222 default:
223 case 0:
224 case 4:
225 /* XXX: just disable/enable counting */
226 break;
227 case 1:
228 case 5:
229 if (s->gate < val) {
230 /* restart counting on rising edge */
231 s->count_load_time = hvm_get_clock();
232 pit_irq_timer_update(s, s->count_load_time);
233 }
234 break;
235 case 2:
236 case 3:
237 if (s->gate < val) {
238 /* restart counting on rising edge */
239 s->count_load_time = hvm_get_clock();
240 pit_irq_timer_update(s, s->count_load_time);
241 }
242 /* XXX: disable/enable counting */
243 break;
244 }
245 s->gate = val;
246 }
248 int pit_get_gate(hvm_virpit *pit, int channel)
249 {
250 PITChannelState *s = &pit->channels[channel];
251 return s->gate;
252 }
254 static inline void pit_load_count(PITChannelState *s, int val)
255 {
256 if (val == 0)
257 val = 0x10000;
259 s->count_load_time = hvm_get_clock();
260 s->count = val;
261 s->period = DIV_ROUND(((s->count) * NSEC_PER_SEC), PIT_FREQ);
263 #ifdef DEBUG_PIT
264 printk("HVM_PIT: pit-load-counter, count=0x%x,period=0x%u us,mode=%d, load_time=%lld\n",
265 val,
266 s->period / 1000,
267 s->mode,
268 s->count_load_time);
269 #endif
271 if (s->mode == HVM_PIT_ACCEL_MODE) {
272 if (!s->hvm_time) {
273 printk("HVM_PIT:guest should only set mod 2 on channel 0!\n");
274 return;
275 }
276 s->hvm_time->period_cycles = (u64)s->period * cpu_khz / 1000000L;
277 s->hvm_time->first_injected = 0;
279 if (s->period < 900000) { /* < 0.9 ms */
280 printk("HVM_PIT: guest programmed too small an count: %x\n",
281 s->count);
282 s->period = 1000000;
283 }
284 }
286 pit_irq_timer_update(s, s->count_load_time);
287 }
289 /* if already latched, do not latch again */
290 static void pit_latch_count(PITChannelState *s)
291 {
292 if (!s->count_latched) {
293 s->latched_count = pit_get_count(s);
294 s->count_latched = s->rw_mode;
295 }
296 }
298 static void pit_ioport_write(void *opaque, u32 addr, u32 val)
299 {
300 hvm_virpit *pit = opaque;
301 int channel, access;
302 PITChannelState *s;
303 val &= 0xff;
305 addr &= 3;
306 if (addr == 3) {
307 channel = val >> 6;
308 if (channel == 3) {
309 /* read back command */
310 for(channel = 0; channel < 3; channel++) {
311 s = &pit->channels[channel];
312 if (val & (2 << channel)) {
313 if (!(val & 0x20)) {
314 pit_latch_count(s);
315 }
316 if (!(val & 0x10) && !s->status_latched) {
317 /* status latch */
318 /* XXX: add BCD and null count */
319 s->status = (pit_get_out1(s, hvm_get_clock()) << 7) |
320 (s->rw_mode << 4) |
321 (s->mode << 1) |
322 s->bcd;
323 s->status_latched = 1;
324 }
325 }
326 }
327 } else {
328 s = &pit->channels[channel];
329 access = (val >> 4) & 3;
330 if (access == 0) {
331 pit_latch_count(s);
332 } else {
333 s->rw_mode = access;
334 s->read_state = access;
335 s->write_state = access;
337 s->mode = (val >> 1) & 7;
338 s->bcd = val & 1;
339 /* XXX: update irq timer ? */
340 }
341 }
342 } else {
343 s = &pit->channels[addr];
344 switch(s->write_state) {
345 default:
346 case RW_STATE_LSB:
347 pit_load_count(s, val);
348 break;
349 case RW_STATE_MSB:
350 pit_load_count(s, val << 8);
351 break;
352 case RW_STATE_WORD0:
353 s->write_latch = val;
354 s->write_state = RW_STATE_WORD1;
355 break;
356 case RW_STATE_WORD1:
357 pit_load_count(s, s->write_latch | (val << 8));
358 s->write_state = RW_STATE_WORD0;
359 break;
360 }
361 }
362 }
364 static u32 pit_ioport_read(void *opaque, u32 addr)
365 {
366 hvm_virpit *pit = opaque;
367 int ret, count;
368 PITChannelState *s;
370 addr &= 3;
371 s = &pit->channels[addr];
372 if (s->status_latched) {
373 s->status_latched = 0;
374 ret = s->status;
375 } else if (s->count_latched) {
376 switch(s->count_latched) {
377 default:
378 case RW_STATE_LSB:
379 ret = s->latched_count & 0xff;
380 s->count_latched = 0;
381 break;
382 case RW_STATE_MSB:
383 ret = s->latched_count >> 8;
384 s->count_latched = 0;
385 break;
386 case RW_STATE_WORD0:
387 ret = s->latched_count & 0xff;
388 s->count_latched = RW_STATE_MSB;
389 break;
390 }
391 } else {
392 switch(s->read_state) {
393 default:
394 case RW_STATE_LSB:
395 count = pit_get_count(s);
396 ret = count & 0xff;
397 break;
398 case RW_STATE_MSB:
399 count = pit_get_count(s);
400 ret = (count >> 8) & 0xff;
401 break;
402 case RW_STATE_WORD0:
403 count = pit_get_count(s);
404 ret = count & 0xff;
405 s->read_state = RW_STATE_WORD1;
406 break;
407 case RW_STATE_WORD1:
408 count = pit_get_count(s);
409 ret = (count >> 8) & 0xff;
410 s->read_state = RW_STATE_WORD0;
411 break;
412 }
413 }
414 return ret;
415 }
417 static void pit_irq_timer_update(PITChannelState *s, s64 current_time)
418 {
419 s64 expire_time;
420 int irq_level;
421 struct vcpu *v = current;
422 struct hvm_virpic *pic= &v->domain->arch.hvm_domain.vpic;
424 if (!s->hvm_time || s->mode == 0xff)
425 return;
427 expire_time = pit_get_next_transition_time(s, current_time);
428 /* not generate intr by direct pic_set_irq in mod 2
429 * XXX:mod 3 should be same as mod 2
430 */
431 if (s->mode != HVM_PIT_ACCEL_MODE) {
432 irq_level = pit_get_out1(s, current_time);
433 pic_set_irq(pic, s->irq, irq_level);
434 s->next_transition_time = expire_time;
435 #ifdef DEBUG_PIT
436 printk("HVM_PIT:irq_level=%d next_delay=%l ns\n",
437 irq_level,
438 (expire_time - current_time));
439 #endif
440 }
442 if (expire_time > 0)
443 set_timer(&(s->hvm_time->pit_timer), s->next_transition_time);
445 }
447 static void pit_irq_timer(void *data)
448 {
449 PITChannelState *s = data;
451 pit_irq_timer_update(s, s->next_transition_time);
452 }
454 static void pit_reset(void *opaque)
455 {
456 hvm_virpit *pit = opaque;
457 PITChannelState *s;
458 int i;
460 for(i = 0;i < 3; i++) {
461 s = &pit->channels[i];
462 s->mode = 0xff; /* the init mode */
463 s->gate = (i != 2);
464 pit_load_count(s, 0);
465 }
466 }
468 /* hvm_io_assist light-weight version, specific to PIT DM */
469 static void resume_pit_io(ioreq_t *p)
470 {
471 struct cpu_user_regs *regs = guest_cpu_user_regs();
472 unsigned long old_eax = regs->eax;
473 p->state = STATE_INVALID;
475 switch(p->size) {
476 case 1:
477 regs->eax = (old_eax & 0xffffff00) | (p->u.data & 0xff);
478 break;
479 case 2:
480 regs->eax = (old_eax & 0xffff0000) | (p->u.data & 0xffff);
481 break;
482 case 4:
483 regs->eax = (p->u.data & 0xffffffff);
484 break;
485 default:
486 BUG();
487 }
488 }
490 /* the intercept action for PIT DM retval:0--not handled; 1--handled */
491 int handle_pit_io(ioreq_t *p)
492 {
493 struct vcpu *v = current;
494 struct hvm_virpit *vpit = &(v->domain->arch.hvm_domain.vpit);
496 if (p->size != 1 ||
497 p->pdata_valid ||
498 p->type != IOREQ_TYPE_PIO){
499 printk("HVM_PIT:wrong PIT IO!\n");
500 return 1;
501 }
503 if (p->dir == 0) {/* write */
504 pit_ioport_write(vpit, p->addr, p->u.data);
505 } else if (p->dir == 1) { /* read */
506 p->u.data = pit_ioport_read(vpit, p->addr);
507 resume_pit_io(p);
508 }
510 /* always return 1, since PIT sit in HV now */
511 return 1;
512 }
514 static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val)
515 {
516 hvm_virpit *pit = opaque;
517 val &= 0xff;
518 pit->speaker_data_on = (val >> 1) & 1;
519 pit_set_gate(pit, 2, val & 1);
520 }
522 static uint32_t speaker_ioport_read(void *opaque, uint32_t addr)
523 {
524 int out;
525 hvm_virpit *pit = opaque;
526 out = pit_get_out(pit, 2, hvm_get_clock());
527 pit->dummy_refresh_clock ^= 1;
529 return (pit->speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) |
530 (pit->dummy_refresh_clock << 4);
531 }
533 int handle_speaker_io(ioreq_t *p)
534 {
535 struct vcpu *v = current;
536 struct hvm_virpit *vpit = &(v->domain->arch.hvm_domain.vpit);
538 if (p->size != 1 ||
539 p->pdata_valid ||
540 p->type != IOREQ_TYPE_PIO){
541 printk("HVM_SPEAKER:wrong SPEAKER IO!\n");
542 return 1;
543 }
545 if (p->dir == 0) {/* write */
546 speaker_ioport_write(vpit, p->addr, p->u.data);
547 } else if (p->dir == 1) {/* read */
548 p->u.data = speaker_ioport_read(vpit, p->addr);
549 resume_pit_io(p);
550 }
552 return 1;
553 }
555 /* pick up missed timer ticks at deactive time */
556 void pickup_deactive_ticks(struct hvm_virpit *vpit)
557 {
558 s64 next_time;
559 PITChannelState *s = &(vpit->channels[0]);
560 if ( !active_timer(&(vpit->time_info.pit_timer)) ) {
561 next_time = pit_get_next_transition_time(s, s->next_transition_time);
562 if (next_time > 0)
563 set_timer(&(s->hvm_time->pit_timer), s->next_transition_time);
564 else {
565 printk("HVM_PIT:not set_timer before resume next_time=%"
566 PRId64"!\n", next_time);
567 next_time = s->next_transition_time;
568 }
569 }
570 }
572 void pit_init(struct hvm_virpit *pit, struct vcpu *v)
573 {
574 PITChannelState *s;
575 struct hvm_time_info *hvm_time;
577 s = &pit->channels[0];
578 /* the timer 0 is connected to an IRQ */
579 s->irq = 0;
580 /* channel 0 need access the related time info for intr injection */
581 hvm_time = s->hvm_time = &pit->time_info;
582 hvm_time->vcpu = v;
584 init_timer(&(hvm_time->pit_timer), pit_irq_timer, s, v->processor);
586 register_portio_handler(PIT_BASE, 4, handle_pit_io);
588 /* register the speaker port */
589 register_portio_handler(0x61, 1, handle_speaker_io);
591 pit_reset(pit);
593 return;
595 }