mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
IRQ injection changes for HVM PCI passthru.
Signed-off-by: Allen Kay <allen.m.kay@intel.com> Signed-off-by: Guy Zana <guy@neocleus.com>
This commit is contained in:
1 parent
2af556c077
commit
7b3cb5e541
12 files changed
+174
-59
No files matched your search
@@ -26,7 +26,7 @@
|
||||
#include <asm/hvm/domain.h>
|
||||
#include <asm/hvm/support.h>
|
||||
|
||||
static void __hvm_pci_intx_assert(
|
||||
void __hvm_pci_intx_assert(
|
||||
struct domain *d, unsigned int device, unsigned int intx)
|
||||
{
|
||||
struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
|
||||
@@ -59,7 +59,7 @@ void hvm_pci_intx_assert(
|
||||
spin_unlock(&d->arch.hvm_domain.irq_lock);
|
||||
}
|
||||
|
||||
static void __hvm_pci_intx_deassert(
|
||||
void __hvm_pci_intx_deassert(
|
||||
struct domain *d, unsigned int device, unsigned int intx)
|
||||
{
|
||||
struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
|
||||
|
||||
@@ -458,6 +458,10 @@ void vioapic_update_EOI(struct domain *d, int vector)
|
||||
ent = &vioapic->redirtbl[gsi];
|
||||
|
||||
ent->fields.remote_irr = 0;
|
||||
|
||||
if ( vtd_enabled )
|
||||
hvm_dpci_eoi(gsi, ent);
|
||||
|
||||
if ( (ent->fields.trig_mode == VIOAPIC_LEVEL_TRIG) &&
|
||||
!ent->fields.mask &&
|
||||
hvm_irq->gsi_assert_count[gsi] )
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
subdir-y += vtd
|
||||
|
||||
subdir-$(x86_32) += x86_32
|
||||
subdir-$(x86_64) += x86_64
|
||||
|
||||
|
||||
@@ -138,6 +138,23 @@ static void update_tpr_threshold(
|
||||
__vmwrite(TPR_THRESHOLD, threshold);
|
||||
}
|
||||
|
||||
static void vmx_dirq_assist(struct domain *d)
|
||||
{
|
||||
unsigned int irq;
|
||||
uint32_t device, intx;
|
||||
struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
|
||||
|
||||
for ( irq = find_first_bit(hvm_irq->dirq_mask, NR_IRQS);
|
||||
irq < NR_IRQS;
|
||||
irq = find_next_bit(hvm_irq->dirq_mask, NR_IRQS, irq + 1) )
|
||||
{
|
||||
test_and_clear_bit(irq, &hvm_irq->dirq_mask);
|
||||
device = hvm_irq->mirq[irq].device;
|
||||
intx = hvm_irq->mirq[irq].intx;
|
||||
hvm_pci_intx_assert(d, device, intx);
|
||||
}
|
||||
}
|
||||
|
||||
asmlinkage void vmx_intr_assist(void)
|
||||
{
|
||||
int intr_vector;
|
||||
@@ -147,6 +164,10 @@ asmlinkage void vmx_intr_assist(void)
|
||||
|
||||
/* Crank the handle on interrupt state. */
|
||||
pt_update_irq(v);
|
||||
|
||||
if ( vtd_enabled && (v->vcpu_id == 0) )
|
||||
vmx_dirq_assist(v->domain);
|
||||
|
||||
hvm_set_callback_irq_level();
|
||||
|
||||
do {
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
#include "pci-direct.h"
|
||||
#include "pci_regs.h"
|
||||
|
||||
#define VTDPREFIX
|
||||
int vtd_enabled;
|
||||
boolean_param("vtd", vtd_enabled);
|
||||
|
||||
#undef PREFIX
|
||||
#define PREFIX VTDPREFIX "ACPI DMAR:"
|
||||
#define DEBUG
|
||||
@@ -484,11 +488,19 @@ acpi_parse_dmar(unsigned long phys_addr, unsigned long size)
|
||||
|
||||
int acpi_dmar_init(void)
|
||||
{
|
||||
extern int ioapic_ack_new;
|
||||
|
||||
acpi_table_parse(ACPI_DMAR, acpi_parse_dmar);
|
||||
|
||||
if (list_empty(&acpi_drhd_units)) {
|
||||
printk(KERN_ERR PREFIX "No DMAR devices found\n");
|
||||
vtd_enabled = 0;
|
||||
return -ENODEV;
|
||||
} else
|
||||
vtd_enabled = 1;
|
||||
}
|
||||
|
||||
/* Use fake-vector style of IOAPIC acknowledgement. */
|
||||
if (vtd_enabled)
|
||||
ioapic_ack_new = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -34,10 +34,16 @@
|
||||
#include "pci_regs.h"
|
||||
#include "msi.h"
|
||||
|
||||
#define VTDPREFIX
|
||||
static inline int request_irq(int vector, void *func,
|
||||
int flags, char *name, void *data)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
extern void print_iommu_regs(struct acpi_drhd_unit *drhd);
|
||||
extern void print_vtd_entries(struct domain *d, int bus, int devfn,
|
||||
unsigned long gmfn);
|
||||
extern void (*interrupt[])(void);
|
||||
|
||||
#define DMAR_OPERATION_TIMEOUT (HZ*60) /* 1m */
|
||||
|
||||
@@ -831,7 +837,6 @@ static struct hw_interrupt_type dma_msi_type = {
|
||||
int iommu_set_interrupt(struct iommu *iommu)
|
||||
{
|
||||
int vector, ret;
|
||||
unsigned long flags;
|
||||
|
||||
vector = assign_irq_vector(AUTO_ASSIGN);
|
||||
vector_to_iommu[vector] = iommu;
|
||||
@@ -845,10 +850,7 @@ int iommu_set_interrupt(struct iommu *iommu)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&irq_desc[vector].lock, flags);
|
||||
irq_desc[vector].handler = &dma_msi_type;
|
||||
spin_unlock_irqrestore(&irq_desc[vector].lock, flags);
|
||||
set_intr_gate(vector, interrupt[vector]);
|
||||
ret = request_irq(vector, iommu_page_fault, 0, "dmar", iommu);
|
||||
if (ret)
|
||||
gdprintk(XENLOG_ERR VTDPREFIX, "IOMMU: can't request irq\n");
|
||||
|
||||
@@ -50,33 +50,31 @@ int hvm_do_IRQ_dpci(struct domain *d, unsigned int mirq)
|
||||
uint32_t link, isa_irq;
|
||||
struct hvm_irq *hvm_irq;
|
||||
|
||||
if (!vtd_enabled || (d == dom0))
|
||||
if ( !vtd_enabled || (d == dom0) ||
|
||||
!d->arch.hvm_domain.irq.mirq[mirq].valid )
|
||||
return 0;
|
||||
|
||||
if (d->arch.hvm_domain.irq.mirq[mirq].valid)
|
||||
device = d->arch.hvm_domain.irq.mirq[mirq].device;
|
||||
intx = d->arch.hvm_domain.irq.mirq[mirq].intx;
|
||||
link = hvm_pci_intx_link(device, intx);
|
||||
hvm_irq = &d->arch.hvm_domain.irq;
|
||||
isa_irq = hvm_irq->pci_link.route[link];
|
||||
|
||||
if ( !d->arch.hvm_domain.irq.girq[isa_irq].valid )
|
||||
{
|
||||
device = d->arch.hvm_domain.irq.mirq[mirq].device;
|
||||
intx = d->arch.hvm_domain.irq.mirq[mirq].intx;
|
||||
link = hvm_pci_intx_link(device, intx);
|
||||
hvm_irq = &d->arch.hvm_domain.irq;
|
||||
isa_irq = hvm_irq->pci_link.route[link];
|
||||
|
||||
if ( !d->arch.hvm_domain.irq.girq[isa_irq].valid )
|
||||
{
|
||||
d->arch.hvm_domain.irq.girq[isa_irq].valid = 1;
|
||||
d->arch.hvm_domain.irq.girq[isa_irq].device = device;
|
||||
d->arch.hvm_domain.irq.girq[isa_irq].intx = intx;
|
||||
d->arch.hvm_domain.irq.girq[isa_irq].machine_gsi = mirq;
|
||||
}
|
||||
|
||||
if ( !test_and_set_bit(mirq, d->arch.hvm_domain.irq.dirq_mask) )
|
||||
{
|
||||
vcpu_kick(d->vcpu[0]);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
dprintk(XENLOG_INFO, "Want to pending mirq, but failed\n");
|
||||
d->arch.hvm_domain.irq.girq[isa_irq].valid = 1;
|
||||
d->arch.hvm_domain.irq.girq[isa_irq].device = device;
|
||||
d->arch.hvm_domain.irq.girq[isa_irq].intx = intx;
|
||||
d->arch.hvm_domain.irq.girq[isa_irq].machine_gsi = mirq;
|
||||
}
|
||||
|
||||
if ( !test_and_set_bit(mirq, d->arch.hvm_domain.irq.dirq_mask) )
|
||||
{
|
||||
vcpu_kick(d->vcpu[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dprintk(XENLOG_INFO, "mirq already pending\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -86,18 +84,21 @@ void hvm_dpci_eoi(unsigned int guest_gsi, union vioapic_redir_entry *ent)
|
||||
uint32_t device, intx, machine_gsi;
|
||||
irq_desc_t *desc;
|
||||
|
||||
if (d->arch.hvm_domain.irq.girq[guest_gsi].valid)
|
||||
ASSERT(spin_is_locked(&d->arch.hvm_domain.irq_lock));
|
||||
|
||||
if ( !vtd_enabled || !d->arch.hvm_domain.irq.girq[guest_gsi].valid )
|
||||
return;
|
||||
|
||||
device = d->arch.hvm_domain.irq.girq[guest_gsi].device;
|
||||
intx = d->arch.hvm_domain.irq.girq[guest_gsi].intx;
|
||||
machine_gsi = d->arch.hvm_domain.irq.girq[guest_gsi].machine_gsi;
|
||||
gdprintk(XENLOG_INFO, "hvm_dpci_eoi:: device %x intx %x\n",
|
||||
device, intx);
|
||||
__hvm_pci_intx_deassert(d, device, intx);
|
||||
if ( (ent == NULL) || (ent->fields.mask == 0) )
|
||||
{
|
||||
device = d->arch.hvm_domain.irq.girq[guest_gsi].device;
|
||||
intx = d->arch.hvm_domain.irq.girq[guest_gsi].intx;
|
||||
machine_gsi = d->arch.hvm_domain.irq.girq[guest_gsi].machine_gsi;
|
||||
gdprintk(XENLOG_INFO, "hvm_dpci_eoi:: device %x intx %x\n",
|
||||
device, intx);
|
||||
hvm_pci_intx_deassert(d, device, intx);
|
||||
if ( (ent == NULL) || (ent && ent->fields.mask == 0) ) {
|
||||
desc = &irq_desc[irq_to_vector(machine_gsi)];
|
||||
desc->handler->end(irq_to_vector(machine_gsi));
|
||||
}
|
||||
desc = &irq_desc[irq_to_vector(machine_gsi)];
|
||||
desc->handler->end(irq_to_vector(machine_gsi));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,14 +108,13 @@ int release_devices(struct domain *d)
|
||||
uint32_t i;
|
||||
int ret = 0;
|
||||
|
||||
if (!vtd_enabled)
|
||||
if ( !vtd_enabled )
|
||||
return ret;
|
||||
|
||||
/* unbind irq */
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
if (hd->irq.mirq[i].valid)
|
||||
for ( i = 0; i < NR_IRQS; i++ )
|
||||
if ( hd->irq.mirq[i].valid )
|
||||
ret = pirq_guest_unbind(d, i);
|
||||
}
|
||||
|
||||
iommu_domain_teardown(d);
|
||||
return ret;
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
#define NR_HP_RESERVED_VECTORS 20
|
||||
|
||||
extern int vector_irq[NR_VECTORS];
|
||||
extern void (*interrupt[NR_IRQS])(void);
|
||||
extern int pci_vector_resources(int last, int nr_released);
|
||||
|
||||
/*
|
||||
|
||||
@@ -182,8 +182,7 @@ static void vpic_ioport_write(
|
||||
|
||||
vpic_lock(vpic);
|
||||
|
||||
addr &= 1;
|
||||
if ( addr == 0 )
|
||||
if ( (addr & 1) == 0 )
|
||||
{
|
||||
if ( val & 0x10 )
|
||||
{
|
||||
@@ -250,6 +249,11 @@ static void vpic_ioport_write(
|
||||
vpic->isr &= ~(1 << irq);
|
||||
if ( cmd == 7 )
|
||||
vpic->priority_add = (irq + 1) & 7;
|
||||
if ( vtd_enabled )
|
||||
{
|
||||
irq |= ((addr & 0xa0) == 0xa0) ? 8 : 0;
|
||||
hvm_dpci_eoi(hvm_isa_irq_to_gsi(irq), NULL);
|
||||
}
|
||||
break;
|
||||
case 6: /* Set Priority */
|
||||
vpic->priority_add = (val + 1) & 7;
|
||||
|
||||
+73
-3
@@ -184,6 +184,68 @@ static void __modify_IO_APIC_irq (unsigned int irq, unsigned long enable, unsign
|
||||
}
|
||||
}
|
||||
|
||||
static int real_vector[MAX_IRQ_SOURCES];
|
||||
static int fake_vector=-1;
|
||||
|
||||
/*
|
||||
* Following 2 functions are used to workaround spurious interrupt
|
||||
* problem related to mask/unmask of interrupts. Instead we program
|
||||
* an unused vector in the IOAPIC before issueing EOI to LAPIC.
|
||||
*/
|
||||
static void write_fake_IO_APIC_vector (unsigned int irq)
|
||||
{
|
||||
struct irq_pin_list *entry = irq_2_pin + irq;
|
||||
unsigned int pin, reg;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ioapic_lock, flags);
|
||||
for (;;) {
|
||||
pin = entry->pin;
|
||||
if (pin == -1)
|
||||
break;
|
||||
reg = io_apic_read(entry->apic, 0x10 + pin*2);
|
||||
real_vector[irq] = reg & 0xff;
|
||||
reg &= ~0xff;
|
||||
|
||||
if (fake_vector == -1)
|
||||
fake_vector = assign_irq_vector(MAX_IRQ_SOURCES-1);
|
||||
|
||||
reg |= fake_vector;
|
||||
io_apic_write(entry->apic, 0x10 + pin*2, reg);
|
||||
|
||||
if (!entry->next)
|
||||
break;
|
||||
entry = irq_2_pin + entry->next;
|
||||
}
|
||||
spin_unlock_irqrestore(&ioapic_lock, flags);
|
||||
}
|
||||
|
||||
static void restore_real_IO_APIC_vector (unsigned int irq)
|
||||
{
|
||||
struct irq_pin_list *entry = irq_2_pin + irq;
|
||||
unsigned int pin, reg;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ioapic_lock, flags);
|
||||
for (;;) {
|
||||
pin = entry->pin;
|
||||
if (pin == -1)
|
||||
break;
|
||||
|
||||
reg = io_apic_read(entry->apic, 0x10 + pin*2);
|
||||
reg &= ~0xff;
|
||||
reg |= real_vector[irq];
|
||||
io_apic_write(entry->apic, 0x10 + pin*2, reg);
|
||||
mb();
|
||||
*(IO_APIC_BASE(entry->apic) + 0x10) = reg & 0xff;
|
||||
|
||||
if (!entry->next)
|
||||
break;
|
||||
entry = irq_2_pin + entry->next;
|
||||
}
|
||||
spin_unlock_irqrestore(&ioapic_lock, flags);
|
||||
}
|
||||
|
||||
/* mask = 1 */
|
||||
static void __mask_IO_APIC_irq (unsigned int irq)
|
||||
{
|
||||
@@ -1356,7 +1418,11 @@ static void mask_and_ack_level_ioapic_irq (unsigned int irq)
|
||||
if ( ioapic_ack_new )
|
||||
return;
|
||||
|
||||
mask_IO_APIC_irq(irq);
|
||||
if ( vtd_enabled )
|
||||
write_fake_IO_APIC_vector(irq);
|
||||
else
|
||||
mask_IO_APIC_irq(irq);
|
||||
|
||||
/*
|
||||
* It appears there is an erratum which affects at least version 0x11
|
||||
* of I/O APIC (that's the 82093AA and cores integrated into various
|
||||
@@ -1398,8 +1464,12 @@ static void end_level_ioapic_irq (unsigned int irq)
|
||||
|
||||
if ( !ioapic_ack_new )
|
||||
{
|
||||
if ( !(irq_desc[IO_APIC_VECTOR(irq)].status & IRQ_DISABLED) )
|
||||
unmask_IO_APIC_irq(irq);
|
||||
if ( !(irq_desc[IO_APIC_VECTOR(irq)].status & IRQ_DISABLED) ) {
|
||||
if ( vtd_enabled )
|
||||
restore_real_IO_APIC_vector(irq);
|
||||
else
|
||||
unmask_IO_APIC_irq(irq);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,8 +114,12 @@ struct hvm_irq {
|
||||
#define hvm_isa_irq_to_gsi(isa_irq) ((isa_irq) ? : 2)
|
||||
|
||||
/* Modify state of a PCI INTx wire. */
|
||||
void __hvm_pci_intx_assert(
|
||||
struct domain *d, unsigned int device, unsigned int intx);
|
||||
void hvm_pci_intx_assert(
|
||||
struct domain *d, unsigned int device, unsigned int intx);
|
||||
void __hvm_pci_intx_deassert(
|
||||
struct domain *d, unsigned int device, unsigned int intx);
|
||||
void hvm_pci_intx_deassert(
|
||||
struct domain *d, unsigned int device, unsigned int intx);
|
||||
|
||||
|
||||
@@ -64,9 +64,6 @@ extern irq_desc_t irq_desc[NR_IRQS];
|
||||
|
||||
extern int setup_irq(unsigned int, struct irqaction *);
|
||||
extern void free_irq(unsigned int);
|
||||
extern int request_irq(unsigned int irq,
|
||||
void (*handler)(int, void *, struct cpu_user_regs *),
|
||||
unsigned long irqflags, const char * devname, void *dev_id);
|
||||
|
||||
extern hw_irq_controller no_irq_type;
|
||||
extern void no_action(int cpl, void *dev_id, struct cpu_user_regs *regs);
|
||||
@@ -80,11 +77,11 @@ extern int pirq_guest_unbind(struct domain *d, int irq);
|
||||
|
||||
static inline void set_native_irq_info(int irq, cpumask_t mask)
|
||||
{
|
||||
irq_desc[irq].affinity = mask;
|
||||
irq_desc[irq].affinity = mask;
|
||||
}
|
||||
|
||||
static inline void set_irq_info(int irq, cpumask_t mask)
|
||||
{
|
||||
set_native_irq_info(irq, mask);
|
||||
set_native_irq_info(irq, mask);
|
||||
}
|
||||
#endif /* __XEN_IRQ_H__ */
|
||||