/* * 8259 interrupt controller emulation * * Copyright (c) 2003-2004 Fabrice Bellard * Copyright (c) 2007 Intel Corporation * Copyright 2009 Red Hat, Inc. and/or its affiliates. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * 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 OTHER * 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: * Yaozu (Eddie) Dong <Eddie.dong@intel.com> * Port from Qemu.
*/ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
if (wakeup) {
kvm_for_each_vcpu(i, vcpu, s->kvm) { if (kvm_apic_accept_pic_intr(vcpu)) {
kvm_make_request(KVM_REQ_EVENT, vcpu);
kvm_vcpu_kick(vcpu); return;
}
}
}
}
staticvoid pic_clear_isr(struct kvm_kpic_state *s, int irq)
{
s->isr &= ~(1 << irq); if (s != &s->pics_state->pics[0])
irq += 8; /* * We are dropping lock while calling ack notifiers since ack * notifier callbacks for assigned devices call into PIC recursively. * Other interrupt may be delivered to PIC while lock is dropped but * it should be safe since PIC state is already updated at this stage.
*/
pic_unlock(s->pics_state);
kvm_notify_acked_irq(s->pics_state->kvm, SELECT_PIC(irq), irq);
pic_lock(s->pics_state);
}
/* * set irq level. If an edge is detected, then the IRR is set to 1
*/ staticinlineint pic_set_irq1(struct kvm_kpic_state *s, int irq, int level)
{ int mask, ret = 1;
mask = 1 << irq; if (s->elcr & mask) /* level triggered */ if (level) {
ret = !(s->irr & mask);
s->irr |= mask;
s->last_irr |= mask;
} else {
s->irr &= ~mask;
s->last_irr &= ~mask;
} else/* edge triggered */ if (level) { if ((s->last_irr & mask) == 0) {
ret = !(s->irr & mask);
s->irr |= mask;
}
s->last_irr |= mask;
} else
s->last_irr &= ~mask;
return (s->imr & mask) ? -1 : ret;
}
/* * return the highest priority found in mask (highest = smallest * number). Return 8 if no irq
*/ staticinlineint get_priority(struct kvm_kpic_state *s, int mask)
{ int priority; if (mask == 0) return 8;
priority = 0; while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
priority++; return priority;
}
/* * return the pic wanted interrupt. return -1 if none
*/ staticint pic_get_irq(struct kvm_kpic_state *s)
{ int mask, cur_priority, priority;
mask = s->irr & ~s->imr;
priority = get_priority(s, mask); if (priority == 8) return -1; /* * compute current priority. If special fully nested mode on the * master, the IRQ coming from the slave is not taken into account * for the priority computation.
*/
mask = s->isr; if (s->special_fully_nested_mode && s == &s->pics_state->pics[0])
mask &= ~(1 << 2);
cur_priority = get_priority(s, mask); if (priority < cur_priority) /* * higher priority found: an irq should be generated
*/ return (priority + s->priority_add) & 7; else return -1;
}
/* * raise irq to CPU if necessary. must be called every time the active * irq may change
*/ staticvoid pic_update_irq(struct kvm_pic *s)
{ int irq2, irq;
irq2 = pic_get_irq(&s->pics[1]); if (irq2 >= 0) { /* * if irq request by slave pic, signal master PIC
*/
pic_set_irq1(&s->pics[0], 2, 1);
pic_set_irq1(&s->pics[0], 2, 0);
}
irq = pic_get_irq(&s->pics[0]);
pic_irq_request(s->kvm, irq >= 0);
}
addr &= 1; if (addr == 0) { if (val & 0x10) {
s->init4 = val & 1; if (val & 0x02)
pr_pic_unimpl("single mode not supported"); if (val & 0x08)
pr_pic_unimpl( "level sensitive irq not supported");
kvm_pic_reset(s);
} elseif (val & 0x08) { if (val & 0x04)
s->poll = 1; if (val & 0x02)
s->read_reg_select = val & 1; if (val & 0x40)
s->special_mask = (val >> 5) & 1;
} else {
cmd = val >> 5; switch (cmd) { case 0: case 4:
s->rotate_on_auto_eoi = cmd >> 2; break; case 1: /* end of interrupt */ case 5:
priority = get_priority(s, s->isr); if (priority != 8) {
irq = (priority + s->priority_add) & 7; if (cmd == 5)
s->priority_add = (irq + 1) & 7;
pic_clear_isr(s, irq);
pic_update_irq(s->pics_state);
} break; case 3:
irq = val & 7;
pic_clear_isr(s, irq);
pic_update_irq(s->pics_state); break; case 6:
s->priority_add = (val + 1) & 7;
pic_update_irq(s->pics_state); break; case 7:
irq = val & 7;
s->priority_add = (irq + 1) & 7;
pic_clear_isr(s, irq);
pic_update_irq(s->pics_state); break; default: break; /* no operation */
}
}
} else switch (s->init_state) { case 0: { /* normal mode */
u8 imr_diff = s->imr ^ val,
off = (s == &s->pics_state->pics[0]) ? 0 : 8;
s->imr = val; for (irq = 0; irq < PIC_NUM_PINS/2; irq++) if (imr_diff & (1 << irq))
kvm_fire_mask_notifiers(
s->pics_state->kvm,
SELECT_PIC(irq + off),
irq + off,
!!(s->imr & (1 << irq)));
pic_update_irq(s->pics_state); break;
} case 1:
s->irq_base = val & 0xf8;
s->init_state = 2; break; case 2: if (s->init4)
s->init_state = 3; else
s->init_state = 0; break; case 3:
s->special_fully_nested_mode = (val >> 4) & 1;
s->auto_eoi = (val >> 1) & 1;
s->init_state = 0; break;
}
}
static u32 pic_poll_read(struct kvm_kpic_state *s, u32 addr1)
{ int ret;
ret = pic_get_irq(s); if (ret >= 0) { if (addr1 >> 7) {
s->pics_state->pics[0].isr &= ~(1 << 2);
s->pics_state->pics[0].irr &= ~(1 << 2);
}
s->irr &= ~(1 << ret);
pic_clear_isr(s, ret); if (addr1 >> 7 || ret != 2)
pic_update_irq(s->pics_state); /* Bit 7 is 1, means there's an interrupt */
ret |= 0x80;
} else { /* Bit 7 is 0, means there's no interrupt */
ret = 0x07;
pic_update_irq(s->pics_state);
}
if (s->poll) {
ret = pic_poll_read(s, addr);
s->poll = 0;
} else if ((addr & 1) == 0) if (s->read_reg_select)
ret = s->isr; else
ret = s->irr; else
ret = s->imr; return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.