[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen master] x86: PIT emulation is common to both PV and HVM
commit 833a81b439912498e2f554560e296eb6abcf3de7 Author: Wei Liu <wei.liu2@xxxxxxxxxx> AuthorDate: Tue Sep 4 17:15:22 2018 +0100 Commit: Wei Liu <wei.liu2@xxxxxxxxxx> CommitDate: Thu Sep 6 16:54:38 2018 +0100 x86: PIT emulation is common to both PV and HVM Move the file to x86 common code and change its name to emul-i8254.c. Put HVM only code under CONFIG_HVM or is_hvm_domain. Signed-off-by: Wei Liu <wei.liu2@xxxxxxxxxx> Acked-by: Jan Beulich <jbeulich@xxxxxxxx> --- xen/arch/x86/Makefile | 1 + xen/arch/x86/emul-i8254.c | 605 ++++++++++++++++++++++++++++++++++++++++++++++ xen/arch/x86/hvm/Makefile | 1 - xen/arch/x86/hvm/i8254.c | 597 --------------------------------------------- 4 files changed, 606 insertions(+), 598 deletions(-) diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile index 2f2ad3adfd..162b0b94c0 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -27,6 +27,7 @@ obj-y += domain.o obj-bin-y += dom0_build.init.o obj-y += domain_page.o obj-y += e820.o +obj-y += emul-i8254.o obj-y += extable.o obj-y += flushtlb.o obj-$(CONFIG_CRASH_DEBUG) += gdbstub.o diff --git a/xen/arch/x86/emul-i8254.c b/xen/arch/x86/emul-i8254.c new file mode 100644 index 0000000000..7f1ded2623 --- /dev/null +++ b/xen/arch/x86/emul-i8254.c @@ -0,0 +1,605 @@ +/* + * 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/types.h> +#include <xen/mm.h> +#include <xen/xmalloc.h> +#include <xen/lib.h> +#include <xen/errno.h> +#include <xen/sched.h> +#include <xen/trace.h> +#include <asm/time.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(x) (&(x)->arch.vpit) +#define vcpu_vpit(x) (domain_vpit((x)->domain)) +#define vpit_domain(x) (container_of((x), struct domain, arch.vpit)) +#define vpit_vcpu(x) (pt_global_vcpu_target(vpit_domain(x))) + +#define RW_STATE_LSB 1 +#define RW_STATE_MSB 2 +#define RW_STATE_WORD0 3 +#define RW_STATE_WORD1 4 + +static int handle_pit_io( + int dir, unsigned int port, unsigned int bytes, uint32_t *val); +static int handle_speaker_io( + int dir, unsigned int port, unsigned int bytes, uint32_t *val); + +#define get_guest_time(v) \ + (is_hvm_vcpu(v) ? hvm_get_guest_time(v) : (u64)get_s_time()) + +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(get_guest_time(v) - pit->count_load_time[channel], + PIT_FREQ, SYSTEM_TIME_HZ); + + 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(get_guest_time(v) - pit->count_load_time[channel], + PIT_FREQ, SYSTEM_TIME_HZ); + + 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] = get_guest_time(v); + break; + } + + s->gate = val; +} + +static 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; + TRACE_0D(TRC_HVM_EMUL_PIT_TIMER_CB); + *count_load_time = 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 ) + pit->count_load_time[channel] = 0; + else + pit->count_load_time[channel] = get_guest_time(v); + s->count = val; + period = DIV_ROUND(val * SYSTEM_TIME_HZ, PIT_FREQ); + + if ( (v == NULL) || !is_hvm_vcpu(v) || (channel != 0) ) + return; + + switch ( s->mode ) + { + case 2: + case 3: + /* Periodic timer. */ + TRACE_2D(TRC_HVM_EMUL_PIT_START_TIMER, period, period); + create_periodic_time(v, &pit->pt0, period, period, 0, pit_time_fired, + &pit->count_load_time[channel], false); + break; + case 1: + case 4: + /* One-shot timer. */ + TRACE_2D(TRC_HVM_EMUL_PIT_START_TIMER, period, 0); + create_periodic_time(v, &pit->pt0, period, 0, 0, pit_time_fired, + &pit->count_load_time[channel], false); + break; + default: + TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER); + 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 ) + { + default: + case RW_STATE_LSB: + pit_load_count(pit, addr, val); + break; + case RW_STATE_MSB: + pit_load_count(pit, addr, val << 8); + break; + case RW_STATE_WORD0: + s->write_latch = val; + s->write_state = RW_STATE_WORD1; + break; + case RW_STATE_WORD1: + pit_load_count(pit, addr, s->write_latch | (val << 8)); + s->write_state = RW_STATE_WORD0; + break; + } + } + + spin_unlock(&pit->lock); +} + +static uint32_t pit_ioport_read(struct PITState *pit, uint32_t addr) +{ + int ret, count; + struct hvm_hw_pit_channel *s; + + addr &= 3; + s = &pit->hw.channels[addr]; + + spin_lock(&pit->lock); + + if ( s->status_latched ) + { + s->status_latched = 0; + ret = s->status; + } + else if ( s->count_latched ) + { + switch ( s->count_latched ) + { + default: + case RW_STATE_LSB: + ret = s->latched_count & 0xff; + s->count_latched = 0; + break; + case RW_STATE_MSB: + ret = s->latched_count >> 8; + s->count_latched = 0; + break; + case RW_STATE_WORD0: + ret = s->latched_count & 0xff; + s->count_latched = RW_STATE_MSB; + break; + } + } + else + { + switch ( s->read_state ) + { + default: + case RW_STATE_LSB: + count = pit_get_count(pit, addr); + ret = count & 0xff; + break; + case RW_STATE_MSB: + count = pit_get_count(pit, addr); + ret = (count >> 8) & 0xff; + break; + case RW_STATE_WORD0: + count = pit_get_count(pit, addr); + ret = count & 0xff; + s->read_state = RW_STATE_WORD1; + break; + case RW_STATE_WORD1: + count = pit_get_count(pit, addr); + ret = (count >> 8) & 0xff; + s->read_state = RW_STATE_WORD0; + break; + } + } + + spin_unlock(&pit->lock); + + return ret; +} + +#ifdef CONFIG_HVM +void pit_stop_channel0_irq(PITState *pit) +{ + if ( !has_vpit(current->domain) ) + return; + + TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER); + spin_lock(&pit->lock); + destroy_periodic_time(&pit->pt0); + spin_unlock(&pit->lock); +} + +static int pit_save(struct domain *d, hvm_domain_context_t *h) +{ + PITState *pit = domain_vpit(d); + int rc; + + if ( !has_vpit(d) ) + return 0; + + spin_lock(&pit->lock); + + rc = hvm_save_entry(PIT, 0, h, &pit->hw); + + spin_unlock(&pit->lock); + + return rc; +} + +static int pit_load(struct domain *d, hvm_domain_context_t *h) +{ + PITState *pit = domain_vpit(d); + int i; + + if ( !has_vpit(d) ) + return -ENODEV; + + spin_lock(&pit->lock); + + if ( hvm_load_entry(PIT, h, &pit->hw) ) + { + spin_unlock(&pit->lock); + return 1; + } + + /* + * Recreate platform timers from hardware state. There will be some + * time jitter here, but the wall-clock will have jumped massively, so + * we hope the guest can handle it. + */ + pit->pt0.last_plt_gtime = get_guest_time(d->vcpu[0]); + for ( i = 0; i < 3; i++ ) + pit_load_count(pit, i, pit->hw.channels[i].count); + + spin_unlock(&pit->lock); + + return 0; +} + +HVM_REGISTER_SAVE_RESTORE(PIT, pit_save, pit_load, 1, HVMSR_PER_DOM); +#endif + +void pit_reset(struct domain *d) +{ + PITState *pit = domain_vpit(d); + struct hvm_hw_pit_channel *s; + int i; + + if ( !has_vpit(d) ) + return; + + if ( is_hvm_domain(d) ) + { + TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER); + destroy_periodic_time(&pit->pt0); + pit->pt0.source = PTSRC_isa; + } + + spin_lock(&pit->lock); + + for ( i = 0; i < 3; i++ ) + { + s = &pit->hw.channels[i]; + s->mode = 0xff; /* the init mode */ + s->gate = (i != 2); + pit_load_count(pit, i, 0); + } + + spin_unlock(&pit->lock); +} + +void pit_init(struct domain *d, unsigned long cpu_khz) +{ + PITState *pit = domain_vpit(d); + + if ( !has_vpit(d) ) + return; + + spin_lock_init(&pit->lock); + + if ( is_hvm_domain(d) ) + { + register_portio_handler(d, PIT_BASE, 4, handle_pit_io); + register_portio_handler(d, 0x61, 1, handle_speaker_io); + } + + pit_reset(d); +} + +void pit_deinit(struct domain *d) +{ + PITState *pit = domain_vpit(d); + + if ( !has_vpit(d) ) + return; + + if ( is_hvm_domain(d) ) + { + TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER); + destroy_periodic_time(&pit->pt0); + } +} + +/* the intercept action for PIT DM retval:0--not handled; 1--handled */ +static int handle_pit_io( + int dir, unsigned int port, unsigned int bytes, uint32_t *val) +{ + struct PITState *vpit = vcpu_vpit(current); + + if ( bytes != 1 ) + { + gdprintk(XENLOG_WARNING, "PIT bad access\n"); + *val = ~0; + return X86EMUL_OKAY; + } + + if ( dir == IOREQ_WRITE ) + { + pit_ioport_write(vpit, port, *val); + } + else + { + if ( (port & 3) != 3 ) + *val = pit_ioport_read(vpit, port); + else + gdprintk(XENLOG_WARNING, "PIT: read A1:A0=3!\n"); + } + + return X86EMUL_OKAY; +} + +static void speaker_ioport_write( + struct PITState *pit, uint32_t addr, uint32_t val) +{ + pit->hw.speaker_data_on = (val >> 1) & 1; + pit_set_gate(pit, 2, val & 1); +} + +static uint32_t speaker_ioport_read( + struct PITState *pit, uint32_t addr) +{ + /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */ + unsigned int refresh_clock = ((unsigned int)NOW() >> 14) & 1; + return ((pit->hw.speaker_data_on << 1) | pit_get_gate(pit, 2) | + (pit_get_out(pit, 2) << 5) | (refresh_clock << 4)); +} + +static int handle_speaker_io( + int dir, unsigned int port, uint32_t bytes, uint32_t *val) +{ + struct PITState *vpit = vcpu_vpit(current); + + BUG_ON(bytes != 1); + + spin_lock(&vpit->lock); + + if ( dir == IOREQ_WRITE ) + speaker_ioport_write(vpit, port, *val); + else + *val = speaker_ioport_read(vpit, port); + + spin_unlock(&vpit->lock); + + return X86EMUL_OKAY; +} + +int pv_pit_handler(int port, int data, int write) +{ + ioreq_t ioreq = { + .size = 1, + .type = IOREQ_TYPE_PIO, + .addr = port, + .dir = write ? IOREQ_WRITE : IOREQ_READ, + .data = data + }; + + if ( !has_vpit(current->domain) ) + return ~0; + + if ( is_hardware_domain(current->domain) && hwdom_pit_access(&ioreq) ) + { + /* nothing to do */; + } + else + { + uint32_t val = data; + if ( port == 0x61 ) + handle_speaker_io(ioreq.dir, port, 1, &val); + else + handle_pit_io(ioreq.dir, port, 1, &val); + ioreq.data = val; + } + + return !write ? ioreq.data : 0; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/x86/hvm/Makefile b/xen/arch/x86/hvm/Makefile index 5bd38f633f..5e04bc1429 100644 --- a/xen/arch/x86/hvm/Makefile +++ b/xen/arch/x86/hvm/Makefile @@ -10,7 +10,6 @@ obj-y += grant_table.o obj-y += hpet.o obj-y += hvm.o obj-y += hypercall.o -obj-y += i8254.o obj-y += intercept.o obj-y += io.o obj-y += ioreq.o diff --git a/xen/arch/x86/hvm/i8254.c b/xen/arch/x86/hvm/i8254.c deleted file mode 100644 index b8ec56f8d3..0000000000 --- a/xen/arch/x86/hvm/i8254.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - * 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/types.h> -#include <xen/mm.h> -#include <xen/xmalloc.h> -#include <xen/lib.h> -#include <xen/errno.h> -#include <xen/sched.h> -#include <xen/trace.h> -#include <asm/time.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(x) (&(x)->arch.vpit) -#define vcpu_vpit(x) (domain_vpit((x)->domain)) -#define vpit_domain(x) (container_of((x), struct domain, arch.vpit)) -#define vpit_vcpu(x) (pt_global_vcpu_target(vpit_domain(x))) - -#define RW_STATE_LSB 1 -#define RW_STATE_MSB 2 -#define RW_STATE_WORD0 3 -#define RW_STATE_WORD1 4 - -static int handle_pit_io( - int dir, unsigned int port, unsigned int bytes, uint32_t *val); -static int handle_speaker_io( - int dir, unsigned int port, unsigned int bytes, uint32_t *val); - -#define get_guest_time(v) \ - (is_hvm_vcpu(v) ? hvm_get_guest_time(v) : (u64)get_s_time()) - -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(get_guest_time(v) - pit->count_load_time[channel], - PIT_FREQ, SYSTEM_TIME_HZ); - - 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(get_guest_time(v) - pit->count_load_time[channel], - PIT_FREQ, SYSTEM_TIME_HZ); - - 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] = get_guest_time(v); - break; - } - - s->gate = val; -} - -static 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; - TRACE_0D(TRC_HVM_EMUL_PIT_TIMER_CB); - *count_load_time = 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 ) - pit->count_load_time[channel] = 0; - else - pit->count_load_time[channel] = get_guest_time(v); - s->count = val; - period = DIV_ROUND(val * SYSTEM_TIME_HZ, PIT_FREQ); - - if ( (v == NULL) || !is_hvm_vcpu(v) || (channel != 0) ) - return; - - switch ( s->mode ) - { - case 2: - case 3: - /* Periodic timer. */ - TRACE_2D(TRC_HVM_EMUL_PIT_START_TIMER, period, period); - create_periodic_time(v, &pit->pt0, period, period, 0, pit_time_fired, - &pit->count_load_time[channel], false); - break; - case 1: - case 4: - /* One-shot timer. */ - TRACE_2D(TRC_HVM_EMUL_PIT_START_TIMER, period, 0); - create_periodic_time(v, &pit->pt0, period, 0, 0, pit_time_fired, - &pit->count_load_time[channel], false); - break; - default: - TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER); - 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 ) - { - default: - case RW_STATE_LSB: - pit_load_count(pit, addr, val); - break; - case RW_STATE_MSB: - pit_load_count(pit, addr, val << 8); - break; - case RW_STATE_WORD0: - s->write_latch = val; - s->write_state = RW_STATE_WORD1; - break; - case RW_STATE_WORD1: - pit_load_count(pit, addr, s->write_latch | (val << 8)); - s->write_state = RW_STATE_WORD0; - break; - } - } - - spin_unlock(&pit->lock); -} - -static uint32_t pit_ioport_read(struct PITState *pit, uint32_t addr) -{ - int ret, count; - struct hvm_hw_pit_channel *s; - - addr &= 3; - s = &pit->hw.channels[addr]; - - spin_lock(&pit->lock); - - if ( s->status_latched ) - { - s->status_latched = 0; - ret = s->status; - } - else if ( s->count_latched ) - { - switch ( s->count_latched ) - { - default: - case RW_STATE_LSB: - ret = s->latched_count & 0xff; - s->count_latched = 0; - break; - case RW_STATE_MSB: - ret = s->latched_count >> 8; - s->count_latched = 0; - break; - case RW_STATE_WORD0: - ret = s->latched_count & 0xff; - s->count_latched = RW_STATE_MSB; - break; - } - } - else - { - switch ( s->read_state ) - { - default: - case RW_STATE_LSB: - count = pit_get_count(pit, addr); - ret = count & 0xff; - break; - case RW_STATE_MSB: - count = pit_get_count(pit, addr); - ret = (count >> 8) & 0xff; - break; - case RW_STATE_WORD0: - count = pit_get_count(pit, addr); - ret = count & 0xff; - s->read_state = RW_STATE_WORD1; - break; - case RW_STATE_WORD1: - count = pit_get_count(pit, addr); - ret = (count >> 8) & 0xff; - s->read_state = RW_STATE_WORD0; - break; - } - } - - spin_unlock(&pit->lock); - - return ret; -} - -void pit_stop_channel0_irq(PITState *pit) -{ - if ( !has_vpit(current->domain) ) - return; - - TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER); - spin_lock(&pit->lock); - destroy_periodic_time(&pit->pt0); - spin_unlock(&pit->lock); -} - -static int pit_save(struct domain *d, hvm_domain_context_t *h) -{ - PITState *pit = domain_vpit(d); - int rc; - - if ( !has_vpit(d) ) - return 0; - - spin_lock(&pit->lock); - - rc = hvm_save_entry(PIT, 0, h, &pit->hw); - - spin_unlock(&pit->lock); - - return rc; -} - -static int pit_load(struct domain *d, hvm_domain_context_t *h) -{ - PITState *pit = domain_vpit(d); - int i; - - if ( !has_vpit(d) ) - return -ENODEV; - - spin_lock(&pit->lock); - - if ( hvm_load_entry(PIT, h, &pit->hw) ) - { - spin_unlock(&pit->lock); - return 1; - } - - /* - * Recreate platform timers from hardware state. There will be some - * time jitter here, but the wall-clock will have jumped massively, so - * we hope the guest can handle it. - */ - pit->pt0.last_plt_gtime = get_guest_time(d->vcpu[0]); - for ( i = 0; i < 3; i++ ) - pit_load_count(pit, i, pit->hw.channels[i].count); - - spin_unlock(&pit->lock); - - return 0; -} - -HVM_REGISTER_SAVE_RESTORE(PIT, pit_save, pit_load, 1, HVMSR_PER_DOM); - -void pit_reset(struct domain *d) -{ - PITState *pit = domain_vpit(d); - struct hvm_hw_pit_channel *s; - int i; - - if ( !has_vpit(d) ) - return; - - TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER); - destroy_periodic_time(&pit->pt0); - pit->pt0.source = PTSRC_isa; - - spin_lock(&pit->lock); - - for ( i = 0; i < 3; i++ ) - { - s = &pit->hw.channels[i]; - s->mode = 0xff; /* the init mode */ - s->gate = (i != 2); - pit_load_count(pit, i, 0); - } - - spin_unlock(&pit->lock); -} - -void pit_init(struct domain *d, unsigned long cpu_khz) -{ - PITState *pit = domain_vpit(d); - - if ( !has_vpit(d) ) - return; - - spin_lock_init(&pit->lock); - - if ( is_hvm_domain(d) ) - { - register_portio_handler(d, PIT_BASE, 4, handle_pit_io); - register_portio_handler(d, 0x61, 1, handle_speaker_io); - } - - pit_reset(d); -} - -void pit_deinit(struct domain *d) -{ - PITState *pit = domain_vpit(d); - - if ( !has_vpit(d) ) - return; - - TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER); - destroy_periodic_time(&pit->pt0); -} - -/* the intercept action for PIT DM retval:0--not handled; 1--handled */ -static int handle_pit_io( - int dir, unsigned int port, unsigned int bytes, uint32_t *val) -{ - struct PITState *vpit = vcpu_vpit(current); - - if ( bytes != 1 ) - { - gdprintk(XENLOG_WARNING, "PIT bad access\n"); - *val = ~0; - return X86EMUL_OKAY; - } - - if ( dir == IOREQ_WRITE ) - { - pit_ioport_write(vpit, port, *val); - } - else - { - if ( (port & 3) != 3 ) - *val = pit_ioport_read(vpit, port); - else - gdprintk(XENLOG_WARNING, "PIT: read A1:A0=3!\n"); - } - - return X86EMUL_OKAY; -} - -static void speaker_ioport_write( - struct PITState *pit, uint32_t addr, uint32_t val) -{ - pit->hw.speaker_data_on = (val >> 1) & 1; - pit_set_gate(pit, 2, val & 1); -} - -static uint32_t speaker_ioport_read( - struct PITState *pit, uint32_t addr) -{ - /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */ - unsigned int refresh_clock = ((unsigned int)NOW() >> 14) & 1; - return ((pit->hw.speaker_data_on << 1) | pit_get_gate(pit, 2) | - (pit_get_out(pit, 2) << 5) | (refresh_clock << 4)); -} - -static int handle_speaker_io( - int dir, unsigned int port, uint32_t bytes, uint32_t *val) -{ - struct PITState *vpit = vcpu_vpit(current); - - BUG_ON(bytes != 1); - - spin_lock(&vpit->lock); - - if ( dir == IOREQ_WRITE ) - speaker_ioport_write(vpit, port, *val); - else - *val = speaker_ioport_read(vpit, port); - - spin_unlock(&vpit->lock); - - return X86EMUL_OKAY; -} - -int pv_pit_handler(int port, int data, int write) -{ - ioreq_t ioreq = { - .size = 1, - .type = IOREQ_TYPE_PIO, - .addr = port, - .dir = write ? IOREQ_WRITE : IOREQ_READ, - .data = data - }; - - if ( !has_vpit(current->domain) ) - return ~0; - - if ( is_hardware_domain(current->domain) && hwdom_pit_access(&ioreq) ) - { - /* nothing to do */; - } - else - { - uint32_t val = data; - if ( port == 0x61 ) - handle_speaker_io(ioreq.dir, port, 1, &val); - else - handle_pit_io(ioreq.dir, port, 1, &val); - ioreq.data = val; - } - - return !write ? ioreq.data : 0; -} - -/* - * Local variables: - * mode: C - * c-file-style: "BSD" - * c-basic-offset: 4 - * indent-tabs-mode: nil - * End: - */ -- generated by git-patchbot for /home/xen/git/xen.git#master _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |