diff options
Diffstat (limited to 'virt/kvm/irq_comm.c')
-rw-r--r-- | virt/kvm/irq_comm.c | 78 |
1 files changed, 71 insertions, 7 deletions
diff --git a/virt/kvm/irq_comm.c b/virt/kvm/irq_comm.c index 7aa5086c8622..6bc7439eff6e 100644 --- a/virt/kvm/irq_comm.c +++ b/virt/kvm/irq_comm.c | |||
@@ -20,6 +20,11 @@ | |||
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/kvm_host.h> | 22 | #include <linux/kvm_host.h> |
23 | |||
24 | #ifdef CONFIG_X86 | ||
25 | #include <asm/msidef.h> | ||
26 | #endif | ||
27 | |||
23 | #include "irq.h" | 28 | #include "irq.h" |
24 | 29 | ||
25 | #include "ioapic.h" | 30 | #include "ioapic.h" |
@@ -38,17 +43,70 @@ static void kvm_set_ioapic_irq(struct kvm_kernel_irq_routing_entry *e, | |||
38 | kvm_ioapic_set_irq(kvm->arch.vioapic, e->irqchip.pin, level); | 43 | kvm_ioapic_set_irq(kvm->arch.vioapic, e->irqchip.pin, level); |
39 | } | 44 | } |
40 | 45 | ||
46 | static void kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, | ||
47 | struct kvm *kvm, int level) | ||
48 | { | ||
49 | int vcpu_id; | ||
50 | struct kvm_vcpu *vcpu; | ||
51 | struct kvm_ioapic *ioapic = ioapic_irqchip(kvm); | ||
52 | int dest_id = (e->msi.address_lo & MSI_ADDR_DEST_ID_MASK) | ||
53 | >> MSI_ADDR_DEST_ID_SHIFT; | ||
54 | int vector = (e->msi.data & MSI_DATA_VECTOR_MASK) | ||
55 | >> MSI_DATA_VECTOR_SHIFT; | ||
56 | int dest_mode = test_bit(MSI_ADDR_DEST_MODE_SHIFT, | ||
57 | (unsigned long *)&e->msi.address_lo); | ||
58 | int trig_mode = test_bit(MSI_DATA_TRIGGER_SHIFT, | ||
59 | (unsigned long *)&e->msi.data); | ||
60 | int delivery_mode = test_bit(MSI_DATA_DELIVERY_MODE_SHIFT, | ||
61 | (unsigned long *)&e->msi.data); | ||
62 | u32 deliver_bitmask; | ||
63 | |||
64 | BUG_ON(!ioapic); | ||
65 | |||
66 | deliver_bitmask = kvm_ioapic_get_delivery_bitmask(ioapic, | ||
67 | dest_id, dest_mode); | ||
68 | /* IOAPIC delivery mode value is the same as MSI here */ | ||
69 | switch (delivery_mode) { | ||
70 | case IOAPIC_LOWEST_PRIORITY: | ||
71 | vcpu = kvm_get_lowest_prio_vcpu(ioapic->kvm, vector, | ||
72 | deliver_bitmask); | ||
73 | if (vcpu != NULL) | ||
74 | kvm_apic_set_irq(vcpu, vector, trig_mode); | ||
75 | else | ||
76 | printk(KERN_INFO "kvm: null lowest priority vcpu!\n"); | ||
77 | break; | ||
78 | case IOAPIC_FIXED: | ||
79 | for (vcpu_id = 0; deliver_bitmask != 0; vcpu_id++) { | ||
80 | if (!(deliver_bitmask & (1 << vcpu_id))) | ||
81 | continue; | ||
82 | deliver_bitmask &= ~(1 << vcpu_id); | ||
83 | vcpu = ioapic->kvm->vcpus[vcpu_id]; | ||
84 | if (vcpu) | ||
85 | kvm_apic_set_irq(vcpu, vector, trig_mode); | ||
86 | } | ||
87 | break; | ||
88 | default: | ||
89 | break; | ||
90 | } | ||
91 | } | ||
92 | |||
41 | /* This should be called with the kvm->lock mutex held */ | 93 | /* This should be called with the kvm->lock mutex held */ |
42 | void kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level) | 94 | void kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level) |
43 | { | 95 | { |
44 | struct kvm_kernel_irq_routing_entry *e; | 96 | struct kvm_kernel_irq_routing_entry *e; |
45 | unsigned long *irq_state = (unsigned long *)&kvm->arch.irq_states[irq]; | 97 | unsigned long *irq_state, sig_level; |
98 | |||
99 | if (irq < KVM_IOAPIC_NUM_PINS) { | ||
100 | irq_state = (unsigned long *)&kvm->arch.irq_states[irq]; | ||
46 | 101 | ||
47 | /* Logical OR for level trig interrupt */ | 102 | /* Logical OR for level trig interrupt */ |
48 | if (level) | 103 | if (level) |
49 | set_bit(irq_source_id, irq_state); | 104 | set_bit(irq_source_id, irq_state); |
50 | else | 105 | else |
51 | clear_bit(irq_source_id, irq_state); | 106 | clear_bit(irq_source_id, irq_state); |
107 | sig_level = !!(*irq_state); | ||
108 | } else /* Deal with MSI/MSI-X */ | ||
109 | sig_level = 1; | ||
52 | 110 | ||
53 | /* Not possible to detect if the guest uses the PIC or the | 111 | /* Not possible to detect if the guest uses the PIC or the |
54 | * IOAPIC. So set the bit in both. The guest will ignore | 112 | * IOAPIC. So set the bit in both. The guest will ignore |
@@ -56,7 +114,7 @@ void kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level) | |||
56 | */ | 114 | */ |
57 | list_for_each_entry(e, &kvm->irq_routing, link) | 115 | list_for_each_entry(e, &kvm->irq_routing, link) |
58 | if (e->gsi == irq) | 116 | if (e->gsi == irq) |
59 | e->set(e, kvm, !!(*irq_state)); | 117 | e->set(e, kvm, sig_level); |
60 | } | 118 | } |
61 | 119 | ||
62 | void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin) | 120 | void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin) |
@@ -186,6 +244,12 @@ int setup_routing_entry(struct kvm_kernel_irq_routing_entry *e, | |||
186 | e->irqchip.irqchip = ue->u.irqchip.irqchip; | 244 | e->irqchip.irqchip = ue->u.irqchip.irqchip; |
187 | e->irqchip.pin = ue->u.irqchip.pin + delta; | 245 | e->irqchip.pin = ue->u.irqchip.pin + delta; |
188 | break; | 246 | break; |
247 | case KVM_IRQ_ROUTING_MSI: | ||
248 | e->set = kvm_set_msi; | ||
249 | e->msi.address_lo = ue->u.msi.address_lo; | ||
250 | e->msi.address_hi = ue->u.msi.address_hi; | ||
251 | e->msi.data = ue->u.msi.data; | ||
252 | break; | ||
189 | default: | 253 | default: |
190 | goto out; | 254 | goto out; |
191 | } | 255 | } |