📄 i8254.c
字号:
/* * QEMU 8253/8254 interval timer emulation * * Copyright (c) 2003-2004 Fabrice Bellard * Copyright (c) 2006 Intel Corperation * Copyright (c) 2007 Keir Fraser, XenSource Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */#include <xen/config.h>#include <xen/types.h>#include <xen/mm.h>#include <xen/xmalloc.h>#include <xen/lib.h>#include <xen/errno.h>#include <xen/sched.h>#include <asm/hvm/hvm.h>#include <asm/hvm/io.h>#include <asm/hvm/support.h>#include <asm/hvm/vpt.h>#include <asm/current.h>#define domain_vpit(d) (&(d)->arch.hvm_domain.pl_time.vpit)#define vcpu_vpit(vcpu) (domain_vpit((vcpu)->domain))#define vpit_domain(pit) (container_of((pit), struct domain, \ arch.hvm_domain.pl_time.vpit))#define vpit_vcpu(pit) (vpit_domain(pit)->vcpu[0])#define RW_STATE_LSB 1#define RW_STATE_MSB 2#define RW_STATE_WORD0 3#define RW_STATE_WORD1 4static int handle_pit_io( int dir, uint32_t port, uint32_t bytes, uint32_t *val);static int handle_speaker_io( int dir, uint32_t port, uint32_t bytes, uint32_t *val);/* Compute with 96 bit intermediate result: (a*b)/c */static uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c){ union { uint64_t ll; struct {#ifdef WORDS_BIGENDIAN uint32_t high, low;#else uint32_t low, high;#endif } l; } u, res; uint64_t rl, rh; u.ll = a; rl = (uint64_t)u.l.low * (uint64_t)b; rh = (uint64_t)u.l.high * (uint64_t)b; rh += (rl >> 32); res.l.high = rh / c; res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; return res.ll;}static int pit_get_count(PITState *pit, int channel){ uint64_t d; int counter; struct hvm_hw_pit_channel *c = &pit->hw.channels[channel]; struct vcpu *v = vpit_vcpu(pit); ASSERT(spin_is_locked(&pit->lock)); d = muldiv64(hvm_get_guest_time(v) - pit->count_load_time[channel], PIT_FREQ, ticks_per_sec(v)); switch ( c->mode ) { case 0: case 1: case 4: case 5: counter = (c->count - d) & 0xffff; break; case 3: /* XXX: may be incorrect for odd counts */ counter = c->count - ((2 * d) % c->count); break; default: counter = c->count - (d % c->count); break; } return counter;}static int pit_get_out(PITState *pit, int channel){ struct hvm_hw_pit_channel *s = &pit->hw.channels[channel]; uint64_t d; int out; struct vcpu *v = vpit_vcpu(pit); ASSERT(spin_is_locked(&pit->lock)); d = muldiv64(hvm_get_guest_time(v) - pit->count_load_time[channel], PIT_FREQ, ticks_per_sec(v)); switch ( s->mode ) { default: case 0: out = (d >= s->count); break; case 1: out = (d < s->count); break; case 2: out = (((d % s->count) == 0) && (d != 0)); break; case 3: out = ((d % s->count) < ((s->count + 1) >> 1)); break; case 4: case 5: out = (d == s->count); break; } return out;}static void pit_set_gate(PITState *pit, int channel, int val){ struct hvm_hw_pit_channel *s = &pit->hw.channels[channel]; struct vcpu *v = vpit_vcpu(pit); ASSERT(spin_is_locked(&pit->lock)); switch ( s->mode ) { default: case 0: case 4: /* XXX: just disable/enable counting */ break; case 1: case 5: case 2: case 3: /* Restart counting on rising edge. */ if ( s->gate < val ) pit->count_load_time[channel] = hvm_get_guest_time(v); break; } s->gate = val;}int pit_get_gate(PITState *pit, int channel){ ASSERT(spin_is_locked(&pit->lock)); return pit->hw.channels[channel].gate;}static void pit_time_fired(struct vcpu *v, void *priv){ uint64_t *count_load_time = priv; *count_load_time = hvm_get_guest_time(v);}static void pit_load_count(PITState *pit, int channel, int val){ u32 period; struct hvm_hw_pit_channel *s = &pit->hw.channels[channel]; struct vcpu *v = vpit_vcpu(pit); ASSERT(spin_is_locked(&pit->lock)); if ( val == 0 ) val = 0x10000; if ( v == NULL ) rdtscll(pit->count_load_time[channel]); else pit->count_load_time[channel] = hvm_get_guest_time(v); s->count = val; period = DIV_ROUND((val * 1000000000ULL), PIT_FREQ); if ( (v == NULL) || !is_hvm_vcpu(v) || (channel != 0) ) return; switch ( s->mode ) { case 2: case 3: /* Periodic timer. */ create_periodic_time(v, &pit->pt0, period, 0, 0, pit_time_fired, &pit->count_load_time[channel]); break; case 1: case 4: /* One-shot timer. */ create_periodic_time(v, &pit->pt0, period, 0, 1, pit_time_fired, &pit->count_load_time[channel]); break; default: destroy_periodic_time(&pit->pt0); break; }}static void pit_latch_count(PITState *pit, int channel){ struct hvm_hw_pit_channel *c = &pit->hw.channels[channel]; ASSERT(spin_is_locked(&pit->lock)); if ( !c->count_latched ) { c->latched_count = pit_get_count(pit, channel); c->count_latched = c->rw_mode; }}static void pit_latch_status(PITState *pit, int channel){ struct hvm_hw_pit_channel *c = &pit->hw.channels[channel]; ASSERT(spin_is_locked(&pit->lock)); if ( !c->status_latched ) { /* TODO: Return NULL COUNT (bit 6). */ c->status = ((pit_get_out(pit, channel) << 7) | (c->rw_mode << 4) | (c->mode << 1) | c->bcd); c->status_latched = 1; }}static void pit_ioport_write(struct PITState *pit, uint32_t addr, uint32_t val){ int channel, access; struct hvm_hw_pit_channel *s; val &= 0xff; addr &= 3; spin_lock(&pit->lock); if ( addr == 3 ) { channel = val >> 6; if ( channel == 3 ) { /* Read-Back Command. */ for ( channel = 0; channel < 3; channel++ ) { s = &pit->hw.channels[channel]; if ( val & (2 << channel) ) { if ( !(val & 0x20) ) pit_latch_count(pit, channel); if ( !(val & 0x10) ) pit_latch_status(pit, channel); } } } else { /* Select Counter <channel>. */ s = &pit->hw.channels[channel]; access = (val >> 4) & 3; if ( access == 0 ) { pit_latch_count(pit, channel); } else { s->rw_mode = access; s->read_state = access; s->write_state = access; s->mode = (val >> 1) & 7; if ( s->mode > 5 ) s->mode -= 4; s->bcd = val & 1; /* XXX: update irq timer ? */ } } } else { /* Write Count. */ s = &pit->hw.channels[addr]; switch ( s->write_state ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -