From mboxrd@z Thu Jan 1 00:00:00 1970 From: "Yang, Sheng" Subject: Re: [RFC][PATCH 1/2] KVM: In-kernel PIT model Date: Thu, 24 Jan 2008 17:29:18 +0800 Message-ID: <200801241729.18787.sheng.yang@intel.com> References: <200801211718.23664.sheng.yang@intel.com> <200801231400.36063.sheng.yang@intel.com> <47970CEE.2050000@qumranet.com> Mime-Version: 1.0 Content-Type: Multipart/Mixed; boundary="Boundary-00=_upFmHuwV2gOJqWs" Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org To: Avi Kivity Return-path: In-Reply-To: <47970CEE.2050000-atKUWr5tajBWk0Htik3J/w@public.gmane.org> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: kvm-devel-bounces-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org Errors-To: kvm-devel-bounces-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org List-Id: kvm.vger.kernel.org --Boundary-00=_upFmHuwV2gOJqWs Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline On Wednesday 23 January 2008 17:46:22 Avi Kivity wrote: > Yang, Sheng wrote: > > On Tuesday 22 January 2008 21:54:10 Avi Kivity wrote: > >> Yang, Sheng wrote: > >>> + > >>> +static int pit_get_out(struct kvm *kvm, int channel) > >>> +{ > >>> + struct PITChannelState *c =3D > >>> + &kvm->arch.vpit->pit_state.channels[channel]; > >>> + struct kvm_vcpu *vcpu =3D kvm->vcpus[0]; > >>> + u64 d, t; > >>> + int out; > >>> + > >>> + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); > >>> + > >>> + kvm_get_msr(vcpu, MSR_IA32_TIME_STAMP_COUNTER, &t); > >>> + d =3D muldiv64(t - c->count_load_time, PIT_FREQ, cpu_khz * 1000); > >> > >> I assume this is to correlate the tsc and the pit counters. Doesn't > >> this break with cpu frequency scaling or with vcpu migrations? > > > > I think the cpu frequency scaling will affect TSC, though cpu_khz would > > be changed along with it. > > It breaks the calculation, though. > > I suggest moving to the Linux clock API (which is in nanoseconds). > I updated patch using ktime_get() (Is it too heavy?). And currently all the muldiv64() is used to divide 1e9, which can be instea= ded=20 by ">> 30" approximately. What's your opinion? But I found just now that the patch would break SMP on pae host(guest hang,= so=20 does the old one)... Don't got clue... =2D- =46rom f61536abab1d4821c027b9645655c717a90093e7 Mon Sep 17 00:00:00 2001 =46rom: Sheng Yang Date: Mon, 21 Jan 2008 16:42:37 +0800 Subject: [PATCH] KVM: In-kernel PIT model Signed-off-by: Sheng Yang =2D-- arch/x86/kvm/Makefile | 3 +- arch/x86/kvm/i8254.c | 575=20 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/i8254.h | 60 +++++ arch/x86/kvm/irq.c | 3 + arch/x86/kvm/x86.c | 9 + include/asm-x86/kvm_host.h | 1 + include/linux/kvm.h | 2 + 7 files changed, 652 insertions(+), 1 deletions(-) create mode 100644 arch/x86/kvm/i8254.c create mode 100644 arch/x86/kvm/i8254.h diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index ffdd0b3..4d0c22e 100644 =2D-- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -6,7 +6,8 @@ common-objs =3D $(addprefix ../../../virt/kvm/, kvm_main.o= =20 ioapic.o) =20 EXTRA_CFLAGS +=3D -Ivirt/kvm -Iarch/x86/kvm =20 =2Dkvm-objs :=3D $(common-objs) x86.o mmu.o x86_emulate.o i8259.o irq.o lap= ic.o +kvm-objs :=3D $(common-objs) x86.o mmu.o x86_emulate.o i8259.o irq.o lapic= =2Eo \ + i8254.o obj-$(CONFIG_KVM) +=3D kvm.o kvm-intel-objs =3D vmx.o obj-$(CONFIG_KVM_INTEL) +=3D kvm-intel.o diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c new file mode 100644 index 0000000..4f1a54a =2D-- /dev/null +++ b/arch/x86/kvm/i8254.c @@ -0,0 +1,575 @@ +/* + * 8253/8254 interval timer emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2006 Intel Corporation + * Copyright (c) 2007 Keir Fraser, XenSource Inc + * Copyright (c) 2008 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a= =20 copy + * of this software and associated documentation files (the "Software"), t= o=20 deal + * in the Software without restriction, including without limitation the=20 rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * 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 OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= =20 =46ROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + * + * Authors: + * Sheng Yang + * Based on QEMU and Xen. + */ + +#include + +#include "i8254.h" + +#define pit_debug(fmt, arg...) printk(KERN_WARNING fmt, ##arg) +/* #define pit_debug(fmt, arg...) */ + +#ifndef CONFIG_X86_64 +#define mod_64(x, y) ((x) - (y) * div64_64(x, y)) +#else +#define mod_64(x, y) ((x) % (y)) +#endif + +#define RW_STATE_LSB 1 +#define RW_STATE_MSB 2 +#define RW_STATE_WORD0 3 +#define RW_STATE_WORD1 4 + +/* Compute with 96 bit intermediate result: (a*b)/c */ +static u64 muldiv64(u64 a, u32 b, u32 c) +{ + union { + u64 ll; + struct { +#ifdef WORDS_BIGENDIAN + u32 high, low; +#else + u32 low, high; +#endif + } l; + } u, res; + u64 rl, rh; + + u.ll =3D a; + rl =3D (u64)u.l.low * (u64)b; + rh =3D (u64)u.l.high * (u64)b; + rh +=3D (rl >> 32); + res.l.high =3D div64_64(rh, c); + res.l.low =3D div64_64(((mod_64(rh, c) << 32) + (rl & 0xffffffff)), c); + return res.ll; +} + +static void pit_set_gate(struct kvm *kvm, int channel, u32 val) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + switch (c->mode) { + default: + case 0: + case 4: + /* XXX: just disable/enable counting */ + break; + case 1: + case 2: + case 3: + case 5: + /* Restart counting on rising edge. */ + if (c->gate < val) + c->count_load_time =3D ktime_get(); + break; + } + + c->gate =3D val; +} + +int pit_get_gate(struct kvm *kvm, int channel) +{ + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + return kvm->arch.vpit->pit_state.channels[channel].gate; +} + +static int pit_get_count(struct kvm *kvm, int channel) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + s64 d, t; + int counter; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + t =3D ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time)); + d =3D muldiv64(t, PIT_FREQ, 1e9); + + switch (c->mode) { + case 0: + case 1: + case 4: + case 5: + counter =3D (c->count - d) & 0xffff; + break; + case 3: + /* XXX: may be incorrect for odd counts */ + counter =3D c->count - (mod_64((2 * d), c->count)); + break; + default: + counter =3D c->count - mod_64(d, c->count); + break; + } + return counter; +} + +static int pit_get_out(struct kvm *kvm, int channel) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + s64 d, t; + int out; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + t =3D ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time)); + d =3D muldiv64(t, PIT_FREQ, 1e9); + + switch (c->mode) { + default: + case 0: + out =3D (d >=3D c->count); + break; + case 1: + out =3D (d < c->count); + break; + case 2: + out =3D ((mod_64(d, c->count) =3D=3D 0) && (d !=3D 0)); + break; + case 3: + out =3D (mod_64(d, c->count) < ((c->count + 1) >> 1)); + break; + case 4: + case 5: + out =3D (d =3D=3D c->count); + break; + } + + return out; +} + +static void pit_latch_count(struct kvm *kvm, int channel) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + if (!c->count_latched) { + c->latched_count =3D pit_get_count(kvm, channel); + c->count_latched =3D c->rw_mode; + } +} + +static void pit_latch_status(struct kvm *kvm, int channel) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + if (!c->status_latched) { + /* TODO: Return NULL COUNT (bit 6). */ + c->status =3D ((pit_get_out(kvm, channel) << 7) | + (c->rw_mode << 4) | + (c->mode << 1) | + c->bcd); + c->status_latched =3D 1; + } +} + +int __pit_timer_fn(struct pit_channel_state *pc) +{ + struct pit_timer *pt =3D &pc->pit_timer; + struct kvm_vcpu *vcpu0 =3D pc->pit_state->pit->kvm->vcpus[0]; + + atomic_inc(&pt->pending); + if (waitqueue_active(&vcpu0->wq)) { + vcpu0->arch.mp_state =3D VCPU_MP_STATE_RUNNABLE; + wake_up_interruptible(&vcpu0->wq); + } + + pt->timer.expires =3D ktime_add_ns(pt->timer.expires, pt->period); + pt->scheduled =3D ktime_to_ns(pt->timer.expires); + + return (pt->period =3D=3D 0 ? 0 : 1); +} + +static enum hrtimer_restart pit_timer_fn(struct hrtimer *data) +{ + struct pit_channel_state *pc; + int restart_timer =3D 0; + + pc =3D container_of(data, struct pit_channel_state, pit_timer.timer); + + restart_timer =3D __pit_timer_fn(pc); + + if (restart_timer) + return HRTIMER_RESTART; + else + return HRTIMER_NORESTART; +} + +static void destroy_pit_timer(struct pit_channel_state *s) +{ + pit_debug("pit: execute del timer!\n"); + hrtimer_cancel(&s->pit_timer.timer); +} + +static void create_pit_timer(struct pit_channel_state *pc, + u32 val, int is_period) +{ + s64 interval; + ktime_t now =3D pc->pit_timer.timer.base->get_time(); + + interval =3D muldiv64(val, 1e9, PIT_FREQ); + + pit_debug("pit: create pit timer, interval is %llu usec\n", interval); + + /* TODO The new value only affected after the retriggered */ + hrtimer_cancel(&pc->pit_timer.timer); + pc->pit_timer.period =3D (is_period =3D=3D 0) ? 0 : interval; + pc->pit_timer.timer.function =3D pit_timer_fn; + atomic_set(&pc->pit_timer.pending, 0); + + hrtimer_start(&pc->pit_timer.timer, ktime_add_ns(now, interval), + HRTIMER_MODE_ABS); +} + +static void pit_load_count(struct kvm *kvm, int channel, u32 val) +{ + struct pit_channel_state *s =3D + &kvm->arch.vpit->pit_state.channels[channel]; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + pit_debug("pit: load_count val is %d, channel is %d\n", val, channel); + + if (val =3D=3D 0) + val =3D 0x10000; + + s->count_load_time =3D ktime_get(); + s->count =3D val; + + if (channel !=3D 0) + return; + + /* Two types of timer + * mode 1 is one shot, mode 2 is period, otherwise del timer */ + switch (s->mode) { + case 1: + create_pit_timer(s, val, 0); + break; + case 2: + create_pit_timer(s, val, 1); + break; + default: + destroy_pit_timer(s); + } +} + +static void pit_ioport_write(struct kvm_io_device *this, + gpa_t addr, int len, const void *data) +{ + struct kvm_pit *pit =3D (struct kvm_pit *)this->private; + struct pit_state *pit_state =3D &pit->pit_state; + struct kvm *kvm =3D pit->kvm; + int channel, access; + struct pit_channel_state *s; + u32 val =3D *(u32 *) data; + + val &=3D 0xff; + addr &=3D 3; + + mutex_lock(&pit_state->lock); + + if (val !=3D 0) + pit_debug("pit: write addr is 0x%x, len is %d, val is 0x%x\n", + (unsigned int)addr, len, val); + + if (addr =3D=3D 3) { + channel =3D val >> 6; + if (channel =3D=3D 3) { + /* Read-Back Command. */ + for (channel =3D 0; channel < 3; channel++) { + s =3D &pit_state->channels[channel]; + if (val & (2 << channel)) { + if (!(val & 0x20)) + pit_latch_count(kvm, channel); + if (!(val & 0x10)) + pit_latch_status(kvm, channel); + } + } + } else { + /* Select Counter . */ + s =3D &pit_state->channels[channel]; + access =3D (val >> 4) & 3; + if (access =3D=3D 0) { + pit_latch_count(kvm, channel); + } else { + s->rw_mode =3D access; + s->read_state =3D access; + s->write_state =3D access; + s->mode =3D (val >> 1) & 7; + if (s->mode > 5) + s->mode -=3D 4; + s->bcd =3D val & 1; + } + } + } else { + /* Write Count. */ + s =3D &pit_state->channels[addr]; + switch (s->write_state) { + default: + case RW_STATE_LSB: + pit_load_count(kvm, addr, val); + break; + case RW_STATE_MSB: + pit_load_count(kvm, addr, val << 8); + break; + case RW_STATE_WORD0: + s->write_latch =3D val; + s->write_state =3D RW_STATE_WORD1; + break; + case RW_STATE_WORD1: + pit_load_count(kvm, addr, s->write_latch | (val << 8)); + s->write_state =3D RW_STATE_WORD0; + break; + } + } + + mutex_unlock(&pit_state->lock); +} + +static void pit_ioport_read(struct kvm_io_device *this, + gpa_t addr, int len, void *data) +{ + struct kvm_pit *pit =3D (struct kvm_pit *)this->private; + struct pit_state *pit_state =3D &pit->pit_state; + struct kvm *kvm =3D pit->kvm; + int ret, count; + struct pit_channel_state *s; + + addr &=3D 3; + s =3D &pit_state->channels[addr]; + + mutex_lock(&pit_state->lock); + + if (s->status_latched) { + s->status_latched =3D 0; + ret =3D s->status; + } else if (s->count_latched) { + switch (s->count_latched) { + default: + case RW_STATE_LSB: + ret =3D s->latched_count & 0xff; + s->count_latched =3D 0; + break; + case RW_STATE_MSB: + ret =3D s->latched_count >> 8; + s->count_latched =3D 0; + break; + case RW_STATE_WORD0: + ret =3D s->latched_count & 0xff; + s->count_latched =3D RW_STATE_MSB; + break; + } + } else { + switch (s->read_state) { + default: + case RW_STATE_LSB: + count =3D pit_get_count(kvm, addr); + ret =3D count & 0xff; + break; + case RW_STATE_MSB: + count =3D pit_get_count(kvm, addr); + ret =3D (count >> 8) & 0xff; + break; + case RW_STATE_WORD0: + count =3D pit_get_count(kvm, addr); + ret =3D count & 0xff; + s->read_state =3D RW_STATE_WORD1; + break; + case RW_STATE_WORD1: + count =3D pit_get_count(kvm, addr); + ret =3D (count >> 8) & 0xff; + s->read_state =3D RW_STATE_WORD0; + break; + } + } + + if (len > sizeof(ret)) + len =3D sizeof(ret); + memcpy(data, (char *)&ret, len); + + mutex_unlock(&pit_state->lock); +} + +static int pit_in_range(struct kvm_io_device *this, gpa_t addr) +{ + return ((addr >=3D PIT_BASE_ADDRESS) && + (addr < PIT_BASE_ADDRESS + PIT_MEM_LENGTH)); +} + +static void speaker_ioport_write(struct kvm_io_device *this, + gpa_t addr, int len, const void *data) +{ + struct kvm_pit *pit =3D (struct kvm_pit *)this->private; + struct pit_state *pit_state =3D &pit->pit_state; + struct kvm *kvm =3D pit->kvm; + u32 val =3D *(u32 *) data; + + mutex_lock(&pit_state->lock); + pit_state->speaker_data_on =3D (val >> 1) & 1; + pit_set_gate(kvm, 2, val & 1); + mutex_unlock(&pit_state->lock); +} + +static void speaker_ioport_read(struct kvm_io_device *this, + gpa_t addr, int len, void *data) +{ + struct kvm_pit *pit =3D (struct kvm_pit *)this->private; + struct pit_state *pit_state =3D &pit->pit_state; + struct kvm *kvm =3D pit->kvm; + unsigned int refresh_clock; + int ret; + + /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */ + refresh_clock =3D ((unsigned int)ktime_to_ns(ktime_get()) >> 14) & 1; + + mutex_lock(&pit_state->lock); + ret =3D ((pit_state->speaker_data_on << 1) | pit_get_gate(kvm, 2) | + (pit_get_out(kvm, 2) << 5) | (refresh_clock << 4)); + if (len > sizeof(ret)) + len =3D sizeof(ret); + memcpy(data, (char *)&ret, len); + mutex_unlock(&pit_state->lock); +} + +static int speaker_in_range(struct kvm_io_device *this, gpa_t addr) +{ + return (addr =3D=3D SPEAKER_BASE_ADDRESS); +} + +struct kvm_pit *kvm_create_pit(struct kvm *kvm) +{ + int i; + struct kvm_pit *pit; + struct pit_state *pit_state; + struct pit_channel_state *c; + + pit =3D kzalloc(sizeof(struct kvm_pit), GFP_KERNEL); + if (!pit) + return NULL; + + mutex_init(&pit->pit_state.lock); + mutex_lock(&pit->pit_state.lock); + + /* Initialize PIO device */ + pit->dev.read =3D pit_ioport_read; + pit->dev.write =3D pit_ioport_write; + pit->dev.in_range =3D pit_in_range; + pit->dev.private =3D pit; + kvm_io_bus_register_dev(&kvm->pio_bus, &pit->dev); + + pit->speaker_dev.read =3D speaker_ioport_read; + pit->speaker_dev.write =3D speaker_ioport_write; + pit->speaker_dev.in_range =3D speaker_in_range; + pit->speaker_dev.private =3D pit; + kvm_io_bus_register_dev(&kvm->pio_bus, &pit->speaker_dev); + + kvm->arch.vpit =3D pit; + pit->kvm =3D kvm; + + pit_state =3D &pit->pit_state; + pit_state->pit =3D pit; + hrtimer_init(&pit_state->channels[0].pit_timer.timer, + CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + atomic_set(&pit_state->channels[0].pit_timer.pending, 0); + for (i =3D 0; i < 3; i++) { + c =3D &pit_state->channels[i]; + c->pit_state =3D pit_state; + c->mode =3D 0xff; + c->gate =3D (i !=3D 2); + pit_load_count(kvm, i, 0); + } + + mutex_unlock(&pit->pit_state.lock); + + return pit; +} + +void kvm_free_pit(struct kvm *kvm) +{ + struct hrtimer *timer; + + if (kvm->arch.vpit) { + mutex_lock(&kvm->arch.vpit->pit_state.lock); + timer =3D &kvm->arch.vpit->pit_state.channels[0].pit_timer.timer; + hrtimer_cancel(timer); + mutex_unlock(&kvm->arch.vpit->pit_state.lock); + kfree(kvm->arch.vpit); + } +} + +int __inject_pit_timer_irq(struct kvm *kvm) +{ + mutex_lock(&kvm->lock); + kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 1); + kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 0); + kvm_pic_set_irq(pic_irqchip(kvm), 0, 1); + kvm_pic_set_irq(pic_irqchip(kvm), 0, 0); + mutex_unlock(&kvm->lock); + + return 1; +} + +void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu) +{ + struct kvm_pit *pit =3D vcpu->kvm->arch.vpit; + struct pit_channel_state *pc; + + if (pit) { + pc =3D &pit->pit_state.channels[0]; + if (atomic_read(&pc->pit_timer.pending)) + __inject_pit_timer_irq(vcpu->kvm); + } +} + +void kvm_pit_timer_intr_post(struct kvm_vcpu *vcpu, int vec) +{ + struct kvm_pit *pit =3D vcpu->kvm->arch.vpit; + struct pit_channel_state *pc; + + if (pit) { + pc =3D &pit->pit_state.channels[0]; + if (atomic_read(&pc->pit_timer.pending) && + (((vcpu->kvm->arch.vpic->pics[0].imr & 1) =3D=3D 0 && + vcpu->kvm->arch.vpic->pics[0].irq_base =3D=3D vec) || + vcpu->kvm->arch.vioapic->redirtbl[0].fields.vector =3D=3D vec)) { + atomic_dec(&pc->pit_timer.pending); + pc->count_load_time =3D ktime_get(); + } + } +} diff --git a/arch/x86/kvm/i8254.h b/arch/x86/kvm/i8254.h new file mode 100644 index 0000000..7f49886 =2D-- /dev/null +++ b/arch/x86/kvm/i8254.h @@ -0,0 +1,60 @@ +#ifndef __I8254_H +#define __I8254_H + +#include "irq.h" + +extern unsigned int cpu_khz; + +struct pit_timer { + struct hrtimer timer; + int irq; + s64 period; /* unit: ns */ + s64 scheduled; + ktime_t last_update; + atomic_t pending; +}; + +struct pit_channel_state { + int count; /* can be 65536 */ + u16 latched_count; + u8 count_latched; + u8 status_latched; + u8 status; + u8 read_state; + u8 write_state; + u8 write_latch; + u8 rw_mode; + u8 mode; + u8 bcd; /* not supported */ + u8 gate; /* timer start */ + ktime_t count_load_time; + struct pit_timer pit_timer; + struct pit_state *pit_state; +}; + +struct pit_state { + struct pit_channel_state channels[3]; + struct mutex lock; + struct kvm_pit *pit; + u32 speaker_data_on; +}; + +struct kvm_pit { + unsigned long base_addresss; + struct kvm_io_device dev; + struct kvm_io_device speaker_dev; + struct kvm *kvm; + struct pit_state pit_state; +}; + +#define PIT_BASE_ADDRESS 0x40 +#define SPEAKER_BASE_ADDRESS 0x61 +#define PIT_MEM_LENGTH 4 +#define PIT_FREQ 1193181 + +void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu); +void kvm_pit_timer_intr_post(struct kvm_vcpu *vcpu, int vec); +struct kvm_pit *kvm_create_pit(struct kvm *kvm); +void kvm_free_pit(struct kvm *kvm); + +#endif diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index e571475..dbfe21c 100644 =2D-- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -23,6 +23,7 @@ #include =20 #include "irq.h" +#include "i8254.h" =20 /* * check if there is pending interrupt without @@ -66,6 +67,7 @@ EXPORT_SYMBOL_GPL(kvm_cpu_get_interrupt); void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu) { kvm_inject_apic_timer_irqs(vcpu); + kvm_inject_pit_timer_irqs(vcpu); /* TODO: PIT, RTC etc. */ } EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs); @@ -73,6 +75,7 @@ EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs); void kvm_timer_intr_post(struct kvm_vcpu *vcpu, int vec) { kvm_apic_timer_intr_post(vcpu, vec); + kvm_pit_timer_intr_post(vcpu, vec); /* TODO: PIT, RTC etc. */ } EXPORT_SYMBOL_GPL(kvm_timer_intr_post); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8f94a0b..0993fad 100644 =2D-- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -18,6 +18,7 @@ #include "segment_descriptor.h" #include "irq.h" #include "mmu.h" +#include "i8254.h" =20 #include #include @@ -680,6 +681,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_USER_MEMORY: case KVM_CAP_SET_TSS_ADDR: case KVM_CAP_EXT_CPUID: + case KVM_CAP_PIT: r =3D 1; break; case KVM_CAP_VAPIC: @@ -1432,6 +1434,12 @@ long kvm_arch_vm_ioctl(struct file *filp, } else goto out; break; + case KVM_CREATE_PIT: + r =3D -ENOMEM; + kvm->arch.vpit =3D kvm_create_pit(kvm); + if (kvm->arch.vpit) + r =3D 0; + break; case KVM_IRQ_LINE: { struct kvm_irq_level irq_event; =20 @@ -3207,6 +3215,7 @@ static void kvm_free_vcpus(struct kvm *kvm) =20 void kvm_arch_destroy_vm(struct kvm *kvm) { + kvm_free_pit(kvm); kfree(kvm->arch.vpic); kfree(kvm->arch.vioapic); kvm_free_vcpus(kvm); diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h index d6db0de..66dbdd5 100644 =2D-- a/include/asm-x86/kvm_host.h +++ b/include/asm-x86/kvm_host.h @@ -283,6 +283,7 @@ struct kvm_arch{ struct list_head active_mmu_pages; struct kvm_pic *vpic; struct kvm_ioapic *vioapic; + struct kvm_pit *vpit; =20 int round_robin_prev_vcpu; unsigned int tss_addr; diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 4de4fd2..2f18cfe 100644 =2D-- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -232,6 +232,7 @@ struct kvm_vapic_addr { #define KVM_CAP_SET_TSS_ADDR 4 #define KVM_CAP_EXT_CPUID 5 #define KVM_CAP_VAPIC 6 +#define KVM_CAP_PIT 7 =20 /* * ioctls for VM fds @@ -255,6 +256,7 @@ struct kvm_vapic_addr { #define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level) #define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip) #define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64) =20 /* * ioctls for vcpu fds =2D-=20 debian.1.5.3.7.1-dirty --Boundary-00=_upFmHuwV2gOJqWs Content-Type: text/x-diff; charset="utf-8"; name="0001-KVM-In-kernel-PIT-model.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="0001-KVM-In-kernel-PIT-model.patch" =46rom f61536abab1d4821c027b9645655c717a90093e7 Mon Sep 17 00:00:00 2001 =46rom: Sheng Yang Date: Mon, 21 Jan 2008 16:42:37 +0800 Subject: [PATCH] KVM: In-kernel PIT model Signed-off-by: Sheng Yang =2D-- arch/x86/kvm/Makefile | 3 +- arch/x86/kvm/i8254.c | 575 ++++++++++++++++++++++++++++++++++++++++= ++++ arch/x86/kvm/i8254.h | 60 +++++ arch/x86/kvm/irq.c | 3 + arch/x86/kvm/x86.c | 9 + include/asm-x86/kvm_host.h | 1 + include/linux/kvm.h | 2 + 7 files changed, 652 insertions(+), 1 deletions(-) create mode 100644 arch/x86/kvm/i8254.c create mode 100644 arch/x86/kvm/i8254.h diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index ffdd0b3..4d0c22e 100644 =2D-- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -6,7 +6,8 @@ common-objs =3D $(addprefix ../../../virt/kvm/, kvm_main.o = ioapic.o) =20 EXTRA_CFLAGS +=3D -Ivirt/kvm -Iarch/x86/kvm =20 =2Dkvm-objs :=3D $(common-objs) x86.o mmu.o x86_emulate.o i8259.o irq.o lap= ic.o +kvm-objs :=3D $(common-objs) x86.o mmu.o x86_emulate.o i8259.o irq.o lapic= =2Eo \ + i8254.o obj-$(CONFIG_KVM) +=3D kvm.o kvm-intel-objs =3D vmx.o obj-$(CONFIG_KVM_INTEL) +=3D kvm-intel.o diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c new file mode 100644 index 0000000..4f1a54a =2D-- /dev/null +++ b/arch/x86/kvm/i8254.c @@ -0,0 +1,575 @@ +/* + * 8253/8254 interval timer emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2006 Intel Corporation + * Copyright (c) 2007 Keir Fraser, XenSource Inc + * Copyright (c) 2008 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * 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 OT= HER + * 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. + * + * Authors: + * Sheng Yang + * Based on QEMU and Xen. + */ + +#include + +#include "i8254.h" + +#define pit_debug(fmt, arg...) printk(KERN_WARNING fmt, ##arg) +/* #define pit_debug(fmt, arg...) */ + +#ifndef CONFIG_X86_64 +#define mod_64(x, y) ((x) - (y) * div64_64(x, y)) +#else +#define mod_64(x, y) ((x) % (y)) +#endif + +#define RW_STATE_LSB 1 +#define RW_STATE_MSB 2 +#define RW_STATE_WORD0 3 +#define RW_STATE_WORD1 4 + +/* Compute with 96 bit intermediate result: (a*b)/c */ +static u64 muldiv64(u64 a, u32 b, u32 c) +{ + union { + u64 ll; + struct { +#ifdef WORDS_BIGENDIAN + u32 high, low; +#else + u32 low, high; +#endif + } l; + } u, res; + u64 rl, rh; + + u.ll =3D a; + rl =3D (u64)u.l.low * (u64)b; + rh =3D (u64)u.l.high * (u64)b; + rh +=3D (rl >> 32); + res.l.high =3D div64_64(rh, c); + res.l.low =3D div64_64(((mod_64(rh, c) << 32) + (rl & 0xffffffff)), c); + return res.ll; +} + +static void pit_set_gate(struct kvm *kvm, int channel, u32 val) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + switch (c->mode) { + default: + case 0: + case 4: + /* XXX: just disable/enable counting */ + break; + case 1: + case 2: + case 3: + case 5: + /* Restart counting on rising edge. */ + if (c->gate < val) + c->count_load_time =3D ktime_get(); + break; + } + + c->gate =3D val; +} + +int pit_get_gate(struct kvm *kvm, int channel) +{ + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + return kvm->arch.vpit->pit_state.channels[channel].gate; +} + +static int pit_get_count(struct kvm *kvm, int channel) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + s64 d, t; + int counter; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + t =3D ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time)); + d =3D muldiv64(t, PIT_FREQ, 1e9); + + switch (c->mode) { + case 0: + case 1: + case 4: + case 5: + counter =3D (c->count - d) & 0xffff; + break; + case 3: + /* XXX: may be incorrect for odd counts */ + counter =3D c->count - (mod_64((2 * d), c->count)); + break; + default: + counter =3D c->count - mod_64(d, c->count); + break; + } + return counter; +} + +static int pit_get_out(struct kvm *kvm, int channel) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + s64 d, t; + int out; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + t =3D ktime_to_ns(ktime_sub(ktime_get(), c->count_load_time)); + d =3D muldiv64(t, PIT_FREQ, 1e9); + + switch (c->mode) { + default: + case 0: + out =3D (d >=3D c->count); + break; + case 1: + out =3D (d < c->count); + break; + case 2: + out =3D ((mod_64(d, c->count) =3D=3D 0) && (d !=3D 0)); + break; + case 3: + out =3D (mod_64(d, c->count) < ((c->count + 1) >> 1)); + break; + case 4: + case 5: + out =3D (d =3D=3D c->count); + break; + } + + return out; +} + +static void pit_latch_count(struct kvm *kvm, int channel) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + if (!c->count_latched) { + c->latched_count =3D pit_get_count(kvm, channel); + c->count_latched =3D c->rw_mode; + } +} + +static void pit_latch_status(struct kvm *kvm, int channel) +{ + struct pit_channel_state *c =3D + &kvm->arch.vpit->pit_state.channels[channel]; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + if (!c->status_latched) { + /* TODO: Return NULL COUNT (bit 6). */ + c->status =3D ((pit_get_out(kvm, channel) << 7) | + (c->rw_mode << 4) | + (c->mode << 1) | + c->bcd); + c->status_latched =3D 1; + } +} + +int __pit_timer_fn(struct pit_channel_state *pc) +{ + struct pit_timer *pt =3D &pc->pit_timer; + struct kvm_vcpu *vcpu0 =3D pc->pit_state->pit->kvm->vcpus[0]; + + atomic_inc(&pt->pending); + if (waitqueue_active(&vcpu0->wq)) { + vcpu0->arch.mp_state =3D VCPU_MP_STATE_RUNNABLE; + wake_up_interruptible(&vcpu0->wq); + } + + pt->timer.expires =3D ktime_add_ns(pt->timer.expires, pt->period); + pt->scheduled =3D ktime_to_ns(pt->timer.expires); + + return (pt->period =3D=3D 0 ? 0 : 1); +} + +static enum hrtimer_restart pit_timer_fn(struct hrtimer *data) +{ + struct pit_channel_state *pc; + int restart_timer =3D 0; + + pc =3D container_of(data, struct pit_channel_state, pit_timer.timer); + + restart_timer =3D __pit_timer_fn(pc); + + if (restart_timer) + return HRTIMER_RESTART; + else + return HRTIMER_NORESTART; +} + +static void destroy_pit_timer(struct pit_channel_state *s) +{ + pit_debug("pit: execute del timer!\n"); + hrtimer_cancel(&s->pit_timer.timer); +} + +static void create_pit_timer(struct pit_channel_state *pc, + u32 val, int is_period) +{ + s64 interval; + ktime_t now =3D pc->pit_timer.timer.base->get_time(); + + interval =3D muldiv64(val, 1e9, PIT_FREQ); + + pit_debug("pit: create pit timer, interval is %llu usec\n", interval); + + /* TODO The new value only affected after the retriggered */ + hrtimer_cancel(&pc->pit_timer.timer); + pc->pit_timer.period =3D (is_period =3D=3D 0) ? 0 : interval; + pc->pit_timer.timer.function =3D pit_timer_fn; + atomic_set(&pc->pit_timer.pending, 0); + + hrtimer_start(&pc->pit_timer.timer, ktime_add_ns(now, interval), + HRTIMER_MODE_ABS); +} + +static void pit_load_count(struct kvm *kvm, int channel, u32 val) +{ + struct pit_channel_state *s =3D + &kvm->arch.vpit->pit_state.channels[channel]; + + ASSERT(mutex_is_locked(&kvm->arch.vpit->pit_state.lock)); + + pit_debug("pit: load_count val is %d, channel is %d\n", val, channel); + + if (val =3D=3D 0) + val =3D 0x10000; + + s->count_load_time =3D ktime_get(); + s->count =3D val; + + if (channel !=3D 0) + return; + + /* Two types of timer + * mode 1 is one shot, mode 2 is period, otherwise del timer */ + switch (s->mode) { + case 1: + create_pit_timer(s, val, 0); + break; + case 2: + create_pit_timer(s, val, 1); + break; + default: + destroy_pit_timer(s); + } +} + +static void pit_ioport_write(struct kvm_io_device *this, + gpa_t addr, int len, const void *data) +{ + struct kvm_pit *pit =3D (struct kvm_pit *)this->private; + struct pit_state *pit_state =3D &pit->pit_state; + struct kvm *kvm =3D pit->kvm; + int channel, access; + struct pit_channel_state *s; + u32 val =3D *(u32 *) data; + + val &=3D 0xff; + addr &=3D 3; + + mutex_lock(&pit_state->lock); + + if (val !=3D 0) + pit_debug("pit: write addr is 0x%x, len is %d, val is 0x%x\n", + (unsigned int)addr, len, val); + + if (addr =3D=3D 3) { + channel =3D val >> 6; + if (channel =3D=3D 3) { + /* Read-Back Command. */ + for (channel =3D 0; channel < 3; channel++) { + s =3D &pit_state->channels[channel]; + if (val & (2 << channel)) { + if (!(val & 0x20)) + pit_latch_count(kvm, channel); + if (!(val & 0x10)) + pit_latch_status(kvm, channel); + } + } + } else { + /* Select Counter . */ + s =3D &pit_state->channels[channel]; + access =3D (val >> 4) & 3; + if (access =3D=3D 0) { + pit_latch_count(kvm, channel); + } else { + s->rw_mode =3D access; + s->read_state =3D access; + s->write_state =3D access; + s->mode =3D (val >> 1) & 7; + if (s->mode > 5) + s->mode -=3D 4; + s->bcd =3D val & 1; + } + } + } else { + /* Write Count. */ + s =3D &pit_state->channels[addr]; + switch (s->write_state) { + default: + case RW_STATE_LSB: + pit_load_count(kvm, addr, val); + break; + case RW_STATE_MSB: + pit_load_count(kvm, addr, val << 8); + break; + case RW_STATE_WORD0: + s->write_latch =3D val; + s->write_state =3D RW_STATE_WORD1; + break; + case RW_STATE_WORD1: + pit_load_count(kvm, addr, s->write_latch | (val << 8)); + s->write_state =3D RW_STATE_WORD0; + break; + } + } + + mutex_unlock(&pit_state->lock); +} + +static void pit_ioport_read(struct kvm_io_device *this, + gpa_t addr, int len, void *data) +{ + struct kvm_pit *pit =3D (struct kvm_pit *)this->private; + struct pit_state *pit_state =3D &pit->pit_state; + struct kvm *kvm =3D pit->kvm; + int ret, count; + struct pit_channel_state *s; + + addr &=3D 3; + s =3D &pit_state->channels[addr]; + + mutex_lock(&pit_state->lock); + + if (s->status_latched) { + s->status_latched =3D 0; + ret =3D s->status; + } else if (s->count_latched) { + switch (s->count_latched) { + default: + case RW_STATE_LSB: + ret =3D s->latched_count & 0xff; + s->count_latched =3D 0; + break; + case RW_STATE_MSB: + ret =3D s->latched_count >> 8; + s->count_latched =3D 0; + break; + case RW_STATE_WORD0: + ret =3D s->latched_count & 0xff; + s->count_latched =3D RW_STATE_MSB; + break; + } + } else { + switch (s->read_state) { + default: + case RW_STATE_LSB: + count =3D pit_get_count(kvm, addr); + ret =3D count & 0xff; + break; + case RW_STATE_MSB: + count =3D pit_get_count(kvm, addr); + ret =3D (count >> 8) & 0xff; + break; + case RW_STATE_WORD0: + count =3D pit_get_count(kvm, addr); + ret =3D count & 0xff; + s->read_state =3D RW_STATE_WORD1; + break; + case RW_STATE_WORD1: + count =3D pit_get_count(kvm, addr); + ret =3D (count >> 8) & 0xff; + s->read_state =3D RW_STATE_WORD0; + break; + } + } + + if (len > sizeof(ret)) + len =3D sizeof(ret); + memcpy(data, (char *)&ret, len); + + mutex_unlock(&pit_state->lock); +} + +static int pit_in_range(struct kvm_io_device *this, gpa_t addr) +{ + return ((addr >=3D PIT_BASE_ADDRESS) && + (addr < PIT_BASE_ADDRESS + PIT_MEM_LENGTH)); +} + +static void speaker_ioport_write(struct kvm_io_device *this, + gpa_t addr, int len, const void *data) +{ + struct kvm_pit *pit =3D (struct kvm_pit *)this->private; + struct pit_state *pit_state =3D &pit->pit_state; + struct kvm *kvm =3D pit->kvm; + u32 val =3D *(u32 *) data; + + mutex_lock(&pit_state->lock); + pit_state->speaker_data_on =3D (val >> 1) & 1; + pit_set_gate(kvm, 2, val & 1); + mutex_unlock(&pit_state->lock); +} + +static void speaker_ioport_read(struct kvm_io_device *this, + gpa_t addr, int len, void *data) +{ + struct kvm_pit *pit =3D (struct kvm_pit *)this->private; + struct pit_state *pit_state =3D &pit->pit_state; + struct kvm *kvm =3D pit->kvm; + unsigned int refresh_clock; + int ret; + + /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */ + refresh_clock =3D ((unsigned int)ktime_to_ns(ktime_get()) >> 14) & 1; + + mutex_lock(&pit_state->lock); + ret =3D ((pit_state->speaker_data_on << 1) | pit_get_gate(kvm, 2) | + (pit_get_out(kvm, 2) << 5) | (refresh_clock << 4)); + if (len > sizeof(ret)) + len =3D sizeof(ret); + memcpy(data, (char *)&ret, len); + mutex_unlock(&pit_state->lock); +} + +static int speaker_in_range(struct kvm_io_device *this, gpa_t addr) +{ + return (addr =3D=3D SPEAKER_BASE_ADDRESS); +} + +struct kvm_pit *kvm_create_pit(struct kvm *kvm) +{ + int i; + struct kvm_pit *pit; + struct pit_state *pit_state; + struct pit_channel_state *c; + + pit =3D kzalloc(sizeof(struct kvm_pit), GFP_KERNEL); + if (!pit) + return NULL; + + mutex_init(&pit->pit_state.lock); + mutex_lock(&pit->pit_state.lock); + + /* Initialize PIO device */ + pit->dev.read =3D pit_ioport_read; + pit->dev.write =3D pit_ioport_write; + pit->dev.in_range =3D pit_in_range; + pit->dev.private =3D pit; + kvm_io_bus_register_dev(&kvm->pio_bus, &pit->dev); + + pit->speaker_dev.read =3D speaker_ioport_read; + pit->speaker_dev.write =3D speaker_ioport_write; + pit->speaker_dev.in_range =3D speaker_in_range; + pit->speaker_dev.private =3D pit; + kvm_io_bus_register_dev(&kvm->pio_bus, &pit->speaker_dev); + + kvm->arch.vpit =3D pit; + pit->kvm =3D kvm; + + pit_state =3D &pit->pit_state; + pit_state->pit =3D pit; + hrtimer_init(&pit_state->channels[0].pit_timer.timer, + CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + atomic_set(&pit_state->channels[0].pit_timer.pending, 0); + for (i =3D 0; i < 3; i++) { + c =3D &pit_state->channels[i]; + c->pit_state =3D pit_state; + c->mode =3D 0xff; + c->gate =3D (i !=3D 2); + pit_load_count(kvm, i, 0); + } + + mutex_unlock(&pit->pit_state.lock); + + return pit; +} + +void kvm_free_pit(struct kvm *kvm) +{ + struct hrtimer *timer; + + if (kvm->arch.vpit) { + mutex_lock(&kvm->arch.vpit->pit_state.lock); + timer =3D &kvm->arch.vpit->pit_state.channels[0].pit_timer.timer; + hrtimer_cancel(timer); + mutex_unlock(&kvm->arch.vpit->pit_state.lock); + kfree(kvm->arch.vpit); + } +} + +int __inject_pit_timer_irq(struct kvm *kvm) +{ + mutex_lock(&kvm->lock); + kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 1); + kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 0); + kvm_pic_set_irq(pic_irqchip(kvm), 0, 1); + kvm_pic_set_irq(pic_irqchip(kvm), 0, 0); + mutex_unlock(&kvm->lock); + + return 1; +} + +void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu) +{ + struct kvm_pit *pit =3D vcpu->kvm->arch.vpit; + struct pit_channel_state *pc; + + if (pit) { + pc =3D &pit->pit_state.channels[0]; + if (atomic_read(&pc->pit_timer.pending)) + __inject_pit_timer_irq(vcpu->kvm); + } +} + +void kvm_pit_timer_intr_post(struct kvm_vcpu *vcpu, int vec) +{ + struct kvm_pit *pit =3D vcpu->kvm->arch.vpit; + struct pit_channel_state *pc; + + if (pit) { + pc =3D &pit->pit_state.channels[0]; + if (atomic_read(&pc->pit_timer.pending) && + (((vcpu->kvm->arch.vpic->pics[0].imr & 1) =3D=3D 0 && + vcpu->kvm->arch.vpic->pics[0].irq_base =3D=3D vec) || + vcpu->kvm->arch.vioapic->redirtbl[0].fields.vector =3D=3D vec)) { + atomic_dec(&pc->pit_timer.pending); + pc->count_load_time =3D ktime_get(); + } + } +} diff --git a/arch/x86/kvm/i8254.h b/arch/x86/kvm/i8254.h new file mode 100644 index 0000000..7f49886 =2D-- /dev/null +++ b/arch/x86/kvm/i8254.h @@ -0,0 +1,60 @@ +#ifndef __I8254_H +#define __I8254_H + +#include "irq.h" + +extern unsigned int cpu_khz; + +struct pit_timer { + struct hrtimer timer; + int irq; + s64 period; /* unit: ns */ + s64 scheduled; + ktime_t last_update; + atomic_t pending; +}; + +struct pit_channel_state { + int count; /* can be 65536 */ + u16 latched_count; + u8 count_latched; + u8 status_latched; + u8 status; + u8 read_state; + u8 write_state; + u8 write_latch; + u8 rw_mode; + u8 mode; + u8 bcd; /* not supported */ + u8 gate; /* timer start */ + ktime_t count_load_time; + struct pit_timer pit_timer; + struct pit_state *pit_state; +}; + +struct pit_state { + struct pit_channel_state channels[3]; + struct mutex lock; + struct kvm_pit *pit; + u32 speaker_data_on; +}; + +struct kvm_pit { + unsigned long base_addresss; + struct kvm_io_device dev; + struct kvm_io_device speaker_dev; + struct kvm *kvm; + struct pit_state pit_state; +}; + +#define PIT_BASE_ADDRESS 0x40 +#define SPEAKER_BASE_ADDRESS 0x61 +#define PIT_MEM_LENGTH 4 +#define PIT_FREQ 1193181 + +void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu); +void kvm_pit_timer_intr_post(struct kvm_vcpu *vcpu, int vec); +struct kvm_pit *kvm_create_pit(struct kvm *kvm); +void kvm_free_pit(struct kvm *kvm); + +#endif diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index e571475..dbfe21c 100644 =2D-- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -23,6 +23,7 @@ #include =20 #include "irq.h" +#include "i8254.h" =20 /* * check if there is pending interrupt without @@ -66,6 +67,7 @@ EXPORT_SYMBOL_GPL(kvm_cpu_get_interrupt); void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu) { kvm_inject_apic_timer_irqs(vcpu); + kvm_inject_pit_timer_irqs(vcpu); /* TODO: PIT, RTC etc. */ } EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs); @@ -73,6 +75,7 @@ EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs); void kvm_timer_intr_post(struct kvm_vcpu *vcpu, int vec) { kvm_apic_timer_intr_post(vcpu, vec); + kvm_pit_timer_intr_post(vcpu, vec); /* TODO: PIT, RTC etc. */ } EXPORT_SYMBOL_GPL(kvm_timer_intr_post); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8f94a0b..0993fad 100644 =2D-- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -18,6 +18,7 @@ #include "segment_descriptor.h" #include "irq.h" #include "mmu.h" +#include "i8254.h" =20 #include #include @@ -680,6 +681,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_USER_MEMORY: case KVM_CAP_SET_TSS_ADDR: case KVM_CAP_EXT_CPUID: + case KVM_CAP_PIT: r =3D 1; break; case KVM_CAP_VAPIC: @@ -1432,6 +1434,12 @@ long kvm_arch_vm_ioctl(struct file *filp, } else goto out; break; + case KVM_CREATE_PIT: + r =3D -ENOMEM; + kvm->arch.vpit =3D kvm_create_pit(kvm); + if (kvm->arch.vpit) + r =3D 0; + break; case KVM_IRQ_LINE: { struct kvm_irq_level irq_event; =20 @@ -3207,6 +3215,7 @@ static void kvm_free_vcpus(struct kvm *kvm) =20 void kvm_arch_destroy_vm(struct kvm *kvm) { + kvm_free_pit(kvm); kfree(kvm->arch.vpic); kfree(kvm->arch.vioapic); kvm_free_vcpus(kvm); diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h index d6db0de..66dbdd5 100644 =2D-- a/include/asm-x86/kvm_host.h +++ b/include/asm-x86/kvm_host.h @@ -283,6 +283,7 @@ struct kvm_arch{ struct list_head active_mmu_pages; struct kvm_pic *vpic; struct kvm_ioapic *vioapic; + struct kvm_pit *vpit; =20 int round_robin_prev_vcpu; unsigned int tss_addr; diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 4de4fd2..2f18cfe 100644 =2D-- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -232,6 +232,7 @@ struct kvm_vapic_addr { #define KVM_CAP_SET_TSS_ADDR 4 #define KVM_CAP_EXT_CPUID 5 #define KVM_CAP_VAPIC 6 +#define KVM_CAP_PIT 7 =20 /* * ioctls for VM fds @@ -255,6 +256,7 @@ struct kvm_vapic_addr { #define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level) #define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip) #define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64) =20 /* * ioctls for vcpu fds =2D-=20 debian.1.5.3.7.1-dirty --Boundary-00=_upFmHuwV2gOJqWs Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ --Boundary-00=_upFmHuwV2gOJqWs Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ kvm-devel mailing list kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org https://lists.sourceforge.net/lists/listinfo/kvm-devel --Boundary-00=_upFmHuwV2gOJqWs--