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>, Eddie Dong <eddie.dong@intel.com> |
---|
26 | * Ported to xen: |
---|
27 | * Add a new layer of periodic time on top of PIT; |
---|
28 | * move speaker io access to hypervisor; |
---|
29 | */ |
---|
30 | |
---|
31 | #include <xen/config.h> |
---|
32 | #include <xen/types.h> |
---|
33 | #include <xen/mm.h> |
---|
34 | #include <xen/xmalloc.h> |
---|
35 | #include <xen/lib.h> |
---|
36 | #include <xen/errno.h> |
---|
37 | #include <xen/sched.h> |
---|
38 | #include <asm/hvm/hvm.h> |
---|
39 | #include <asm/hvm/io.h> |
---|
40 | #include <asm/hvm/support.h> |
---|
41 | #include <asm/hvm/vpt.h> |
---|
42 | #include <asm/current.h> |
---|
43 | |
---|
44 | /* Enable DEBUG_PIT may cause guest calibration inaccuracy */ |
---|
45 | /* #define DEBUG_PIT */ |
---|
46 | |
---|
47 | #define RW_STATE_LSB 1 |
---|
48 | #define RW_STATE_MSB 2 |
---|
49 | #define RW_STATE_WORD0 3 |
---|
50 | #define RW_STATE_WORD1 4 |
---|
51 | |
---|
52 | static int handle_pit_io(ioreq_t *p); |
---|
53 | static int handle_speaker_io(ioreq_t *p); |
---|
54 | |
---|
55 | /* compute with 96 bit intermediate result: (a*b)/c */ |
---|
56 | uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) |
---|
57 | { |
---|
58 | union { |
---|
59 | uint64_t ll; |
---|
60 | struct { |
---|
61 | #ifdef WORDS_BIGENDIAN |
---|
62 | uint32_t high, low; |
---|
63 | #else |
---|
64 | uint32_t low, high; |
---|
65 | #endif |
---|
66 | } l; |
---|
67 | } u, res; |
---|
68 | uint64_t rl, rh; |
---|
69 | |
---|
70 | u.ll = a; |
---|
71 | rl = (uint64_t)u.l.low * (uint64_t)b; |
---|
72 | rh = (uint64_t)u.l.high * (uint64_t)b; |
---|
73 | rh += (rl >> 32); |
---|
74 | res.l.high = rh / c; |
---|
75 | res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; |
---|
76 | return res.ll; |
---|
77 | } |
---|
78 | |
---|
79 | static int pit_get_count(PITState *s, int channel) |
---|
80 | { |
---|
81 | uint64_t d; |
---|
82 | int counter; |
---|
83 | struct hvm_hw_pit_channel *c = &s->hw.channels[channel]; |
---|
84 | struct periodic_time *pt = &s->pt[channel]; |
---|
85 | |
---|
86 | d = muldiv64(hvm_get_guest_time(pt->vcpu) - s->count_load_time[channel], |
---|
87 | PIT_FREQ, ticks_per_sec(pt->vcpu)); |
---|
88 | switch(c->mode) { |
---|
89 | case 0: |
---|
90 | case 1: |
---|
91 | case 4: |
---|
92 | case 5: |
---|
93 | counter = (c->count - d) & 0xffff; |
---|
94 | break; |
---|
95 | case 3: |
---|
96 | /* XXX: may be incorrect for odd counts */ |
---|
97 | counter = c->count - ((2 * d) % c->count); |
---|
98 | break; |
---|
99 | default: |
---|
100 | counter = c->count - (d % c->count); |
---|
101 | break; |
---|
102 | } |
---|
103 | return counter; |
---|
104 | } |
---|
105 | |
---|
106 | /* get pit output bit */ |
---|
107 | int pit_get_out(PITState *pit, int channel, int64_t current_time) |
---|
108 | { |
---|
109 | struct hvm_hw_pit_channel *s = &pit->hw.channels[channel]; |
---|
110 | uint64_t d; |
---|
111 | int out; |
---|
112 | |
---|
113 | d = muldiv64(current_time - pit->count_load_time[channel], |
---|
114 | PIT_FREQ, ticks_per_sec(pit->pt[channel].vcpu)); |
---|
115 | switch(s->mode) { |
---|
116 | default: |
---|
117 | case 0: |
---|
118 | out = (d >= s->count); |
---|
119 | break; |
---|
120 | case 1: |
---|
121 | out = (d < s->count); |
---|
122 | break; |
---|
123 | case 2: |
---|
124 | if ((d % s->count) == 0 && d != 0) |
---|
125 | out = 1; |
---|
126 | else |
---|
127 | out = 0; |
---|
128 | break; |
---|
129 | case 3: |
---|
130 | out = (d % s->count) < ((s->count + 1) >> 1); |
---|
131 | break; |
---|
132 | case 4: |
---|
133 | case 5: |
---|
134 | out = (d == s->count); |
---|
135 | break; |
---|
136 | } |
---|
137 | return out; |
---|
138 | } |
---|
139 | |
---|
140 | /* val must be 0 or 1 */ |
---|
141 | void pit_set_gate(PITState *pit, int channel, int val) |
---|
142 | { |
---|
143 | struct hvm_hw_pit_channel *s = &pit->hw.channels[channel]; |
---|
144 | struct periodic_time *pt = &pit->pt[channel]; |
---|
145 | |
---|
146 | switch(s->mode) { |
---|
147 | default: |
---|
148 | case 0: |
---|
149 | case 4: |
---|
150 | /* XXX: just disable/enable counting */ |
---|
151 | break; |
---|
152 | case 1: |
---|
153 | case 5: |
---|
154 | if (s->gate < val) { |
---|
155 | /* restart counting on rising edge */ |
---|
156 | pit->count_load_time[channel] = hvm_get_guest_time(pt->vcpu); |
---|
157 | // pit_irq_timer_update(s, s->count_load_time); |
---|
158 | } |
---|
159 | break; |
---|
160 | case 2: |
---|
161 | case 3: |
---|
162 | if (s->gate < val) { |
---|
163 | /* restart counting on rising edge */ |
---|
164 | pit->count_load_time[channel] = hvm_get_guest_time(pt->vcpu); |
---|
165 | // pit_irq_timer_update(s, s->count_load_time); |
---|
166 | } |
---|
167 | /* XXX: disable/enable counting */ |
---|
168 | break; |
---|
169 | } |
---|
170 | s->gate = val; |
---|
171 | } |
---|
172 | |
---|
173 | int pit_get_gate(PITState *pit, int channel) |
---|
174 | { |
---|
175 | return pit->hw.channels[channel].gate; |
---|
176 | } |
---|
177 | |
---|
178 | 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 | } |
---|
183 | |
---|
184 | static inline 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 periodic_time *pt = &pit->pt[channel]; |
---|
189 | struct vcpu *v; |
---|
190 | |
---|
191 | if (val == 0) |
---|
192 | val = 0x10000; |
---|
193 | pit->count_load_time[channel] = hvm_get_guest_time(pt->vcpu); |
---|
194 | s->count = val; |
---|
195 | period = DIV_ROUND((val * 1000000000ULL), PIT_FREQ); |
---|
196 | |
---|
197 | if (channel != 0) |
---|
198 | return; |
---|
199 | |
---|
200 | #ifdef DEBUG_PIT |
---|
201 | printk("HVM_PIT: pit-load-counter(%p), count=0x%x, period=%uns mode=%d, load_time=%lld\n", |
---|
202 | s, |
---|
203 | val, |
---|
204 | period, |
---|
205 | s->mode, |
---|
206 | (long long)pit->count_load_time[channel]); |
---|
207 | #endif |
---|
208 | |
---|
209 | /* Choose a vcpu to set the timer on: current if appropriate else vcpu 0 */ |
---|
210 | if ( likely(pit == ¤t->domain->arch.hvm_domain.pl_time.vpit) ) |
---|
211 | v = current; |
---|
212 | else |
---|
213 | v = container_of(pit, struct domain, |
---|
214 | arch.hvm_domain.pl_time.vpit)->vcpu[0]; |
---|
215 | |
---|
216 | switch (s->mode) { |
---|
217 | case 2: |
---|
218 | /* create periodic time */ |
---|
219 | create_periodic_time(v, pt, period, 0, 0, pit_time_fired, |
---|
220 | &pit->count_load_time[channel]); |
---|
221 | break; |
---|
222 | case 1: |
---|
223 | /* create one shot time */ |
---|
224 | create_periodic_time(v, pt, period, 0, 1, pit_time_fired, |
---|
225 | &pit->count_load_time[channel]); |
---|
226 | #ifdef DEBUG_PIT |
---|
227 | printk("HVM_PIT: create one shot time.\n"); |
---|
228 | #endif |
---|
229 | break; |
---|
230 | default: |
---|
231 | destroy_periodic_time(pt); |
---|
232 | break; |
---|
233 | } |
---|
234 | } |
---|
235 | |
---|
236 | /* if already latched, do not latch again */ |
---|
237 | static void pit_latch_count(PITState *s, int channel) |
---|
238 | { |
---|
239 | struct hvm_hw_pit_channel *c = &s->hw.channels[channel]; |
---|
240 | if (!c->count_latched) { |
---|
241 | c->latched_count = pit_get_count(s, channel); |
---|
242 | c->count_latched = c->rw_mode; |
---|
243 | } |
---|
244 | } |
---|
245 | |
---|
246 | static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
---|
247 | { |
---|
248 | PITState *pit = opaque; |
---|
249 | int channel, access; |
---|
250 | struct hvm_hw_pit_channel *s; |
---|
251 | val &= 0xff; |
---|
252 | |
---|
253 | addr &= 3; |
---|
254 | if (addr == 3) { |
---|
255 | channel = val >> 6; |
---|
256 | if (channel == 3) { |
---|
257 | /* read back command */ |
---|
258 | for(channel = 0; channel < 3; channel++) { |
---|
259 | s = &pit->hw.channels[channel]; |
---|
260 | if (val & (2 << channel)) { |
---|
261 | if (!(val & 0x20)) { |
---|
262 | pit_latch_count(pit, channel); |
---|
263 | } |
---|
264 | if (!(val & 0x10) && !s->status_latched) { |
---|
265 | /* status latch */ |
---|
266 | /* XXX: add BCD and null count */ |
---|
267 | s->status = (pit_get_out(pit, channel, hvm_get_guest_time(pit->pt[channel].vcpu)) << 7) | |
---|
268 | (s->rw_mode << 4) | |
---|
269 | (s->mode << 1) | |
---|
270 | s->bcd; |
---|
271 | s->status_latched = 1; |
---|
272 | } |
---|
273 | } |
---|
274 | } |
---|
275 | } else { |
---|
276 | s = &pit->hw.channels[channel]; |
---|
277 | access = (val >> 4) & 3; |
---|
278 | if (access == 0) { |
---|
279 | pit_latch_count(pit, channel); |
---|
280 | } else { |
---|
281 | s->rw_mode = access; |
---|
282 | s->read_state = access; |
---|
283 | s->write_state = access; |
---|
284 | |
---|
285 | s->mode = (val >> 1) & 7; |
---|
286 | s->bcd = val & 1; |
---|
287 | /* XXX: update irq timer ? */ |
---|
288 | } |
---|
289 | } |
---|
290 | } else { |
---|
291 | s = &pit->hw.channels[addr]; |
---|
292 | switch(s->write_state) { |
---|
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 | } |
---|
310 | } |
---|
311 | |
---|
312 | static uint32_t pit_ioport_read(void *opaque, uint32_t addr) |
---|
313 | { |
---|
314 | PITState *pit = opaque; |
---|
315 | int ret, count; |
---|
316 | struct hvm_hw_pit_channel *s; |
---|
317 | |
---|
318 | addr &= 3; |
---|
319 | s = &pit->hw.channels[addr]; |
---|
320 | if (s->status_latched) { |
---|
321 | s->status_latched = 0; |
---|
322 | ret = s->status; |
---|
323 | } else if (s->count_latched) { |
---|
324 | switch(s->count_latched) { |
---|
325 | default: |
---|
326 | case RW_STATE_LSB: |
---|
327 | ret = s->latched_count & 0xff; |
---|
328 | s->count_latched = 0; |
---|
329 | break; |
---|
330 | case RW_STATE_MSB: |
---|
331 | ret = s->latched_count >> 8; |
---|
332 | s->count_latched = 0; |
---|
333 | break; |
---|
334 | case RW_STATE_WORD0: |
---|
335 | ret = s->latched_count & 0xff; |
---|
336 | s->count_latched = RW_STATE_MSB; |
---|
337 | break; |
---|
338 | } |
---|
339 | } else { |
---|
340 | switch(s->read_state) { |
---|
341 | default: |
---|
342 | case RW_STATE_LSB: |
---|
343 | count = pit_get_count(pit, addr); |
---|
344 | ret = count & 0xff; |
---|
345 | break; |
---|
346 | case RW_STATE_MSB: |
---|
347 | count = pit_get_count(pit, addr); |
---|
348 | ret = (count >> 8) & 0xff; |
---|
349 | break; |
---|
350 | case RW_STATE_WORD0: |
---|
351 | count = pit_get_count(pit, addr); |
---|
352 | ret = count & 0xff; |
---|
353 | s->read_state = RW_STATE_WORD1; |
---|
354 | break; |
---|
355 | case RW_STATE_WORD1: |
---|
356 | count = pit_get_count(pit, addr); |
---|
357 | ret = (count >> 8) & 0xff; |
---|
358 | s->read_state = RW_STATE_WORD0; |
---|
359 | break; |
---|
360 | } |
---|
361 | } |
---|
362 | return ret; |
---|
363 | } |
---|
364 | |
---|
365 | void pit_stop_channel0_irq(PITState * pit) |
---|
366 | { |
---|
367 | destroy_periodic_time(&pit->pt[0]); |
---|
368 | } |
---|
369 | |
---|
370 | #ifdef HVM_DEBUG_SUSPEND |
---|
371 | static void pit_info(PITState *pit) |
---|
372 | { |
---|
373 | struct hvm_hw_pit_channel *s; |
---|
374 | struct periodic_time *pt; |
---|
375 | int i; |
---|
376 | |
---|
377 | for(i = 0; i < 3; i++) { |
---|
378 | printk("*****pit channel %d's state:*****\n", i); |
---|
379 | s = &pit->hw.channels[i]; |
---|
380 | printk("pit 0x%x.\n", s->count); |
---|
381 | printk("pit 0x%x.\n", s->latched_count); |
---|
382 | printk("pit 0x%x.\n", s->count_latched); |
---|
383 | printk("pit 0x%x.\n", s->status_latched); |
---|
384 | printk("pit 0x%x.\n", s->status); |
---|
385 | printk("pit 0x%x.\n", s->read_state); |
---|
386 | printk("pit 0x%x.\n", s->write_state); |
---|
387 | printk("pit 0x%x.\n", s->write_latch); |
---|
388 | printk("pit 0x%x.\n", s->rw_mode); |
---|
389 | printk("pit 0x%x.\n", s->mode); |
---|
390 | printk("pit 0x%x.\n", s->bcd); |
---|
391 | printk("pit 0x%x.\n", s->gate); |
---|
392 | printk("pit %"PRId64"\n", pit->count_load_time[i]); |
---|
393 | |
---|
394 | pt = &pit->pt[i]; |
---|
395 | if (pt) { |
---|
396 | printk("pit channel %d has a periodic timer:\n", i); |
---|
397 | printk("pt %d.\n", pt->enabled); |
---|
398 | printk("pt %d.\n", pt->one_shot); |
---|
399 | printk("pt %d.\n", pt->irq); |
---|
400 | printk("pt %d.\n", pt->first_injected); |
---|
401 | |
---|
402 | printk("pt %d.\n", pt->pending_intr_nr); |
---|
403 | printk("pt %d.\n", pt->period); |
---|
404 | printk("pt %"PRId64"\n", pt->period_cycles); |
---|
405 | printk("pt %"PRId64"\n", pt->last_plt_gtime); |
---|
406 | } |
---|
407 | } |
---|
408 | |
---|
409 | } |
---|
410 | #else |
---|
411 | static void pit_info(PITState *pit) |
---|
412 | { |
---|
413 | } |
---|
414 | #endif |
---|
415 | |
---|
416 | static int pit_save(struct domain *d, hvm_domain_context_t *h) |
---|
417 | { |
---|
418 | PITState *pit = &d->arch.hvm_domain.pl_time.vpit; |
---|
419 | |
---|
420 | pit_info(pit); |
---|
421 | |
---|
422 | /* Save the PIT hardware state */ |
---|
423 | return hvm_save_entry(PIT, 0, h, &pit->hw); |
---|
424 | } |
---|
425 | |
---|
426 | static int pit_load(struct domain *d, hvm_domain_context_t *h) |
---|
427 | { |
---|
428 | PITState *pit = &d->arch.hvm_domain.pl_time.vpit; |
---|
429 | int i; |
---|
430 | |
---|
431 | /* Restore the PIT hardware state */ |
---|
432 | if ( hvm_load_entry(PIT, h, &pit->hw) ) |
---|
433 | return 1; |
---|
434 | |
---|
435 | /* Recreate platform timers from hardware state. There will be some |
---|
436 | * time jitter here, but the wall-clock will have jumped massively, so |
---|
437 | * we hope the guest can handle it. */ |
---|
438 | |
---|
439 | for(i = 0; i < 3; i++) { |
---|
440 | pit_load_count(pit, i, pit->hw.channels[i].count); |
---|
441 | pit->pt[i].last_plt_gtime = hvm_get_guest_time(d->vcpu[0]); |
---|
442 | } |
---|
443 | |
---|
444 | pit_info(pit); |
---|
445 | return 0; |
---|
446 | } |
---|
447 | |
---|
448 | HVM_REGISTER_SAVE_RESTORE(PIT, pit_save, pit_load, 1, HVMSR_PER_DOM); |
---|
449 | |
---|
450 | static void pit_reset(void *opaque) |
---|
451 | { |
---|
452 | PITState *pit = opaque; |
---|
453 | struct hvm_hw_pit_channel *s; |
---|
454 | int i; |
---|
455 | |
---|
456 | for(i = 0;i < 3; i++) { |
---|
457 | s = &pit->hw.channels[i]; |
---|
458 | destroy_periodic_time(&pit->pt[i]); |
---|
459 | s->mode = 0xff; /* the init mode */ |
---|
460 | s->gate = (i != 2); |
---|
461 | pit_load_count(pit, i, 0); |
---|
462 | } |
---|
463 | } |
---|
464 | |
---|
465 | void pit_init(struct vcpu *v, unsigned long cpu_khz) |
---|
466 | { |
---|
467 | PITState *pit = &v->domain->arch.hvm_domain.pl_time.vpit; |
---|
468 | struct periodic_time *pt; |
---|
469 | |
---|
470 | pt = &pit->pt[0]; |
---|
471 | pt->vcpu = v; |
---|
472 | /* the timer 0 is connected to an IRQ */ |
---|
473 | init_timer(&pt->timer, pt_timer_fn, pt, v->processor); |
---|
474 | pt++; pt->vcpu = v; |
---|
475 | pt++; pt->vcpu = v; |
---|
476 | |
---|
477 | register_portio_handler(v->domain, PIT_BASE, 4, handle_pit_io); |
---|
478 | /* register the speaker port */ |
---|
479 | register_portio_handler(v->domain, 0x61, 1, handle_speaker_io); |
---|
480 | ticks_per_sec(v) = cpu_khz * (int64_t)1000; |
---|
481 | #ifdef DEBUG_PIT |
---|
482 | printk("HVM_PIT: guest frequency =%lld\n", (long long)ticks_per_sec(v)); |
---|
483 | #endif |
---|
484 | pit_reset(pit); |
---|
485 | return; |
---|
486 | } |
---|
487 | |
---|
488 | void pit_migrate_timers(struct vcpu *v) |
---|
489 | { |
---|
490 | PITState *pit = &v->domain->arch.hvm_domain.pl_time.vpit; |
---|
491 | struct periodic_time *pt; |
---|
492 | |
---|
493 | pt = &pit->pt[0]; |
---|
494 | if ( pt->vcpu == v && pt->enabled ) |
---|
495 | migrate_timer(&pt->timer, v->processor); |
---|
496 | } |
---|
497 | |
---|
498 | void pit_deinit(struct domain *d) |
---|
499 | { |
---|
500 | PITState *pit = &d->arch.hvm_domain.pl_time.vpit; |
---|
501 | |
---|
502 | kill_timer(&pit->pt[0].timer); |
---|
503 | } |
---|
504 | |
---|
505 | /* the intercept action for PIT DM retval:0--not handled; 1--handled */ |
---|
506 | static int handle_pit_io(ioreq_t *p) |
---|
507 | { |
---|
508 | struct vcpu *v = current; |
---|
509 | struct PITState *vpit = &(v->domain->arch.hvm_domain.pl_time.vpit); |
---|
510 | |
---|
511 | if (p->size != 1 || |
---|
512 | p->data_is_ptr || |
---|
513 | p->type != IOREQ_TYPE_PIO){ |
---|
514 | printk("HVM_PIT:wrong PIT IO!\n"); |
---|
515 | return 1; |
---|
516 | } |
---|
517 | |
---|
518 | if (p->dir == 0) {/* write */ |
---|
519 | pit_ioport_write(vpit, p->addr, p->data); |
---|
520 | } else if (p->dir == 1) { /* read */ |
---|
521 | if ( (p->addr & 3) != 3 ) { |
---|
522 | p->data = pit_ioport_read(vpit, p->addr); |
---|
523 | } else { |
---|
524 | printk("HVM_PIT: read A1:A0=3!\n"); |
---|
525 | } |
---|
526 | } |
---|
527 | return 1; |
---|
528 | } |
---|
529 | |
---|
530 | static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
---|
531 | { |
---|
532 | PITState *pit = opaque; |
---|
533 | pit->hw.speaker_data_on = (val >> 1) & 1; |
---|
534 | pit_set_gate(pit, 2, val & 1); |
---|
535 | } |
---|
536 | |
---|
537 | static uint32_t speaker_ioport_read(void *opaque, uint32_t addr) |
---|
538 | { |
---|
539 | PITState *pit = opaque; |
---|
540 | int out = pit_get_out(pit, 2, |
---|
541 | hvm_get_guest_time(pit->pt[2].vcpu)); |
---|
542 | /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */ |
---|
543 | unsigned int refresh_clock = ((unsigned int)NOW() >> 14) & 1; |
---|
544 | return ((pit->hw.speaker_data_on << 1) | pit_get_gate(pit, 2) | |
---|
545 | (out << 5) | refresh_clock << 4); |
---|
546 | } |
---|
547 | |
---|
548 | static int handle_speaker_io(ioreq_t *p) |
---|
549 | { |
---|
550 | struct vcpu *v = current; |
---|
551 | struct PITState *vpit = &(v->domain->arch.hvm_domain.pl_time.vpit); |
---|
552 | |
---|
553 | if (p->size != 1 || |
---|
554 | p->data_is_ptr || |
---|
555 | p->type != IOREQ_TYPE_PIO){ |
---|
556 | printk("HVM_SPEAKER:wrong SPEAKER IO!\n"); |
---|
557 | return 1; |
---|
558 | } |
---|
559 | |
---|
560 | if (p->dir == 0) {/* write */ |
---|
561 | speaker_ioport_write(vpit, p->addr, p->data); |
---|
562 | } else if (p->dir == 1) {/* read */ |
---|
563 | p->data = speaker_ioport_read(vpit, p->addr); |
---|
564 | } |
---|
565 | |
---|
566 | return 1; |
---|
567 | } |
---|
568 | |
---|
569 | int pv_pit_handler(int port, int data, int write) |
---|
570 | { |
---|
571 | ioreq_t ioreq = { |
---|
572 | .size = 1, |
---|
573 | .type = IOREQ_TYPE_PIO, |
---|
574 | .addr = port, |
---|
575 | .dir = write ? 0 : 1, |
---|
576 | .data = write ? data : 0, |
---|
577 | }; |
---|
578 | |
---|
579 | if (port == 0x61) |
---|
580 | handle_speaker_io(&ioreq); |
---|
581 | else |
---|
582 | handle_pit_io(&ioreq); |
---|
583 | |
---|
584 | return !write ? ioreq.data : 0; |
---|
585 | } |
---|