Skip to content

Commit

Permalink
KVM: Emulate local APIC in kernel
Browse files Browse the repository at this point in the history
Because lightweight exits (exits which don't involve userspace) are many
times faster than heavyweight exits, it makes sense to emulate high usage
devices in the kernel.  The local APIC is one such device, especially for
Windows and for SMP, so we add an APIC model to kvm.

It also allows in-kernel host-side drivers to inject interrupts without
going through userspace.

[compile fix on i386 from Jindrich Makovicka]

Signed-off-by: Yaozu (Eddie) Dong <Eddie.Dong@intel.com>
Signed-off-by: Qing He <qing.he@intel.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
  • Loading branch information
Eddie Dong authored and Avi Kivity committed Oct 13, 2007
1 parent 7017fc3 commit 97222cc
Show file tree
Hide file tree
Showing 9 changed files with 1,067 additions and 33 deletions.
2 changes: 1 addition & 1 deletion drivers/kvm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Makefile for Kernel-based Virtual Machine module
#

kvm-objs := kvm_main.o mmu.o x86_emulate.o i8259.o irq.o
kvm-objs := kvm_main.o mmu.o x86_emulate.o i8259.o irq.o lapic.o
obj-$(CONFIG_KVM) += kvm.o
kvm-intel-objs = vmx.o
obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
Expand Down
53 changes: 36 additions & 17 deletions drivers/kvm/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@
*/
int kvm_cpu_has_interrupt(struct kvm_vcpu *v)
{
struct kvm_pic *s = pic_irqchip(v->kvm);

if (s->output) /* PIC */
return 1;
/*
* TODO: APIC
*/
return 0;
struct kvm_pic *s;

if (kvm_apic_has_interrupt(v) == -1) { /* LAPIC */
s = pic_irqchip(v->kvm); /* PIC */
return s->output;
}
return 1;
}
EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt);

Expand All @@ -46,16 +45,36 @@ EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt);
*/
int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
{
struct kvm_pic *s = pic_irqchip(v->kvm);
struct kvm_pic *s;
int vector;

s->output = 0;
vector = kvm_pic_read_irq(s);
if (vector != -1)
return vector;
/*
* TODO: APIC
*/
return -1;
vector = kvm_get_apic_interrupt(v); /* APIC */
if (vector == -1) {
s = pic_irqchip(v->kvm);
s->output = 0; /* PIC */
vector = kvm_pic_read_irq(s);
}
return vector;
}
EXPORT_SYMBOL_GPL(kvm_cpu_get_interrupt);

static void vcpu_kick_intr(void *info)
{
#ifdef DEBUG
struct kvm_vcpu *vcpu = (struct kvm_vcpu *)info;
printk(KERN_DEBUG "vcpu_kick_intr %p \n", vcpu);
#endif
}

void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
{
int ipi_pcpu = vcpu->cpu;

if (vcpu->guest_mode)
smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0, 0);
}

void kvm_ioapic_update_eoi(struct kvm *kvm, int vector)
{
/* TODO: for kernel IOAPIC */
}
41 changes: 40 additions & 1 deletion drivers/kvm/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

typedef void irq_request_func(void *opaque, int level);

struct kvm_pic;
struct kvm_kpic_state {
u8 last_irr; /* edge detection */
u8 irr; /* interrupt request register */
Expand Down Expand Up @@ -61,4 +60,44 @@ int kvm_pic_read_irq(struct kvm_pic *s);
int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
int kvm_cpu_has_interrupt(struct kvm_vcpu *v);

struct kvm_lapic {
unsigned long base_address;
struct kvm_io_device dev;
struct {
atomic_t pending;
s64 period; /* unit: ns */
u32 divide_count;
ktime_t last_update;
struct hrtimer dev;
} timer;
struct kvm_vcpu *vcpu;
struct page *regs_page;
void *regs;
};

#ifdef DEBUG
#define ASSERT(x) \
do { \
if (!(x)) { \
printk(KERN_EMERG "assertion failed %s: %d: %s\n", \
__FILE__, __LINE__, #x); \
BUG(); \
} \
} while (0)
#else
#define ASSERT(x) do { } while (0)
#endif

void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu);
int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu);
int kvm_create_lapic(struct kvm_vcpu *vcpu);
void kvm_free_apic(struct kvm_lapic *apic);
u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu);
void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8);
void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
u64 kvm_get_apic_base(struct kvm_vcpu *vcpu);
void kvm_set_apic_base(struct kvm_vcpu *vcpu, u64 data);
void kvm_ioapic_update_eoi(struct kvm *kvm, int vector);

#endif
3 changes: 1 addition & 2 deletions drivers/kvm/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ struct kvm_vcpu {
u64 pdptrs[4]; /* pae */
u64 shadow_efer;
u64 apic_base;
struct kvm_lapic *apic; /* kernel irqchip context */
u64 ia32_misc_enable_msr;

struct kvm_mmu mmu;
Expand Down Expand Up @@ -569,8 +570,6 @@ void set_cr3(struct kvm_vcpu *vcpu, unsigned long cr0);
void set_cr4(struct kvm_vcpu *vcpu, unsigned long cr0);
void set_cr8(struct kvm_vcpu *vcpu, unsigned long cr0);
unsigned long get_cr8(struct kvm_vcpu *vcpu);
u64 kvm_get_apic_base(struct kvm_vcpu *vcpu);
void kvm_set_apic_base(struct kvm_vcpu *vcpu, u64 data);
void lmsw(struct kvm_vcpu *vcpu, unsigned long msw);

int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
Expand Down
52 changes: 42 additions & 10 deletions drivers/kvm/kvm_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ EXPORT_SYMBOL_GPL(kvm_vcpu_init);
void kvm_vcpu_uninit(struct kvm_vcpu *vcpu)
{
kvm_mmu_destroy(vcpu);
kvm_free_apic(vcpu->apic);
free_page((unsigned long)vcpu->pio_data);
free_page((unsigned long)vcpu->run);
}
Expand Down Expand Up @@ -598,25 +599,38 @@ void set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8)
inject_gp(vcpu);
return;
}
vcpu->cr8 = cr8;
if (irqchip_in_kernel(vcpu->kvm))
kvm_lapic_set_tpr(vcpu, cr8);
else
vcpu->cr8 = cr8;
}
EXPORT_SYMBOL_GPL(set_cr8);

unsigned long get_cr8(struct kvm_vcpu *vcpu)
{
return vcpu->cr8;
if (irqchip_in_kernel(vcpu->kvm))
return kvm_lapic_get_cr8(vcpu);
else
return vcpu->cr8;
}
EXPORT_SYMBOL_GPL(get_cr8);

u64 kvm_get_apic_base(struct kvm_vcpu *vcpu)
{
return vcpu->apic_base;
if (irqchip_in_kernel(vcpu->kvm))
return vcpu->apic_base;
else
return vcpu->apic_base;
}
EXPORT_SYMBOL_GPL(kvm_get_apic_base);

void kvm_set_apic_base(struct kvm_vcpu *vcpu, u64 data)
{
vcpu->apic_base = data;
/* TODO: reserve bits check */
if (irqchip_in_kernel(vcpu->kvm))
kvm_lapic_set_base(vcpu, data);
else
vcpu->apic_base = data;
}
EXPORT_SYMBOL_GPL(kvm_set_apic_base);

Expand Down Expand Up @@ -986,15 +1000,31 @@ static int emulator_write_std(unsigned long addr,
return X86EMUL_UNHANDLEABLE;
}

/*
* Only apic need an MMIO device hook, so shortcut now..
*/
static struct kvm_io_device *vcpu_find_pervcpu_dev(struct kvm_vcpu *vcpu,
gpa_t addr)
{
struct kvm_io_device *dev;

if (vcpu->apic) {
dev = &vcpu->apic->dev;
if (dev->in_range(dev, addr))
return dev;
}
return NULL;
}

static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu,
gpa_t addr)
{
/*
* Note that its important to have this wrapper function because
* in the very near future we will be checking for MMIOs against
* the LAPIC as well as the general MMIO bus
*/
return kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr);
struct kvm_io_device *dev;

dev = vcpu_find_pervcpu_dev(vcpu, addr);
if (dev == NULL)
dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr);
return dev;
}

static struct kvm_io_device *vcpu_find_pio_dev(struct kvm_vcpu *vcpu,
Expand Down Expand Up @@ -2256,6 +2286,8 @@ static int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
{
if (irq->irq < 0 || irq->irq >= 256)
return -EINVAL;
if (irqchip_in_kernel(vcpu->kvm))
return -ENXIO;
vcpu_load(vcpu);

set_bit(irq->irq, vcpu->irq_pending);
Expand Down
Loading

0 comments on commit 97222cc

Please sign in to comment.