aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/hvm/irq.c
diff options
context:
space:
mode:
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>2006-11-21 19:22:25 +0000
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>2006-11-21 19:22:25 +0000
commitd46c9ce0d7b805e6b4bbf54b381728a391e23e45 (patch)
treeea5cbf1372bb5d4f2dbc953eb70a84a90f4cc7d0 /xen/arch/x86/hvm/irq.c
parent3b724073d6a410b5a967a6827d2f8c50693aadfb (diff)
downloadxen-d46c9ce0d7b805e6b4bbf54b381728a391e23e45.tar.gz
xen-d46c9ce0d7b805e6b4bbf54b381728a391e23e45.tar.bz2
xen-d46c9ce0d7b805e6b4bbf54b381728a391e23e45.zip
[HVM] Reworked interrupt distribution logic.
TODO: 1. Fix IO-APIC ID to not conflict with LAPIC IDS. 2. Fix i8259 device model (seems to work already though!). 3. Add INTSRC overrides in MPBIOS and ACPI tables so that PCI legacy IRQ routing always ends up at an IO-APIC input with level trigger. Restricting link routing to {5,6,10,11} and setting overrides for all four of those would work. Signed-off-by: Keir Fraser <keir@xensource.com>
Diffstat (limited to 'xen/arch/x86/hvm/irq.c')
-rw-r--r--xen/arch/x86/hvm/irq.c227
1 files changed, 227 insertions, 0 deletions
diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c
new file mode 100644
index 0000000000..f32e57e2b2
--- /dev/null
+++ b/xen/arch/x86/hvm/irq.c
@@ -0,0 +1,227 @@
+/******************************************************************************
+ * irq.c
+ *
+ * Interrupt distribution and delivery logic.
+ *
+ * Copyright (c) 2006, K A Fraser, XenSource Inc.
+ *
+ * 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.
+ */
+
+#include <xen/config.h>
+#include <xen/types.h>
+#include <xen/event.h>
+#include <xen/sched.h>
+#include <asm/hvm/domain.h>
+
+void hvm_pci_intx_assert(
+ struct domain *d, unsigned int device, unsigned int intx)
+{
+ struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
+ unsigned int gsi, link, isa_irq;
+
+ ASSERT((device <= 31) && (intx <= 3));
+
+ spin_lock(&hvm_irq->lock);
+
+ if ( __test_and_set_bit(device*4 + intx, &hvm_irq->pci_intx) )
+ goto out;
+
+ gsi = hvm_pci_intx_gsi(device, intx);
+ if ( hvm_irq->gsi_assert_count[gsi]++ == 0 )
+ vioapic_irq_positive_edge(d, gsi);
+
+ link = hvm_pci_intx_link(device, intx);
+ isa_irq = hvm_irq->pci_link_route[link];
+ if ( (hvm_irq->pci_link_assert_count[link]++ == 0) && isa_irq &&
+ (hvm_irq->gsi_assert_count[isa_irq]++ == 0) )
+ {
+ vioapic_irq_positive_edge(d, isa_irq);
+ pic_set_irq(&hvm_irq->vpic, isa_irq, 1);
+ }
+
+ out:
+ spin_unlock(&hvm_irq->lock);
+}
+
+void hvm_pci_intx_deassert(
+ struct domain *d, unsigned int device, unsigned int intx)
+{
+ struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
+ unsigned int gsi, link, isa_irq;
+
+ ASSERT((device <= 31) && (intx <= 3));
+
+ spin_lock(&hvm_irq->lock);
+
+ if ( !__test_and_clear_bit(device*4 + intx, &hvm_irq->pci_intx) )
+ goto out;
+
+ gsi = hvm_pci_intx_gsi(device, intx);
+ --hvm_irq->gsi_assert_count[gsi];
+
+ link = hvm_pci_intx_link(device, intx);
+ isa_irq = hvm_irq->pci_link_route[link];
+ if ( (--hvm_irq->pci_link_assert_count[link] == 0) && isa_irq &&
+ (--hvm_irq->gsi_assert_count[isa_irq] == 0) )
+ pic_set_irq(&hvm_irq->vpic, isa_irq, 0);
+
+ out:
+ spin_unlock(&hvm_irq->lock);
+}
+
+void hvm_isa_irq_assert(
+ struct domain *d, unsigned int isa_irq)
+{
+ struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
+
+ ASSERT(isa_irq <= 15);
+
+ spin_lock(&hvm_irq->lock);
+
+ if ( !__test_and_set_bit(isa_irq, &hvm_irq->isa_irq) &&
+ (hvm_irq->gsi_assert_count[isa_irq]++ == 0) )
+ {
+ vioapic_irq_positive_edge(d, isa_irq);
+ pic_set_irq(&hvm_irq->vpic, isa_irq, 1);
+ }
+
+ spin_unlock(&hvm_irq->lock);
+}
+
+void hvm_isa_irq_deassert(
+ struct domain *d, unsigned int isa_irq)
+{
+ struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
+
+ ASSERT(isa_irq <= 15);
+
+ spin_lock(&hvm_irq->lock);
+
+ if ( __test_and_clear_bit(isa_irq, &hvm_irq->isa_irq) &&
+ (--hvm_irq->gsi_assert_count[isa_irq] == 0) )
+ pic_set_irq(&hvm_irq->vpic, isa_irq, 0);
+
+ spin_unlock(&hvm_irq->lock);
+}
+
+void hvm_set_callback_irq_level(void)
+{
+ struct vcpu *v = current;
+ struct domain *d = v->domain;
+ struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
+ unsigned int gsi = hvm_irq->callback_gsi;
+
+ /* Fast lock-free tests. */
+ if ( (v->vcpu_id != 0) || (gsi == 0) )
+ return;
+
+ spin_lock(&hvm_irq->lock);
+
+ gsi = hvm_irq->callback_gsi;
+ if ( gsi == 0 )
+ goto out;
+
+ if ( local_events_need_delivery() )
+ {
+ if ( !__test_and_set_bit(0, &hvm_irq->callback_irq_wire) &&
+ (hvm_irq->gsi_assert_count[gsi]++ == 0) )
+ {
+ vioapic_irq_positive_edge(d, gsi);
+ if ( gsi <= 15 )
+ pic_set_irq(&hvm_irq->vpic, gsi, 1);
+ }
+ }
+ else
+ {
+ if ( __test_and_clear_bit(0, &hvm_irq->callback_irq_wire) &&
+ (--hvm_irq->gsi_assert_count[gsi] == 0) )
+ {
+ if ( gsi <= 15 )
+ pic_set_irq(&hvm_irq->vpic, gsi, 0);
+ }
+ }
+
+ out:
+ spin_unlock(&hvm_irq->lock);
+}
+
+void hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq)
+{
+ struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
+ u8 old_isa_irq;
+
+ ASSERT((link <= 3) && (isa_irq <= 15));
+
+ spin_lock(&hvm_irq->lock);
+
+ old_isa_irq = hvm_irq->pci_link_route[link];
+ if ( old_isa_irq == isa_irq )
+ goto out;
+ hvm_irq->pci_link_route[link] = isa_irq;
+
+ if ( hvm_irq->pci_link_assert_count[link] == 0 )
+ goto out;
+
+ if ( old_isa_irq && (--hvm_irq->gsi_assert_count[old_isa_irq] == 0) )
+ pic_set_irq(&hvm_irq->vpic, isa_irq, 0);
+
+ if ( isa_irq && (hvm_irq->gsi_assert_count[isa_irq]++ == 0) )
+ {
+ vioapic_irq_positive_edge(d, isa_irq);
+ pic_set_irq(&hvm_irq->vpic, isa_irq, 1);
+ }
+
+ out:
+ spin_unlock(&hvm_irq->lock);
+
+ dprintk(XENLOG_G_INFO, "Dom%u PCI link %u changed %u -> %u\n",
+ d->domain_id, link, old_isa_irq, isa_irq);
+}
+
+void hvm_set_callback_gsi(struct domain *d, unsigned int gsi)
+{
+ struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
+ unsigned int old_gsi;
+
+ if ( gsi >= ARRAY_SIZE(hvm_irq->gsi_assert_count) )
+ gsi = 0;
+
+ spin_lock(&hvm_irq->lock);
+
+ old_gsi = hvm_irq->callback_gsi;
+ if ( old_gsi == gsi )
+ goto out;
+ hvm_irq->callback_gsi = gsi;
+
+ if ( !test_bit(0, &hvm_irq->callback_irq_wire) )
+ goto out;
+
+ if ( old_gsi && (--hvm_irq->gsi_assert_count[old_gsi] == 0) )
+ if ( old_gsi <= 15 )
+ pic_set_irq(&hvm_irq->vpic, old_gsi, 0);
+
+ if ( gsi && (hvm_irq->gsi_assert_count[gsi]++ == 0) )
+ {
+ vioapic_irq_positive_edge(d, gsi);
+ if ( gsi <= 15 )
+ pic_set_irq(&hvm_irq->vpic, gsi, 1);
+ }
+
+ out:
+ spin_unlock(&hvm_irq->lock);
+
+ dprintk(XENLOG_G_INFO, "Dom%u callback GSI changed %u -> %u\n",
+ d->domain_id, old_gsi, gsi);
+}