diff options
Diffstat (limited to 'arch/x86/kvm/ioapic.c')
-rw-r--r-- | arch/x86/kvm/ioapic.c | 34 |
1 files changed, 25 insertions, 9 deletions
diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c index bdff437acbcb..4e822ad363f3 100644 --- a/arch/x86/kvm/ioapic.c +++ b/arch/x86/kvm/ioapic.c | |||
@@ -209,12 +209,12 @@ static int ioapic_set_irq(struct kvm_ioapic *ioapic, unsigned int irq, | |||
209 | 209 | ||
210 | old_irr = ioapic->irr; | 210 | old_irr = ioapic->irr; |
211 | ioapic->irr |= mask; | 211 | ioapic->irr |= mask; |
212 | if (edge) | 212 | if (edge) { |
213 | ioapic->irr_delivered &= ~mask; | 213 | ioapic->irr_delivered &= ~mask; |
214 | if ((edge && old_irr == ioapic->irr) || | 214 | if (old_irr == ioapic->irr) { |
215 | (!edge && entry.fields.remote_irr)) { | 215 | ret = 0; |
216 | ret = 0; | 216 | goto out; |
217 | goto out; | 217 | } |
218 | } | 218 | } |
219 | 219 | ||
220 | ret = ioapic_service(ioapic, irq, line_status); | 220 | ret = ioapic_service(ioapic, irq, line_status); |
@@ -257,8 +257,7 @@ void kvm_ioapic_scan_entry(struct kvm_vcpu *vcpu, ulong *ioapic_handled_vectors) | |||
257 | index == RTC_GSI) { | 257 | index == RTC_GSI) { |
258 | if (kvm_apic_match_dest(vcpu, NULL, 0, | 258 | if (kvm_apic_match_dest(vcpu, NULL, 0, |
259 | e->fields.dest_id, e->fields.dest_mode) || | 259 | e->fields.dest_id, e->fields.dest_mode) || |
260 | (e->fields.trig_mode == IOAPIC_EDGE_TRIG && | 260 | kvm_apic_pending_eoi(vcpu, e->fields.vector)) |
261 | kvm_apic_pending_eoi(vcpu, e->fields.vector))) | ||
262 | __set_bit(e->fields.vector, | 261 | __set_bit(e->fields.vector, |
263 | ioapic_handled_vectors); | 262 | ioapic_handled_vectors); |
264 | } | 263 | } |
@@ -277,6 +276,7 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val) | |||
277 | { | 276 | { |
278 | unsigned index; | 277 | unsigned index; |
279 | bool mask_before, mask_after; | 278 | bool mask_before, mask_after; |
279 | int old_remote_irr, old_delivery_status; | ||
280 | union kvm_ioapic_redirect_entry *e; | 280 | union kvm_ioapic_redirect_entry *e; |
281 | 281 | ||
282 | switch (ioapic->ioregsel) { | 282 | switch (ioapic->ioregsel) { |
@@ -299,14 +299,28 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val) | |||
299 | return; | 299 | return; |
300 | e = &ioapic->redirtbl[index]; | 300 | e = &ioapic->redirtbl[index]; |
301 | mask_before = e->fields.mask; | 301 | mask_before = e->fields.mask; |
302 | /* Preserve read-only fields */ | ||
303 | old_remote_irr = e->fields.remote_irr; | ||
304 | old_delivery_status = e->fields.delivery_status; | ||
302 | if (ioapic->ioregsel & 1) { | 305 | if (ioapic->ioregsel & 1) { |
303 | e->bits &= 0xffffffff; | 306 | e->bits &= 0xffffffff; |
304 | e->bits |= (u64) val << 32; | 307 | e->bits |= (u64) val << 32; |
305 | } else { | 308 | } else { |
306 | e->bits &= ~0xffffffffULL; | 309 | e->bits &= ~0xffffffffULL; |
307 | e->bits |= (u32) val; | 310 | e->bits |= (u32) val; |
308 | e->fields.remote_irr = 0; | ||
309 | } | 311 | } |
312 | e->fields.remote_irr = old_remote_irr; | ||
313 | e->fields.delivery_status = old_delivery_status; | ||
314 | |||
315 | /* | ||
316 | * Some OSes (Linux, Xen) assume that Remote IRR bit will | ||
317 | * be cleared by IOAPIC hardware when the entry is configured | ||
318 | * as edge-triggered. This behavior is used to simulate an | ||
319 | * explicit EOI on IOAPICs that don't have the EOI register. | ||
320 | */ | ||
321 | if (e->fields.trig_mode == IOAPIC_EDGE_TRIG) | ||
322 | e->fields.remote_irr = 0; | ||
323 | |||
310 | mask_after = e->fields.mask; | 324 | mask_after = e->fields.mask; |
311 | if (mask_before != mask_after) | 325 | if (mask_before != mask_after) |
312 | kvm_fire_mask_notifiers(ioapic->kvm, KVM_IRQCHIP_IOAPIC, index, mask_after); | 326 | kvm_fire_mask_notifiers(ioapic->kvm, KVM_IRQCHIP_IOAPIC, index, mask_after); |
@@ -324,7 +338,9 @@ static int ioapic_service(struct kvm_ioapic *ioapic, int irq, bool line_status) | |||
324 | struct kvm_lapic_irq irqe; | 338 | struct kvm_lapic_irq irqe; |
325 | int ret; | 339 | int ret; |
326 | 340 | ||
327 | if (entry->fields.mask) | 341 | if (entry->fields.mask || |
342 | (entry->fields.trig_mode == IOAPIC_LEVEL_TRIG && | ||
343 | entry->fields.remote_irr)) | ||
328 | return -1; | 344 | return -1; |
329 | 345 | ||
330 | ioapic_debug("dest=%x dest_mode=%x delivery_mode=%x " | 346 | ioapic_debug("dest=%x dest_mode=%x delivery_mode=%x " |