ia64/xen-unstable

view xen/arch/x86/hvm/i8254.c @ 19824:50634c215234

Move muldiv64 out and make it as a public function.

muldiv64 is used to caculate u64*u32/u32, and we
will use it for TSC scaling.

Signed-off-by: Xiantao Zhang <xiantao.zhang@intel.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Jun 24 10:57:00 2009 +0100 (2009-06-24)
parents 82bbce59b65d
children
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/time.h>
35 #include <asm/hvm/hvm.h>
36 #include <asm/hvm/io.h>
37 #include <asm/hvm/support.h>
38 #include <asm/hvm/vpt.h>
39 #include <asm/current.h>
41 #define domain_vpit(d) (&(d)->arch.hvm_domain.pl_time.vpit)
42 #define vcpu_vpit(vcpu) (domain_vpit((vcpu)->domain))
43 #define vpit_domain(pit) (container_of((pit), struct domain, \
44 arch.hvm_domain.pl_time.vpit))
45 #define vpit_vcpu(pit) (vpit_domain(pit)->vcpu[0])
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 static int handle_pit_io(
53 int dir, uint32_t port, uint32_t bytes, uint32_t *val);
54 static int handle_speaker_io(
55 int dir, uint32_t port, uint32_t bytes, uint32_t *val);
57 #define get_guest_time(v) \
58 (is_hvm_vcpu(v) ? hvm_get_guest_time(v) : (u64)get_s_time())
60 static int pit_get_count(PITState *pit, int channel)
61 {
62 uint64_t d;
63 int counter;
64 struct hvm_hw_pit_channel *c = &pit->hw.channels[channel];
65 struct vcpu *v = vpit_vcpu(pit);
67 ASSERT(spin_is_locked(&pit->lock));
69 d = muldiv64(get_guest_time(v) - pit->count_load_time[channel],
70 PIT_FREQ, SYSTEM_TIME_HZ);
72 switch ( c->mode )
73 {
74 case 0:
75 case 1:
76 case 4:
77 case 5:
78 counter = (c->count - d) & 0xffff;
79 break;
80 case 3:
81 /* XXX: may be incorrect for odd counts */
82 counter = c->count - ((2 * d) % c->count);
83 break;
84 default:
85 counter = c->count - (d % c->count);
86 break;
87 }
88 return counter;
89 }
91 static int pit_get_out(PITState *pit, int channel)
92 {
93 struct hvm_hw_pit_channel *s = &pit->hw.channels[channel];
94 uint64_t d;
95 int out;
96 struct vcpu *v = vpit_vcpu(pit);
98 ASSERT(spin_is_locked(&pit->lock));
100 d = muldiv64(get_guest_time(v) - pit->count_load_time[channel],
101 PIT_FREQ, SYSTEM_TIME_HZ);
103 switch ( s->mode )
104 {
105 default:
106 case 0:
107 out = (d >= s->count);
108 break;
109 case 1:
110 out = (d < s->count);
111 break;
112 case 2:
113 out = (((d % s->count) == 0) && (d != 0));
114 break;
115 case 3:
116 out = ((d % s->count) < ((s->count + 1) >> 1));
117 break;
118 case 4:
119 case 5:
120 out = (d == s->count);
121 break;
122 }
124 return out;
125 }
127 static void pit_set_gate(PITState *pit, int channel, int val)
128 {
129 struct hvm_hw_pit_channel *s = &pit->hw.channels[channel];
130 struct vcpu *v = vpit_vcpu(pit);
132 ASSERT(spin_is_locked(&pit->lock));
134 switch ( s->mode )
135 {
136 default:
137 case 0:
138 case 4:
139 /* XXX: just disable/enable counting */
140 break;
141 case 1:
142 case 5:
143 case 2:
144 case 3:
145 /* Restart counting on rising edge. */
146 if ( s->gate < val )
147 pit->count_load_time[channel] = get_guest_time(v);
148 break;
149 }
151 s->gate = val;
152 }
154 int pit_get_gate(PITState *pit, int channel)
155 {
156 ASSERT(spin_is_locked(&pit->lock));
157 return pit->hw.channels[channel].gate;
158 }
160 static void pit_time_fired(struct vcpu *v, void *priv)
161 {
162 uint64_t *count_load_time = priv;
163 *count_load_time = get_guest_time(v);
164 }
166 static void pit_load_count(PITState *pit, int channel, int val)
167 {
168 u32 period;
169 struct hvm_hw_pit_channel *s = &pit->hw.channels[channel];
170 struct vcpu *v = vpit_vcpu(pit);
172 ASSERT(spin_is_locked(&pit->lock));
174 if ( val == 0 )
175 val = 0x10000;
177 if ( v == NULL )
178 pit->count_load_time[channel] = 0;
179 else
180 pit->count_load_time[channel] = get_guest_time(v);
181 s->count = val;
182 period = DIV_ROUND(val * SYSTEM_TIME_HZ, PIT_FREQ);
184 if ( (v == NULL) || !is_hvm_vcpu(v) || (channel != 0) )
185 return;
187 switch ( s->mode )
188 {
189 case 2:
190 case 3:
191 /* Periodic timer. */
192 create_periodic_time(v, &pit->pt0, period, period, 0, pit_time_fired,
193 &pit->count_load_time[channel]);
194 break;
195 case 1:
196 case 4:
197 /* One-shot timer. */
198 create_periodic_time(v, &pit->pt0, period, 0, 0, pit_time_fired,
199 &pit->count_load_time[channel]);
200 break;
201 default:
202 destroy_periodic_time(&pit->pt0);
203 break;
204 }
205 }
207 static void pit_latch_count(PITState *pit, int channel)
208 {
209 struct hvm_hw_pit_channel *c = &pit->hw.channels[channel];
211 ASSERT(spin_is_locked(&pit->lock));
213 if ( !c->count_latched )
214 {
215 c->latched_count = pit_get_count(pit, channel);
216 c->count_latched = c->rw_mode;
217 }
218 }
220 static void pit_latch_status(PITState *pit, int channel)
221 {
222 struct hvm_hw_pit_channel *c = &pit->hw.channels[channel];
224 ASSERT(spin_is_locked(&pit->lock));
226 if ( !c->status_latched )
227 {
228 /* TODO: Return NULL COUNT (bit 6). */
229 c->status = ((pit_get_out(pit, channel) << 7) |
230 (c->rw_mode << 4) |
231 (c->mode << 1) |
232 c->bcd);
233 c->status_latched = 1;
234 }
235 }
237 static void pit_ioport_write(struct PITState *pit, uint32_t addr, uint32_t val)
238 {
239 int channel, access;
240 struct hvm_hw_pit_channel *s;
242 val &= 0xff;
243 addr &= 3;
245 spin_lock(&pit->lock);
247 if ( addr == 3 )
248 {
249 channel = val >> 6;
250 if ( channel == 3 )
251 {
252 /* Read-Back Command. */
253 for ( channel = 0; channel < 3; channel++ )
254 {
255 s = &pit->hw.channels[channel];
256 if ( val & (2 << channel) )
257 {
258 if ( !(val & 0x20) )
259 pit_latch_count(pit, channel);
260 if ( !(val & 0x10) )
261 pit_latch_status(pit, channel);
262 }
263 }
264 }
265 else
266 {
267 /* Select Counter <channel>. */
268 s = &pit->hw.channels[channel];
269 access = (val >> 4) & 3;
270 if ( access == 0 )
271 {
272 pit_latch_count(pit, channel);
273 }
274 else
275 {
276 s->rw_mode = access;
277 s->read_state = access;
278 s->write_state = access;
279 s->mode = (val >> 1) & 7;
280 if ( s->mode > 5 )
281 s->mode -= 4;
282 s->bcd = val & 1;
283 /* XXX: update irq timer ? */
284 }
285 }
286 }
287 else
288 {
289 /* Write Count. */
290 s = &pit->hw.channels[addr];
291 switch ( s->write_state )
292 {
293 default:
294 case RW_STATE_LSB:
295 pit_load_count(pit, addr, val);
296 break;
297 case RW_STATE_MSB:
298 pit_load_count(pit, addr, val << 8);
299 break;
300 case RW_STATE_WORD0:
301 s->write_latch = val;
302 s->write_state = RW_STATE_WORD1;
303 break;
304 case RW_STATE_WORD1:
305 pit_load_count(pit, addr, s->write_latch | (val << 8));
306 s->write_state = RW_STATE_WORD0;
307 break;
308 }
309 }
311 spin_unlock(&pit->lock);
312 }
314 static uint32_t pit_ioport_read(struct PITState *pit, uint32_t addr)
315 {
316 int ret, count;
317 struct hvm_hw_pit_channel *s;
319 addr &= 3;
320 s = &pit->hw.channels[addr];
322 spin_lock(&pit->lock);
324 if ( s->status_latched )
325 {
326 s->status_latched = 0;
327 ret = s->status;
328 }
329 else if ( s->count_latched )
330 {
331 switch ( s->count_latched )
332 {
333 default:
334 case RW_STATE_LSB:
335 ret = s->latched_count & 0xff;
336 s->count_latched = 0;
337 break;
338 case RW_STATE_MSB:
339 ret = s->latched_count >> 8;
340 s->count_latched = 0;
341 break;
342 case RW_STATE_WORD0:
343 ret = s->latched_count & 0xff;
344 s->count_latched = RW_STATE_MSB;
345 break;
346 }
347 }
348 else
349 {
350 switch ( s->read_state )
351 {
352 default:
353 case RW_STATE_LSB:
354 count = pit_get_count(pit, addr);
355 ret = count & 0xff;
356 break;
357 case RW_STATE_MSB:
358 count = pit_get_count(pit, addr);
359 ret = (count >> 8) & 0xff;
360 break;
361 case RW_STATE_WORD0:
362 count = pit_get_count(pit, addr);
363 ret = count & 0xff;
364 s->read_state = RW_STATE_WORD1;
365 break;
366 case RW_STATE_WORD1:
367 count = pit_get_count(pit, addr);
368 ret = (count >> 8) & 0xff;
369 s->read_state = RW_STATE_WORD0;
370 break;
371 }
372 }
374 spin_unlock(&pit->lock);
376 return ret;
377 }
379 void pit_stop_channel0_irq(PITState *pit)
380 {
381 spin_lock(&pit->lock);
382 destroy_periodic_time(&pit->pt0);
383 spin_unlock(&pit->lock);
384 }
386 static int pit_save(struct domain *d, hvm_domain_context_t *h)
387 {
388 PITState *pit = domain_vpit(d);
389 int rc;
391 spin_lock(&pit->lock);
393 rc = hvm_save_entry(PIT, 0, h, &pit->hw);
395 spin_unlock(&pit->lock);
397 return rc;
398 }
400 static int pit_load(struct domain *d, hvm_domain_context_t *h)
401 {
402 PITState *pit = domain_vpit(d);
403 int i;
405 spin_lock(&pit->lock);
407 if ( hvm_load_entry(PIT, h, &pit->hw) )
408 {
409 spin_unlock(&pit->lock);
410 return 1;
411 }
413 /*
414 * Recreate platform timers from hardware state. There will be some
415 * time jitter here, but the wall-clock will have jumped massively, so
416 * we hope the guest can handle it.
417 */
418 pit->pt0.last_plt_gtime = get_guest_time(d->vcpu[0]);
419 for ( i = 0; i < 3; i++ )
420 pit_load_count(pit, i, pit->hw.channels[i].count);
422 spin_unlock(&pit->lock);
424 return 0;
425 }
427 HVM_REGISTER_SAVE_RESTORE(PIT, pit_save, pit_load, 1, HVMSR_PER_DOM);
429 void pit_reset(struct domain *d)
430 {
431 PITState *pit = domain_vpit(d);
432 struct hvm_hw_pit_channel *s;
433 int i;
435 destroy_periodic_time(&pit->pt0);
436 pit->pt0.source = PTSRC_isa;
438 spin_lock(&pit->lock);
440 for ( i = 0; i < 3; i++ )
441 {
442 s = &pit->hw.channels[i];
443 s->mode = 0xff; /* the init mode */
444 s->gate = (i != 2);
445 pit_load_count(pit, i, 0);
446 }
448 spin_unlock(&pit->lock);
449 }
451 void pit_init(struct vcpu *v, unsigned long cpu_khz)
452 {
453 PITState *pit = vcpu_vpit(v);
455 spin_lock_init(&pit->lock);
457 register_portio_handler(v->domain, PIT_BASE, 4, handle_pit_io);
458 register_portio_handler(v->domain, 0x61, 1, handle_speaker_io);
460 pit_reset(v->domain);
461 }
463 void pit_deinit(struct domain *d)
464 {
465 PITState *pit = domain_vpit(d);
466 destroy_periodic_time(&pit->pt0);
467 }
469 /* the intercept action for PIT DM retval:0--not handled; 1--handled */
470 static int handle_pit_io(
471 int dir, uint32_t port, uint32_t bytes, uint32_t *val)
472 {
473 struct PITState *vpit = vcpu_vpit(current);
475 if ( bytes != 1 )
476 {
477 gdprintk(XENLOG_WARNING, "PIT bad access\n");
478 return X86EMUL_OKAY;
479 }
481 if ( dir == IOREQ_WRITE )
482 {
483 pit_ioport_write(vpit, port, *val);
484 }
485 else
486 {
487 if ( (port & 3) != 3 )
488 *val = pit_ioport_read(vpit, port);
489 else
490 gdprintk(XENLOG_WARNING, "PIT: read A1:A0=3!\n");
491 }
493 return X86EMUL_OKAY;
494 }
496 static void speaker_ioport_write(
497 struct PITState *pit, uint32_t addr, uint32_t val)
498 {
499 pit->hw.speaker_data_on = (val >> 1) & 1;
500 pit_set_gate(pit, 2, val & 1);
501 }
503 static uint32_t speaker_ioport_read(
504 struct PITState *pit, uint32_t addr)
505 {
506 /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */
507 unsigned int refresh_clock = ((unsigned int)NOW() >> 14) & 1;
508 return ((pit->hw.speaker_data_on << 1) | pit_get_gate(pit, 2) |
509 (pit_get_out(pit, 2) << 5) | (refresh_clock << 4));
510 }
512 static int handle_speaker_io(
513 int dir, uint32_t port, uint32_t bytes, uint32_t *val)
514 {
515 struct PITState *vpit = vcpu_vpit(current);
517 BUG_ON(bytes != 1);
519 spin_lock(&vpit->lock);
521 if ( dir == IOREQ_WRITE )
522 speaker_ioport_write(vpit, port, *val);
523 else
524 *val = speaker_ioport_read(vpit, port);
526 spin_unlock(&vpit->lock);
528 return X86EMUL_OKAY;
529 }
531 int pv_pit_handler(int port, int data, int write)
532 {
533 ioreq_t ioreq = {
534 .size = 1,
535 .type = IOREQ_TYPE_PIO,
536 .addr = port,
537 .dir = write ? IOREQ_WRITE : IOREQ_READ,
538 .data = data
539 };
541 if ( (current->domain->domain_id == 0) && dom0_pit_access(&ioreq) )
542 {
543 /* nothing to do */;
544 }
545 else
546 {
547 uint32_t val = data;
548 if ( port == 0x61 )
549 handle_speaker_io(ioreq.dir, port, 1, &val);
550 else
551 handle_pit_io(ioreq.dir, port, 1, &val);
552 ioreq.data = val;
553 }
555 return !write ? ioreq.data : 0;
556 }