diff options
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | kvm.c | 438 | ||||
-rw-r--r-- | kvm_irq.c | 33 | ||||
-rw-r--r-- | kvm_irq_comm.c | 554 | ||||
-rw-r--r-- | kvm_x86.c | 45 |
5 files changed, 563 insertions, 521 deletions
@@ -16,7 +16,7 @@ CSTYLE=$(KERNEL_SOURCE)/usr/src/tools/scripts/cstyle all: kvm kvm.so -kvm: kvm.c kvm_x86.c kvm_emulate.c kvm.h kvm_x86host.h msr.h bitops.h kvm_subr.c kvm_irq.c kvm_i8254.c kvm_lapic.c kvm_mmu.c kvm_iodev.c kvm_ioapic.c kvm_vmx.c kvm_i8259.c kvm_coalesced_mmio.c +kvm: kvm.c kvm_x86.c kvm_emulate.c kvm.h kvm_x86host.h msr.h bitops.h kvm_subr.c kvm_irq.c kvm_i8254.c kvm_lapic.c kvm_mmu.c kvm_iodev.c kvm_ioapic.c kvm_vmx.c kvm_i8259.c kvm_coalesced_mmio.c kvm_irq_comm.c $(CC) $(CFLAGS) $(INCLUDEDIR) kvm.c $(CC) $(CFLAGS) $(INCLUDEDIR) kvm_x86.c $(CC) $(CFLAGS) $(INCLUDEDIR) kvm_emulate.c @@ -30,6 +30,7 @@ kvm: kvm.c kvm_x86.c kvm_emulate.c kvm.h kvm_x86host.h msr.h bitops.h kvm_subr.c $(CC) $(CFLAGS) $(INCLUDEDIR) kvm_vmx.c $(CC) $(CFLAGS) $(INCLUDEDIR) kvm_i8259.c $(CC) $(CFLAGS) $(INCLUDEDIR) kvm_coalesced_mmio.c + $(CC) $(CFLAGS) $(INCLUDEDIR) kvm_irq_comm.c $(CTFCONVERT) -i -L VERSION kvm.o $(CTFCONVERT) -i -L VERSION kvm_x86.o $(CTFCONVERT) -i -L VERSION kvm_emulate.o @@ -42,9 +43,10 @@ kvm: kvm.c kvm_x86.c kvm_emulate.c kvm.h kvm_x86host.h msr.h bitops.h kvm_subr.c $(CTFCONVERT) -i -L VERSION kvm_ioapic.o $(CTFCONVERT) -i -L VERSION kvm_vmx.o $(CTFCONVERT) -i -L VERSION kvm_i8259.o - $(CC) $(CFLAGS) $(INCLUDEDIR) kvm_coalesced_mmio.o - $(LD) -r -o kvm kvm.o kvm_x86.o kvm_emulate.o kvm_subr.o kvm_irq.o kvm_i8254.o kvm_lapic.o kvm_mmu.o kvm_iodev.o kvm_ioapic.o kvm_vmx.o kvm_i8259.o kvm_coalesced_mmio.o - $(CTFMERGE) -L VERSION -o kvm kvm.o kvm_x86.o kvm_emulate.o kvm_subr.o kvm_irq.o kvm_i8254.o kvm_lapic.o kvm_mmu.o kvm_iodev.o kvm_ioapic.o kvm_vmx.o kvm_i8259.o kvm_coalesced_mmio.o + $(CTFCONVERT) -i -L VERSION kvm_coalesced_mmio.o + $(CTFCONVERT) -i -L VERSION kvm_irq_comm.o + $(LD) -r -o kvm kvm.o kvm_x86.o kvm_emulate.o kvm_subr.o kvm_irq.o kvm_i8254.o kvm_lapic.o kvm_mmu.o kvm_iodev.o kvm_ioapic.o kvm_vmx.o kvm_i8259.o kvm_coalesced_mmio.o kvm_irq_comm.o + $(CTFMERGE) -L VERSION -o kvm kvm.o kvm_x86.o kvm_emulate.o kvm_subr.o kvm_irq.o kvm_i8254.o kvm_lapic.o kvm_mmu.o kvm_iodev.o kvm_ioapic.o kvm_vmx.o kvm_i8259.o kvm_coalesced_mmio.o kvm_irq_comm.o kvm.so: kvm_mdb.c gcc -m64 -shared \ @@ -57,8 +59,8 @@ install: kvm @pfexec cp kvm.conf /usr/kernel/drv check: - @$(CSTYLE) kvm.c kvm_mdb.c kvm_emulate.c kvm_x86.c kvm_irq.c kvm_lapic.c kvm_i8254.c kvm_mmu.c kvm_iodev.c kvm_subr.c kvm_ioapic.c kvm_vmx.c kvm_i8259.c kvm_coalesced_mmio.c - @./tools/xxxcheck kvm_x86.c kvm.c kvm_irq.c kvm_lapic.c kvm_i8254.c kvm_mmu.c kvm_iodev.c kvm_ioapic.c kvm_vmx.c kvm_i8259.c kvm_coalesced_mmio.c + @$(CSTYLE) kvm.c kvm_mdb.c kvm_emulate.c kvm_x86.c kvm_irq.c kvm_lapic.c kvm_i8254.c kvm_mmu.c kvm_iodev.c kvm_subr.c kvm_ioapic.c kvm_vmx.c kvm_i8259.c kvm_coalesced_mmio.c kvm_irq_comm.c + @./tools/xxxcheck kvm_x86.c kvm.c kvm_irq.c kvm_lapic.c kvm_i8254.c kvm_mmu.c kvm_iodev.c kvm_ioapic.c kvm_vmx.c kvm_i8259.c kvm_coalesced_mmio.c kvm_irq_comm.c load: install @echo "==> Loading kvm module" @@ -246,6 +246,7 @@ extern void kvm_mmu_sync_roots(struct kvm_vcpu *); extern void kvm_mmu_flush_tlb(struct kvm_vcpu *); extern void kvm_mmu_unload(struct kvm_vcpu *vcpu); extern int kvm_pic_set_irq(void *, int, int); +extern /* * Find the first cleared bit in a memory region. @@ -5671,38 +5672,6 @@ page_address(page_t *page) return (hat_kpm_mapin_pfn(page->p_pagenum)); } -void -kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin) -{ - struct kvm_irq_ack_notifier *kian; - struct hlist_node *n; - int gsi; - - KVM_TRACE2(ack__irq, unsigned int, irqchip, unsigned int, pin); - -#ifdef XXX - rcu_read_lock(); - - gsi = rcu_dereference(kvm->irq_routing)->chip[irqchip][pin]; -#else - XXX_KVM_SYNC_PROBE; -#endif - gsi = (kvm->irq_routing)->chip[irqchip][pin]; - - if (gsi != -1) { - for (kian = list_head(&kvm->irq_ack_notifier_list); - kian != NULL; - kian = list_next(&kvm->irq_ack_notifier_list, kian)) { - if (kian->gsi == gsi) - kian->irq_acked(kian); - } - } -#ifdef XXX - rcu_read_unlock(); -#else - XXX_KVM_SYNC_PROBE; -#endif -} /* * Read pending interrupt vector and intack. @@ -6469,306 +6438,6 @@ kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, extern void kvm_vcpu_kick(struct kvm_vcpu *vcpu); -void -kvm_fire_mask_notifiers(struct kvm *kvm, int irq, int mask) -{ - struct kvm_irq_mask_notifier *kimn; - -#ifdef XXX - rcu_read_lock(); -#else - XXX_KVM_SYNC_PROBE; -#endif - - for (kimn = list_head(&kvm->mask_notifier_list); kimn != NULL; - kimn = list_next(&kvm->mask_notifier_list, kimn)) { - if (kimn->irq == irq) - kimn->func(kimn, mask); - } - -#ifdef XXX - rcu_read_unlock(); -#else - XXX_KVM_SYNC_PROBE; -#endif -} - - - -static inline int -kvm_irq_line_state(unsigned long *irq_state, int irq_source_id, int level) -{ - /* Logical OR for level trig interrupt */ - if (level) { - set_bit(irq_source_id, irq_state); - } else { - clear_bit(irq_source_id, irq_state); - } - - return (!!(*irq_state)); -} - - - -static int -kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e, - struct kvm *kvm, int irq_source_id, int level) -{ -#ifdef CONFIG_X86 - struct kvm_pic *pic = pic_irqchip(kvm); - level = kvm_irq_line_state(&pic->irq_states[e->irqchip.pin], - irq_source_id, level); - return (kvm_pic_set_irq(pic, e->irqchip.pin, level)); -#else - return (-1); -#endif -} - -static int -kvm_set_ioapic_irq(struct kvm_kernel_irq_routing_entry *e, - struct kvm *kvm, int irq_source_id, int level) -{ - struct kvm_ioapic *ioapic = kvm->arch.vioapic; - level = kvm_irq_line_state(&ioapic->irq_states[e->irqchip.pin], - irq_source_id, level); - - return (kvm_ioapic_set_irq(ioapic, e->irqchip.pin, level)); -} - -static int -kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, - int irq_source_id, int level) -{ - struct kvm_lapic_irq irq; - - if (!level) - return (-1); - - KVM_TRACE2(msi__set__irq, uintptr_t, e->msi.address_lo, - uintptr_t, e->msi.data); - - irq.dest_id = (e->msi.address_lo & MSI_ADDR_DEST_ID_MASK) >> - MSI_ADDR_DEST_ID_SHIFT; - irq.vector = (e->msi.data & MSI_DATA_VECTOR_MASK) >> - MSI_DATA_VECTOR_SHIFT; - irq.dest_mode = (1 << MSI_ADDR_DEST_MODE_SHIFT) & e->msi.address_lo; - irq.trig_mode = (1 << MSI_DATA_TRIGGER_SHIFT) & e->msi.data; - irq.delivery_mode = e->msi.data & 0x700; - irq.level = 1; - irq.shorthand = 0; - - /* TODO Deal with RH bit of MSI message address */ - return (kvm_irq_delivery_to_apic(kvm, NULL, &irq)); -} - -static int -setup_routing_entry(struct kvm_irq_routing_table *rt, - struct kvm_kernel_irq_routing_entry *e, - const struct kvm_irq_routing_entry *ue) -{ - int r = -EINVAL; - int delta; - unsigned max_pin; - struct kvm_kernel_irq_routing_entry *ei; - - /* - * Do not allow GSI to be mapped to the same irqchip more than once. - * Allow only one to one mapping between GSI and MSI. - */ - for (ei = list_head(&rt->map[ue->gsi]); ei != NULL; - ei = list_next(&rt->map[ue->gsi], ei)) { - if (ei->type == KVM_IRQ_ROUTING_MSI || - ue->u.irqchip.irqchip == ei->irqchip.irqchip) - return (r); - } - - e->gsi = ue->gsi; - e->type = ue->type; - switch (ue->type) { - case KVM_IRQ_ROUTING_IRQCHIP: - delta = 0; - switch (ue->u.irqchip.irqchip) { - case KVM_IRQCHIP_PIC_MASTER: - e->set = kvm_set_pic_irq; - max_pin = 16; - break; - case KVM_IRQCHIP_PIC_SLAVE: - e->set = kvm_set_pic_irq; - max_pin = 16; - delta = 8; - break; - case KVM_IRQCHIP_IOAPIC: - max_pin = KVM_IOAPIC_NUM_PINS; - e->set = kvm_set_ioapic_irq; - break; - default: - goto out; - } - e->irqchip.irqchip = ue->u.irqchip.irqchip; - e->irqchip.pin = ue->u.irqchip.pin + delta; - if (e->irqchip.pin >= max_pin) - goto out; - rt->chip[ue->u.irqchip.irqchip][e->irqchip.pin] = ue->gsi; - break; - case KVM_IRQ_ROUTING_MSI: - e->set = kvm_set_msi; - e->msi.address_lo = ue->u.msi.address_lo; - e->msi.address_hi = ue->u.msi.address_hi; - e->msi.data = ue->u.msi.data; - break; - default: - goto out; - } - - list_insert_head(&rt->map[e->gsi], e); - r = 0; -out: - return (r); -} - -int -kvm_set_irq_routing(struct kvm *kvm, const struct kvm_irq_routing_entry *ue, - unsigned nr, unsigned flags) -{ - struct kvm_irq_routing_table *new, *old; - uint32_t i, j, nr_rt_entries = 0; - size_t sz = sizeof (struct kvm_kernel_irq_routing_entry); - int r; - - for (i = 0; i < nr; ++i) { - if (ue[i].gsi >= KVM_MAX_IRQ_ROUTES) - return (-EINVAL); - nr_rt_entries = max(nr_rt_entries, ue[i].gsi); - } - - nr_rt_entries += 1; - -#ifdef XXX - new = kmem_zalloc(sizeof (*new) + (nr_rt_entries * sizeof (list_t)) + - (nr * sz), KM_SLEEP); - - new->rt_entries = (void *)&new->map[nr_rt_entries]; -#else - XXX_KVM_PROBE; - new = kmem_zalloc(sizeof (*new), KM_SLEEP); - - for (i = 0; i < KVM_MAX_IRQ_ROUTES; i++) { - list_create(&new->map[i], sz, - offsetof(struct kvm_kernel_irq_routing_entry, link)); - } - - new->rt_entries = kmem_zalloc(sz * nr, KM_SLEEP); - -#endif - - new->nr_rt_entries = nr_rt_entries; - for (i = 0; i < 3; i++) - for (j = 0; j < KVM_IOAPIC_NUM_PINS; j++) - new->chip[i][j] = -1; - - for (i = 0; i < nr; ++i) { - r = -EINVAL; - if (ue->flags) - goto out; - r = setup_routing_entry(new, - (struct kvm_kernel_irq_routing_entry *) - ((caddr_t)new->rt_entries + (i * sz)), ue); - - if (r) - goto out; - ++ue; - } - - mutex_enter(&kvm->irq_lock); - old = kvm->irq_routing; -#ifdef XXX - rcu_assign_pointer(kvm->irq_routing, new); -#else - XXX_KVM_SYNC_PROBE; - kvm->irq_routing = new; -#endif - mutex_exit(&kvm->irq_lock); -#ifdef XXX - synchronize_rcu(); -#else - XXX_KVM_SYNC_PROBE; -#endif - - new = old; - r = 0; - -out: - if (new) { - if (new->rt_entries != NULL) - kmem_free(new->rt_entries, sz * nr); - - kmem_free(new, sizeof (*new)); - } - return (r); -} - -#define IOAPIC_ROUTING_ENTRY(irq) \ - { \ - .gsi = irq, \ - .type = KVM_IRQ_ROUTING_IRQCHIP, \ - .u.irqchip.irqchip = KVM_IRQCHIP_IOAPIC, \ - .u.irqchip.pin = (irq) \ - } - -#define ROUTING_ENTRY1(irq) IOAPIC_ROUTING_ENTRY(irq) - -#ifdef CONFIG_X86 -#define PIC_ROUTING_ENTRY(irq) \ - { \ - .gsi = irq, \ - .type = KVM_IRQ_ROUTING_IRQCHIP, \ - .u.irqchip.irqchip = SELECT_PIC(irq), \ - .u.irqchip.pin = (irq) % 8 \ - } - -#define ROUTING_ENTRY2(irq) \ - IOAPIC_ROUTING_ENTRY(irq), PIC_ROUTING_ENTRY(irq) -#else -#define ROUTING_ENTRY2(irq) \ - IOAPIC_ROUTING_ENTRY(irq) -#endif - -static const struct kvm_irq_routing_entry default_routing[] = { - ROUTING_ENTRY2(0), ROUTING_ENTRY2(1), - ROUTING_ENTRY2(2), ROUTING_ENTRY2(3), - ROUTING_ENTRY2(4), ROUTING_ENTRY2(5), - ROUTING_ENTRY2(6), ROUTING_ENTRY2(7), - ROUTING_ENTRY2(8), ROUTING_ENTRY2(9), - ROUTING_ENTRY2(10), ROUTING_ENTRY2(11), - ROUTING_ENTRY2(12), ROUTING_ENTRY2(13), - ROUTING_ENTRY2(14), ROUTING_ENTRY2(15), - ROUTING_ENTRY1(16), ROUTING_ENTRY1(17), - ROUTING_ENTRY1(18), ROUTING_ENTRY1(19), - ROUTING_ENTRY1(20), ROUTING_ENTRY1(21), - ROUTING_ENTRY1(22), ROUTING_ENTRY1(23), -#ifdef CONFIG_IA64 - ROUTING_ENTRY1(24), ROUTING_ENTRY1(25), - ROUTING_ENTRY1(26), ROUTING_ENTRY1(27), - ROUTING_ENTRY1(28), ROUTING_ENTRY1(29), - ROUTING_ENTRY1(30), ROUTING_ENTRY1(31), - ROUTING_ENTRY1(32), ROUTING_ENTRY1(33), - ROUTING_ENTRY1(34), ROUTING_ENTRY1(35), - ROUTING_ENTRY1(36), ROUTING_ENTRY1(37), - ROUTING_ENTRY1(38), ROUTING_ENTRY1(39), - ROUTING_ENTRY1(40), ROUTING_ENTRY1(41), - ROUTING_ENTRY1(42), ROUTING_ENTRY1(43), - ROUTING_ENTRY1(44), ROUTING_ENTRY1(45), - ROUTING_ENTRY1(46), ROUTING_ENTRY1(47), -#endif -}; - -int -kvm_setup_default_irq_routing(struct kvm *kvm) -{ - return (kvm_set_irq_routing(kvm, default_routing, - ARRAY_SIZE(default_routing), 0)); -} - static int kvm_vm_ioctl_set_identity_map_addr(struct kvm *kvm, uint64_t ident_addr) { @@ -6776,28 +6445,6 @@ kvm_vm_ioctl_set_identity_map_addr(struct kvm *kvm, uint64_t ident_addr) return (0); } -int -kvm_request_irq_source_id(struct kvm *kvm) -{ - unsigned long *bitmap = &kvm->arch.irq_sources_bitmap; - int irq_source_id; - - mutex_enter(&kvm->irq_lock); - irq_source_id = find_first_zero_bit(bitmap, 64); - - if (irq_source_id >= 64) { - irq_source_id = -EFAULT; - goto unlock; - } - - ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); - set_bit(irq_source_id, bitmap); -unlock: - mutex_exit(&kvm->irq_lock); - - return (irq_source_id); -} - void kvm_timer_fire(void *arg) { @@ -6820,42 +6467,7 @@ kvm_timer_fire(void *arg) mutex_exit(&vcpu->kvcpu_kick_lock); } -void -kvm_register_irq_ack_notifier(struct kvm *kvm, - struct kvm_irq_ack_notifier *kian) -{ - mutex_enter(&kvm->irq_lock); - list_insert_head(&kvm->irq_ack_notifier_list, kian); - mutex_exit(&kvm->irq_lock); -} - -void -kvm_unregister_irq_ack_notifier(struct kvm *kvm, - struct kvm_irq_ack_notifier *kian) -{ - mutex_enter(&kvm->irq_lock); - list_remove(&kvm->irq_ack_notifier_list, kian); - mutex_exit(&kvm->irq_lock); -} - -void -kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, - struct kvm_irq_mask_notifier *kimn) -{ - mutex_enter(&kvm->irq_lock); - kimn->irq = irq; - list_insert_head(&kvm->mask_notifier_list, kimn); - mutex_exit(&kvm->irq_lock); -} -void -kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, - struct kvm_irq_mask_notifier *kimn) -{ - mutex_enter(&kvm->irq_lock); - list_remove(&kvm->mask_notifier_list, kimn); - mutex_exit(&kvm->irq_lock); -} static int kvm_vcpu_ioctl_get_lapic(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) @@ -6938,54 +6550,6 @@ kvm_vm_ioctl_set_irqchip(struct kvm *kvm, struct kvm_irqchip *chip) return (r); } -/* - * Return value: - * < 0 Interrupt was ignored (masked or not delivered for other reasons) - * = 0 Interrupt was coalesced (previous irq is still pending) - * > 0 Number of CPUs interrupt was delivered to - */ -int -kvm_set_irq(struct kvm *kvm, int irq_source_id, uint32_t irq, int level) -{ - struct kvm_kernel_irq_routing_entry *e, irq_set[KVM_NR_IRQCHIPS]; - int ret = -1, i = 0; - struct kvm_irq_routing_table *irq_rt; - - /* - * Not possible to detect if the guest uses the PIC or the - * IOAPIC. So set the bit in both. The guest will ignore - * writes to the unused one. - */ -#ifdef XXX - rcu_read_lock(); - irq_rt = rcu_dereference(kvm->irq_routing); -#else - XXX_KVM_SYNC_PROBE; - irq_rt = kvm->irq_routing; -#endif - if (irq < irq_rt->nr_rt_entries) { - for (e = list_head(&irq_rt->map[irq]); e != NULL; - e = list_next(&irq_rt->map[irq], e)) { - irq_set[i++] = *e; - } - } -#ifdef XXX - rcu_read_unlock(); -#else - XXX_KVM_SYNC_PROBE; -#endif - - while (i--) { - int r; - r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level); - if (r < 0) - continue; - - ret = r + ((ret < 0) ? 0 : ret); - } - - return (ret); -} static int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) @@ -60,39 +60,6 @@ kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu) } void -kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id) -{ - int i; - - ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); - - mutex_enter(&kvm->irq_lock); - if (irq_source_id < 0 || - irq_source_id >= BITS_PER_LONG) { -#ifdef XXX - printk(KERN_ERR "kvm: IRQ source ID out of range!\n"); -#else - XXX_KVM_PROBE; -#endif - goto unlock; - } - clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap); - if (!irqchip_in_kernel(kvm)) - goto unlock; - - for (i = 0; i < KVM_IOAPIC_NUM_PINS; i++) { - clear_bit(irq_source_id, &kvm->arch.vioapic->irq_states[i]); - if (i >= 16) - continue; -#ifdef CONFIG_X86 - clear_bit(irq_source_id, &pic_irqchip(kvm)->irq_states[i]); -#endif - } -unlock: - mutex_exit(&kvm->irq_lock); -} - -void __kvm_migrate_timers(struct kvm_vcpu *vcpu) { __kvm_migrate_apic_timer(vcpu); diff --git a/kvm_irq_comm.c b/kvm_irq_comm.c new file mode 100644 index 0000000..aa4cf14 --- /dev/null +++ b/kvm_irq_comm.c @@ -0,0 +1,554 @@ +/* + * irq_comm.c: Common API for in kernel interrupt controller + * Copyright (c) 2007, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * Authors: + * Yaozu (Eddie) Dong <Eddie.dong@intel.com> + * + */ + +/* + * XXX Need proper header files! + */ +#include "msr.h" +#include "irqflags.h" +#include "kvm_host.h" +#include "kvm_x86host.h" +#include "kvm_iodev.h" +#include "kvm.h" +#include "apicdef.h" +#include "kvm_lapic.h" +#include "kvm_ioapic.h" +#include "irq.h" + +extern long find_first_zero_bit(const unsigned long *, unsigned long); +extern int kvm_pic_set_irq(void *, int, int); + +static int +kvm_irq_line_state(unsigned long *irq_state, int irq_source_id, int level) +{ + /* Logical OR for level trig interrupt */ + if (level) { + set_bit(irq_source_id, irq_state); + } else { + clear_bit(irq_source_id, irq_state); + } + + return (!!(*irq_state)); +} + +static int +kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level) +{ +#ifdef CONFIG_X86 + struct kvm_pic *pic = pic_irqchip(kvm); + level = kvm_irq_line_state(&pic->irq_states[e->irqchip.pin], + irq_source_id, level); + return (kvm_pic_set_irq(pic, e->irqchip.pin, level)); +#else + return (-1); +#endif +} + +static int +kvm_set_ioapic_irq(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level) +{ + struct kvm_ioapic *ioapic = kvm->arch.vioapic; + level = kvm_irq_line_state(&ioapic->irq_states[e->irqchip.pin], + irq_source_id, level); + + return (kvm_ioapic_set_irq(ioapic, e->irqchip.pin, level)); +} + +static int +kvm_is_dm_lowest_prio(struct kvm_lapic_irq *irq) +{ +#ifdef CONFIG_IA64 + return (irq->delivery_mode == + (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT)); +#else + return (irq->delivery_mode == APIC_DM_LOWEST); +#endif +} + +int +kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, + struct kvm_lapic_irq *irq) +{ + int i, r = -1; + struct kvm_vcpu *vcpu, *lowest = NULL; + + if (irq->dest_mode == 0 && irq->dest_id == 0xff && + kvm_is_dm_lowest_prio(irq)) + cmn_err(CE_NOTE, "kvm: apic: phys broadcast and lowest prio\n"); + + kvm_for_each_vcpu(i, vcpu, kvm) { + if (!kvm_apic_present(vcpu)) + continue; + + if (!kvm_apic_match_dest(vcpu, src, irq->shorthand, + irq->dest_id, irq->dest_mode)) + continue; + + if (!kvm_is_dm_lowest_prio(irq)) { + if (r < 0) + r = 0; + r += kvm_apic_set_irq(vcpu, irq); + } else { + if (!lowest) + lowest = vcpu; + else if (kvm_apic_compare_prio(vcpu, lowest) < 0) + lowest = vcpu; + } + } + if (lowest) + r = kvm_apic_set_irq(lowest, irq); + + return (r); +} + +static int +kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, + int irq_source_id, int level) +{ + struct kvm_lapic_irq irq; + + if (!level) + return (-1); + + KVM_TRACE2(msi__set__irq, uintptr_t, e->msi.address_lo, + uintptr_t, e->msi.data); + + irq.dest_id = (e->msi.address_lo & MSI_ADDR_DEST_ID_MASK) >> + MSI_ADDR_DEST_ID_SHIFT; + irq.vector = (e->msi.data & MSI_DATA_VECTOR_MASK) >> + MSI_DATA_VECTOR_SHIFT; + irq.dest_mode = (1 << MSI_ADDR_DEST_MODE_SHIFT) & e->msi.address_lo; + irq.trig_mode = (1 << MSI_DATA_TRIGGER_SHIFT) & e->msi.data; + irq.delivery_mode = e->msi.data & 0x700; + irq.level = 1; + irq.shorthand = 0; + + /* TODO Deal with RH bit of MSI message address */ + return (kvm_irq_delivery_to_apic(kvm, NULL, &irq)); +} + +/* + * Return value: + * < 0 Interrupt was ignored (masked or not delivered for other reasons) + * = 0 Interrupt was coalesced (previous irq is still pending) + * > 0 Number of CPUs interrupt was delivered to + */ +int +kvm_set_irq(struct kvm *kvm, int irq_source_id, uint32_t irq, int level) +{ + struct kvm_kernel_irq_routing_entry *e, irq_set[KVM_NR_IRQCHIPS]; + int ret = -1, i = 0; + struct kvm_irq_routing_table *irq_rt; + + /* + * Not possible to detect if the guest uses the PIC or the + * IOAPIC. So set the bit in both. The guest will ignore + * writes to the unused one. + */ +#ifdef XXX + rcu_read_lock(); + irq_rt = rcu_dereference(kvm->irq_routing); +#else + XXX_KVM_SYNC_PROBE; + irq_rt = kvm->irq_routing; +#endif + if (irq < irq_rt->nr_rt_entries) { + for (e = list_head(&irq_rt->map[irq]); e != NULL; + e = list_next(&irq_rt->map[irq], e)) { + irq_set[i++] = *e; + } + } +#ifdef XXX + rcu_read_unlock(); +#else + XXX_KVM_SYNC_PROBE; +#endif + + while (i--) { + int r; + r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level); + if (r < 0) + continue; + + ret = r + ((ret < 0) ? 0 : ret); + } + + return (ret); +} + +void +kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin) +{ + struct kvm_irq_ack_notifier *kian; + struct hlist_node *n; + int gsi; + + KVM_TRACE2(ack__irq, unsigned int, irqchip, unsigned int, pin); + +#ifdef XXX + rcu_read_lock(); + + gsi = rcu_dereference(kvm->irq_routing)->chip[irqchip][pin]; +#else + XXX_KVM_SYNC_PROBE; +#endif + gsi = (kvm->irq_routing)->chip[irqchip][pin]; + + if (gsi != -1) { + for (kian = list_head(&kvm->irq_ack_notifier_list); + kian != NULL; + kian = list_next(&kvm->irq_ack_notifier_list, kian)) { + if (kian->gsi == gsi) + kian->irq_acked(kian); + } + } +#ifdef XXX + rcu_read_unlock(); +#else + XXX_KVM_SYNC_PROBE; +#endif +} + +void +kvm_register_irq_ack_notifier(struct kvm *kvm, + struct kvm_irq_ack_notifier *kian) +{ + mutex_enter(&kvm->irq_lock); + list_insert_head(&kvm->irq_ack_notifier_list, kian); + mutex_exit(&kvm->irq_lock); +} + +void +kvm_unregister_irq_ack_notifier(struct kvm *kvm, + struct kvm_irq_ack_notifier *kian) +{ + mutex_enter(&kvm->irq_lock); + list_remove(&kvm->irq_ack_notifier_list, kian); + mutex_exit(&kvm->irq_lock); +} + +int +kvm_request_irq_source_id(struct kvm *kvm) +{ + unsigned long *bitmap = &kvm->arch.irq_sources_bitmap; + int irq_source_id; + + mutex_enter(&kvm->irq_lock); + irq_source_id = find_first_zero_bit(bitmap, 64); + + if (irq_source_id >= 64) { + irq_source_id = -EFAULT; + goto unlock; + } + + ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); + set_bit(irq_source_id, bitmap); +unlock: + mutex_exit(&kvm->irq_lock); + + return (irq_source_id); +} + +void +kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id) +{ + int i; + + ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); + + mutex_enter(&kvm->irq_lock); + if (irq_source_id < 0 || + irq_source_id >= BITS_PER_LONG) { +#ifdef XXX + printk(KERN_ERR "kvm: IRQ source ID out of range!\n"); +#else + XXX_KVM_PROBE; +#endif + goto unlock; + } + clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap); + if (!irqchip_in_kernel(kvm)) + goto unlock; + + for (i = 0; i < KVM_IOAPIC_NUM_PINS; i++) { + clear_bit(irq_source_id, &kvm->arch.vioapic->irq_states[i]); + if (i >= 16) + continue; +#ifdef CONFIG_X86 + clear_bit(irq_source_id, &pic_irqchip(kvm)->irq_states[i]); +#endif + } +unlock: + mutex_exit(&kvm->irq_lock); +} + +void +kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, + struct kvm_irq_mask_notifier *kimn) +{ + mutex_enter(&kvm->irq_lock); + kimn->irq = irq; + list_insert_head(&kvm->mask_notifier_list, kimn); + mutex_exit(&kvm->irq_lock); +} + +void +kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, + struct kvm_irq_mask_notifier *kimn) +{ + mutex_enter(&kvm->irq_lock); + list_remove(&kvm->mask_notifier_list, kimn); + mutex_exit(&kvm->irq_lock); +} + +void +kvm_fire_mask_notifiers(struct kvm *kvm, int irq, int mask) +{ + struct kvm_irq_mask_notifier *kimn; + +#ifdef XXX + rcu_read_lock(); +#else + XXX_KVM_SYNC_PROBE; +#endif + + for (kimn = list_head(&kvm->mask_notifier_list); kimn != NULL; + kimn = list_next(&kvm->mask_notifier_list, kimn)) { + if (kimn->irq == irq) + kimn->func(kimn, mask); + } + +#ifdef XXX + rcu_read_unlock(); +#else + XXX_KVM_SYNC_PROBE; +#endif +} + +static int +setup_routing_entry(struct kvm_irq_routing_table *rt, + struct kvm_kernel_irq_routing_entry *e, + const struct kvm_irq_routing_entry *ue) +{ + int r = -EINVAL; + int delta; + unsigned max_pin; + struct kvm_kernel_irq_routing_entry *ei; + + /* + * Do not allow GSI to be mapped to the same irqchip more than once. + * Allow only one to one mapping between GSI and MSI. + */ + for (ei = list_head(&rt->map[ue->gsi]); ei != NULL; + ei = list_next(&rt->map[ue->gsi], ei)) { + if (ei->type == KVM_IRQ_ROUTING_MSI || + ue->u.irqchip.irqchip == ei->irqchip.irqchip) + return (r); + } + + e->gsi = ue->gsi; + e->type = ue->type; + switch (ue->type) { + case KVM_IRQ_ROUTING_IRQCHIP: + delta = 0; + switch (ue->u.irqchip.irqchip) { + case KVM_IRQCHIP_PIC_MASTER: + e->set = kvm_set_pic_irq; + max_pin = 16; + break; + case KVM_IRQCHIP_PIC_SLAVE: + e->set = kvm_set_pic_irq; + max_pin = 16; + delta = 8; + break; + case KVM_IRQCHIP_IOAPIC: + max_pin = KVM_IOAPIC_NUM_PINS; + e->set = kvm_set_ioapic_irq; + break; + default: + goto out; + } + e->irqchip.irqchip = ue->u.irqchip.irqchip; + e->irqchip.pin = ue->u.irqchip.pin + delta; + if (e->irqchip.pin >= max_pin) + goto out; + rt->chip[ue->u.irqchip.irqchip][e->irqchip.pin] = ue->gsi; + break; + case KVM_IRQ_ROUTING_MSI: + e->set = kvm_set_msi; + e->msi.address_lo = ue->u.msi.address_lo; + e->msi.address_hi = ue->u.msi.address_hi; + e->msi.data = ue->u.msi.data; + break; + default: + goto out; + } + + list_insert_head(&rt->map[e->gsi], e); + r = 0; +out: + return (r); +} + +int +kvm_set_irq_routing(struct kvm *kvm, const struct kvm_irq_routing_entry *ue, + unsigned nr, unsigned flags) +{ + struct kvm_irq_routing_table *new, *old; + uint32_t i, j, nr_rt_entries = 0; + size_t sz = sizeof (struct kvm_kernel_irq_routing_entry); + int r; + + for (i = 0; i < nr; ++i) { + if (ue[i].gsi >= KVM_MAX_IRQ_ROUTES) + return (-EINVAL); + nr_rt_entries = max(nr_rt_entries, ue[i].gsi); + } + + nr_rt_entries += 1; + +#ifdef XXX + new = kmem_zalloc(sizeof (*new) + (nr_rt_entries * sizeof (list_t)) + + (nr * sz), KM_SLEEP); + + new->rt_entries = (void *)&new->map[nr_rt_entries]; +#else + XXX_KVM_PROBE; + new = kmem_zalloc(sizeof (*new), KM_SLEEP); + + for (i = 0; i < KVM_MAX_IRQ_ROUTES; i++) { + list_create(&new->map[i], sz, + offsetof(struct kvm_kernel_irq_routing_entry, link)); + } + + new->rt_entries = kmem_zalloc(sz * nr, KM_SLEEP); + +#endif + + new->nr_rt_entries = nr_rt_entries; + for (i = 0; i < 3; i++) + for (j = 0; j < KVM_IOAPIC_NUM_PINS; j++) + new->chip[i][j] = -1; + + for (i = 0; i < nr; ++i) { + r = -EINVAL; + if (ue->flags) + goto out; + r = setup_routing_entry(new, + (struct kvm_kernel_irq_routing_entry *) + ((caddr_t)new->rt_entries + (i * sz)), ue); + + if (r) + goto out; + ++ue; + } + + mutex_enter(&kvm->irq_lock); + old = kvm->irq_routing; +#ifdef XXX + rcu_assign_pointer(kvm->irq_routing, new); +#else + XXX_KVM_SYNC_PROBE; + kvm->irq_routing = new; +#endif + mutex_exit(&kvm->irq_lock); +#ifdef XXX + synchronize_rcu(); +#else + XXX_KVM_SYNC_PROBE; +#endif + + new = old; + r = 0; + +out: + if (new) { + if (new->rt_entries != NULL) + kmem_free(new->rt_entries, sz * nr); + + kmem_free(new, sizeof (*new)); + } + return (r); +} + +#define IOAPIC_ROUTING_ENTRY(irq) \ + { \ + .gsi = irq, \ + .type = KVM_IRQ_ROUTING_IRQCHIP, \ + .u.irqchip.irqchip = KVM_IRQCHIP_IOAPIC, \ + .u.irqchip.pin = (irq) \ + } + +#define ROUTING_ENTRY1(irq) IOAPIC_ROUTING_ENTRY(irq) + +#ifdef CONFIG_X86 +#define PIC_ROUTING_ENTRY(irq) \ + { \ + .gsi = irq, \ + .type = KVM_IRQ_ROUTING_IRQCHIP, \ + .u.irqchip.irqchip = SELECT_PIC(irq), \ + .u.irqchip.pin = (irq) % 8 \ + } + +#define ROUTING_ENTRY2(irq) \ + IOAPIC_ROUTING_ENTRY(irq), PIC_ROUTING_ENTRY(irq) +#else +#define ROUTING_ENTRY2(irq) \ + IOAPIC_ROUTING_ENTRY(irq) +#endif + +static const struct kvm_irq_routing_entry default_routing[] = { + ROUTING_ENTRY2(0), ROUTING_ENTRY2(1), + ROUTING_ENTRY2(2), ROUTING_ENTRY2(3), + ROUTING_ENTRY2(4), ROUTING_ENTRY2(5), + ROUTING_ENTRY2(6), ROUTING_ENTRY2(7), + ROUTING_ENTRY2(8), ROUTING_ENTRY2(9), + ROUTING_ENTRY2(10), ROUTING_ENTRY2(11), + ROUTING_ENTRY2(12), ROUTING_ENTRY2(13), + ROUTING_ENTRY2(14), ROUTING_ENTRY2(15), + ROUTING_ENTRY1(16), ROUTING_ENTRY1(17), + ROUTING_ENTRY1(18), ROUTING_ENTRY1(19), + ROUTING_ENTRY1(20), ROUTING_ENTRY1(21), + ROUTING_ENTRY1(22), ROUTING_ENTRY1(23), +#ifdef CONFIG_IA64 + ROUTING_ENTRY1(24), ROUTING_ENTRY1(25), + ROUTING_ENTRY1(26), ROUTING_ENTRY1(27), + ROUTING_ENTRY1(28), ROUTING_ENTRY1(29), + ROUTING_ENTRY1(30), ROUTING_ENTRY1(31), + ROUTING_ENTRY1(32), ROUTING_ENTRY1(33), + ROUTING_ENTRY1(34), ROUTING_ENTRY1(35), + ROUTING_ENTRY1(36), ROUTING_ENTRY1(37), + ROUTING_ENTRY1(38), ROUTING_ENTRY1(39), + ROUTING_ENTRY1(40), ROUTING_ENTRY1(41), + ROUTING_ENTRY1(42), ROUTING_ENTRY1(43), + ROUTING_ENTRY1(44), ROUTING_ENTRY1(45), + ROUTING_ENTRY1(46), ROUTING_ENTRY1(47), +#endif +}; + +int +kvm_setup_default_irq_routing(struct kvm *kvm) +{ + return (kvm_set_irq_routing(kvm, default_routing, + ARRAY_SIZE(default_routing), 0)); +} @@ -468,16 +468,6 @@ apic_x2apic_mode(struct kvm_lapic *apic) extern unsigned long kvm_rip_read(struct kvm_vcpu *vcpu); -inline static int -kvm_is_dm_lowest_prio(struct kvm_lapic_irq *irq) -{ -#ifdef CONFIG_IA64 - return (irq->delivery_mode == - (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT)); -#else - return (irq->delivery_mode == APIC_DM_LOWEST); -#endif -} void kvm_inject_nmi(struct kvm_vcpu *vcpu) @@ -485,41 +475,6 @@ kvm_inject_nmi(struct kvm_vcpu *vcpu) vcpu->arch.nmi_pending = 1; } -int -kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, - struct kvm_lapic_irq *irq) -{ - int i, r = -1; - struct kvm_vcpu *vcpu, *lowest = NULL; - - if (irq->dest_mode == 0 && irq->dest_id == 0xff && - kvm_is_dm_lowest_prio(irq)) - cmn_err(CE_NOTE, "kvm: apic: phys broadcast and lowest prio\n"); - - kvm_for_each_vcpu(i, vcpu, kvm) { - if (!kvm_apic_present(vcpu)) - continue; - - if (!kvm_apic_match_dest(vcpu, src, irq->shorthand, - irq->dest_id, irq->dest_mode)) - continue; - - if (!kvm_is_dm_lowest_prio(irq)) { - if (r < 0) - r = 0; - r += kvm_apic_set_irq(vcpu, irq); - } else { - if (!lowest) - lowest = vcpu; - else if (kvm_apic_compare_prio(vcpu, lowest) < 0) - lowest = vcpu; - } - } - if (lowest) - r = kvm_apic_set_irq(lowest, irq); - - return (r); -} static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq) |