source: trunk/packages/xen-common/xen-common/xen/arch/x86/hvm/i8254.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 17 years ago

Add xen and xen-common

File size: 16.3 KB
Line 
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
52static int handle_pit_io(ioreq_t *p);
53static int handle_speaker_io(ioreq_t *p);
54
55/* compute with 96 bit intermediate result: (a*b)/c */
56uint64_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
79static 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 */
107int 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 */
141void 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
173int pit_get_gate(PITState *pit, int channel)
174{
175    return pit->hw.channels[channel].gate;
176}
177
178void 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
184static 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 == &current->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 */
237static 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
246static 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
312static 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
365void pit_stop_channel0_irq(PITState * pit)
366{
367    destroy_periodic_time(&pit->pt[0]);
368}
369
370#ifdef HVM_DEBUG_SUSPEND
371static 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
411static void pit_info(PITState *pit)
412{
413}
414#endif
415
416static 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
426static 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
448HVM_REGISTER_SAVE_RESTORE(PIT, pit_save, pit_load, 1, HVMSR_PER_DOM);
449
450static 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
465void 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
488void 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
498void 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 */ 
506static 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
530static 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
537static 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
548static 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
569int 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}
Note: See TracBrowser for help on using the repository browser.