Commit ffa009c3 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.infradead.org/iommu-2.6

* git://git.infradead.org/iommu-2.6:
  drivers/pci/intr_remapping.c: include acpi.h
  intel-iommu: Fix oops in device_to_iommu() when devices not found.
  intel-iommu: Handle PCI domains appropriately.
  intel-iommu: Fix device-to-iommu mapping for PCI-PCI bridges.
  x2apic/intr-remap: decouple interrupt remapping from x2apic
  x86, dmar: check if it's initialized before disable queue invalidation
  intel-iommu: set compatibility format interrupt
  Intel IOMMU Suspend/Resume Support - Interrupt Remapping
  Intel IOMMU Suspend/Resume Support - Queued Invalidation
  Intel IOMMU Suspend/Resume Support - DMAR
  intel-iommu: Add for_each_iommu() and for_each_active_iommu() macros
parents 8e320d02 46f06b72
...@@ -253,6 +253,7 @@ config SMP ...@@ -253,6 +253,7 @@ config SMP
config X86_X2APIC config X86_X2APIC
bool "Support x2apic" bool "Support x2apic"
depends on X86_LOCAL_APIC && X86_64 depends on X86_LOCAL_APIC && X86_64
select INTR_REMAP
---help--- ---help---
This enables x2apic support on CPUs that have this feature. This enables x2apic support on CPUs that have this feature.
...@@ -1881,7 +1882,6 @@ config DMAR_FLOPPY_WA ...@@ -1881,7 +1882,6 @@ config DMAR_FLOPPY_WA
config INTR_REMAP config INTR_REMAP
bool "Support for Interrupt Remapping (EXPERIMENTAL)" bool "Support for Interrupt Remapping (EXPERIMENTAL)"
depends on X86_64 && X86_IO_APIC && PCI_MSI && ACPI && EXPERIMENTAL depends on X86_64 && X86_IO_APIC && PCI_MSI && ACPI && EXPERIMENTAL
select X86_X2APIC
---help--- ---help---
Supports Interrupt remapping for IO-APIC and MSI devices. Supports Interrupt remapping for IO-APIC and MSI devices.
To use x2apic mode in the CPU's which support x2APIC enhancements or To use x2apic mode in the CPU's which support x2APIC enhancements or
......
...@@ -107,6 +107,9 @@ extern u32 native_safe_apic_wait_icr_idle(void); ...@@ -107,6 +107,9 @@ extern u32 native_safe_apic_wait_icr_idle(void);
extern void native_apic_icr_write(u32 low, u32 id); extern void native_apic_icr_write(u32 low, u32 id);
extern u64 native_apic_icr_read(void); extern u64 native_apic_icr_read(void);
#define EIM_8BIT_APIC_ID 0
#define EIM_32BIT_APIC_ID 1
#ifdef CONFIG_X86_X2APIC #ifdef CONFIG_X86_X2APIC
/* /*
* Make previous memory operations globally visible before * Make previous memory operations globally visible before
......
...@@ -162,10 +162,13 @@ extern int (*ioapic_renumber_irq)(int ioapic, int irq); ...@@ -162,10 +162,13 @@ extern int (*ioapic_renumber_irq)(int ioapic, int irq);
extern void ioapic_init_mappings(void); extern void ioapic_init_mappings(void);
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
extern int save_IO_APIC_setup(void); extern struct IO_APIC_route_entry **alloc_ioapic_entries(void);
extern void mask_IO_APIC_setup(void); extern void free_ioapic_entries(struct IO_APIC_route_entry **ioapic_entries);
extern void restore_IO_APIC_setup(void); extern int save_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries);
extern void reinit_intr_remapped_IO_APIC(int); extern void mask_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries);
extern int restore_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries);
extern void reinit_intr_remapped_IO_APIC(int intr_remapping,
struct IO_APIC_route_entry **ioapic_entries);
#endif #endif
extern void probe_nr_irqs_gsi(void); extern void probe_nr_irqs_gsi(void);
......
...@@ -1304,6 +1304,7 @@ void __init enable_IR_x2apic(void) ...@@ -1304,6 +1304,7 @@ void __init enable_IR_x2apic(void)
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_INTR_REMAP
int ret; int ret;
unsigned long flags; unsigned long flags;
struct IO_APIC_route_entry **ioapic_entries = NULL;
if (!cpu_has_x2apic) if (!cpu_has_x2apic)
return; return;
...@@ -1334,17 +1335,23 @@ void __init enable_IR_x2apic(void) ...@@ -1334,17 +1335,23 @@ void __init enable_IR_x2apic(void)
return; return;
} }
ret = save_IO_APIC_setup(); ioapic_entries = alloc_ioapic_entries();
if (!ioapic_entries) {
pr_info("Allocate ioapic_entries failed: %d\n", ret);
goto end;
}
ret = save_IO_APIC_setup(ioapic_entries);
if (ret) { if (ret) {
pr_info("Saving IO-APIC state failed: %d\n", ret); pr_info("Saving IO-APIC state failed: %d\n", ret);
goto end; goto end;
} }
local_irq_save(flags); local_irq_save(flags);
mask_IO_APIC_setup(); mask_IO_APIC_setup(ioapic_entries);
mask_8259A(); mask_8259A();
ret = enable_intr_remapping(1); ret = enable_intr_remapping(EIM_32BIT_APIC_ID);
if (ret && x2apic_preenabled) { if (ret && x2apic_preenabled) {
local_irq_restore(flags); local_irq_restore(flags);
...@@ -1364,9 +1371,9 @@ void __init enable_IR_x2apic(void) ...@@ -1364,9 +1371,9 @@ void __init enable_IR_x2apic(void)
/* /*
* IR enabling failed * IR enabling failed
*/ */
restore_IO_APIC_setup(); restore_IO_APIC_setup(ioapic_entries);
else else
reinit_intr_remapped_IO_APIC(x2apic_preenabled); reinit_intr_remapped_IO_APIC(x2apic_preenabled, ioapic_entries);
unmask_8259A(); unmask_8259A();
local_irq_restore(flags); local_irq_restore(flags);
...@@ -1379,6 +1386,8 @@ void __init enable_IR_x2apic(void) ...@@ -1379,6 +1386,8 @@ void __init enable_IR_x2apic(void)
pr_info("Enabled Interrupt-remapping\n"); pr_info("Enabled Interrupt-remapping\n");
} else } else
pr_err("Failed to enable Interrupt-remapping and x2apic\n"); pr_err("Failed to enable Interrupt-remapping and x2apic\n");
if (ioapic_entries)
free_ioapic_entries(ioapic_entries);
#else #else
if (!cpu_has_x2apic) if (!cpu_has_x2apic)
return; return;
...@@ -1954,6 +1963,10 @@ static int lapic_suspend(struct sys_device *dev, pm_message_t state) ...@@ -1954,6 +1963,10 @@ static int lapic_suspend(struct sys_device *dev, pm_message_t state)
local_irq_save(flags); local_irq_save(flags);
disable_local_APIC(); disable_local_APIC();
#ifdef CONFIG_INTR_REMAP
if (intr_remapping_enabled)
disable_intr_remapping();
#endif
local_irq_restore(flags); local_irq_restore(flags);
return 0; return 0;
} }
...@@ -1964,15 +1977,41 @@ static int lapic_resume(struct sys_device *dev) ...@@ -1964,15 +1977,41 @@ static int lapic_resume(struct sys_device *dev)
unsigned long flags; unsigned long flags;
int maxlvt; int maxlvt;
#ifdef CONFIG_INTR_REMAP
int ret;
struct IO_APIC_route_entry **ioapic_entries = NULL;
if (!apic_pm_state.active) if (!apic_pm_state.active)
return 0; return 0;
maxlvt = lapic_get_maxlvt();
local_irq_save(flags); local_irq_save(flags);
if (x2apic) {
ioapic_entries = alloc_ioapic_entries();
if (!ioapic_entries) {
WARN(1, "Alloc ioapic_entries in lapic resume failed.");
return -ENOMEM;
}
ret = save_IO_APIC_setup(ioapic_entries);
if (ret) {
WARN(1, "Saving IO-APIC state failed: %d\n", ret);
free_ioapic_entries(ioapic_entries);
return ret;
}
mask_IO_APIC_setup(ioapic_entries);
mask_8259A();
enable_x2apic();
}
#else
if (!apic_pm_state.active)
return 0;
local_irq_save(flags);
if (x2apic) if (x2apic)
enable_x2apic(); enable_x2apic();
#endif
else { else {
/* /*
* Make sure the APICBASE points to the right address * Make sure the APICBASE points to the right address
...@@ -1986,6 +2025,7 @@ static int lapic_resume(struct sys_device *dev) ...@@ -1986,6 +2025,7 @@ static int lapic_resume(struct sys_device *dev)
wrmsr(MSR_IA32_APICBASE, l, h); wrmsr(MSR_IA32_APICBASE, l, h);
} }
maxlvt = lapic_get_maxlvt();
apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED);
apic_write(APIC_ID, apic_pm_state.apic_id); apic_write(APIC_ID, apic_pm_state.apic_id);
apic_write(APIC_DFR, apic_pm_state.apic_dfr); apic_write(APIC_DFR, apic_pm_state.apic_dfr);
...@@ -2009,8 +2049,20 @@ static int lapic_resume(struct sys_device *dev) ...@@ -2009,8 +2049,20 @@ static int lapic_resume(struct sys_device *dev)
apic_write(APIC_ESR, 0); apic_write(APIC_ESR, 0);
apic_read(APIC_ESR); apic_read(APIC_ESR);
#ifdef CONFIG_INTR_REMAP
if (intr_remapping_enabled)
reenable_intr_remapping(EIM_32BIT_APIC_ID);
if (x2apic) {
unmask_8259A();
restore_IO_APIC_setup(ioapic_entries);
free_ioapic_entries(ioapic_entries);
}
#endif
local_irq_restore(flags); local_irq_restore(flags);
return 0; return 0;
} }
...@@ -2048,7 +2100,9 @@ static int __init init_lapic_sysfs(void) ...@@ -2048,7 +2100,9 @@ static int __init init_lapic_sysfs(void)
error = sysdev_register(&device_lapic); error = sysdev_register(&device_lapic);
return error; return error;
} }
device_initcall(init_lapic_sysfs);
/* local apic needs to resume before other devices access its registers. */
core_initcall(init_lapic_sysfs);
#else /* CONFIG_PM */ #else /* CONFIG_PM */
......
...@@ -851,63 +851,74 @@ __setup("pirq=", ioapic_pirq_setup); ...@@ -851,63 +851,74 @@ __setup("pirq=", ioapic_pirq_setup);
#endif /* CONFIG_X86_32 */ #endif /* CONFIG_X86_32 */
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_INTR_REMAP
/* I/O APIC RTE contents at the OS boot up */ struct IO_APIC_route_entry **alloc_ioapic_entries(void)
static struct IO_APIC_route_entry *early_ioapic_entries[MAX_IO_APICS]; {
int apic;
struct IO_APIC_route_entry **ioapic_entries;
ioapic_entries = kzalloc(sizeof(*ioapic_entries) * nr_ioapics,
GFP_ATOMIC);
if (!ioapic_entries)
return 0;
for (apic = 0; apic < nr_ioapics; apic++) {
ioapic_entries[apic] =
kzalloc(sizeof(struct IO_APIC_route_entry) *
nr_ioapic_registers[apic], GFP_ATOMIC);
if (!ioapic_entries[apic])
goto nomem;
}
return ioapic_entries;
nomem:
while (--apic >= 0)
kfree(ioapic_entries[apic]);
kfree(ioapic_entries);
return 0;
}
/* /*
* Saves all the IO-APIC RTE's * Saves all the IO-APIC RTE's
*/ */
int save_IO_APIC_setup(void) int save_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries)
{ {
union IO_APIC_reg_01 reg_01;
unsigned long flags;
int apic, pin; int apic, pin;
/* if (!ioapic_entries)
* The number of IO-APIC IRQ registers (== #pins): return -ENOMEM;
*/
for (apic = 0; apic < nr_ioapics; apic++) {
spin_lock_irqsave(&ioapic_lock, flags);
reg_01.raw = io_apic_read(apic, 1);
spin_unlock_irqrestore(&ioapic_lock, flags);
nr_ioapic_registers[apic] = reg_01.bits.entries+1;
}
for (apic = 0; apic < nr_ioapics; apic++) { for (apic = 0; apic < nr_ioapics; apic++) {
early_ioapic_entries[apic] = if (!ioapic_entries[apic])
kzalloc(sizeof(struct IO_APIC_route_entry) * return -ENOMEM;
nr_ioapic_registers[apic], GFP_KERNEL);
if (!early_ioapic_entries[apic])
goto nomem;
}
for (apic = 0; apic < nr_ioapics; apic++)
for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
early_ioapic_entries[apic][pin] = ioapic_entries[apic][pin] =
ioapic_read_entry(apic, pin); ioapic_read_entry(apic, pin);
}
return 0; return 0;
nomem:
while (apic >= 0)
kfree(early_ioapic_entries[apic--]);
memset(early_ioapic_entries, 0,
ARRAY_SIZE(early_ioapic_entries));
return -ENOMEM;
} }
void mask_IO_APIC_setup(void) /*
* Mask all IO APIC entries.
*/
void mask_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries)
{ {
int apic, pin; int apic, pin;
if (!ioapic_entries)
return;
for (apic = 0; apic < nr_ioapics; apic++) { for (apic = 0; apic < nr_ioapics; apic++) {
if (!early_ioapic_entries[apic]) if (!ioapic_entries[apic])
break; break;
for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
struct IO_APIC_route_entry entry; struct IO_APIC_route_entry entry;
entry = early_ioapic_entries[apic][pin]; entry = ioapic_entries[apic][pin];
if (!entry.mask) { if (!entry.mask) {
entry.mask = 1; entry.mask = 1;
ioapic_write_entry(apic, pin, entry); ioapic_write_entry(apic, pin, entry);
...@@ -916,22 +927,30 @@ void mask_IO_APIC_setup(void) ...@@ -916,22 +927,30 @@ void mask_IO_APIC_setup(void)
} }
} }
void restore_IO_APIC_setup(void) /*
* Restore IO APIC entries which was saved in ioapic_entries.
*/
int restore_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries)
{ {
int apic, pin; int apic, pin;
if (!ioapic_entries)
return -ENOMEM;
for (apic = 0; apic < nr_ioapics; apic++) { for (apic = 0; apic < nr_ioapics; apic++) {
if (!early_ioapic_entries[apic]) if (!ioapic_entries[apic])
break; return -ENOMEM;
for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
ioapic_write_entry(apic, pin, ioapic_write_entry(apic, pin,
early_ioapic_entries[apic][pin]); ioapic_entries[apic][pin]);
kfree(early_ioapic_entries[apic]);
early_ioapic_entries[apic] = NULL;
} }
return 0;
} }
void reinit_intr_remapped_IO_APIC(int intr_remapping) void reinit_intr_remapped_IO_APIC(int intr_remapping,
struct IO_APIC_route_entry **ioapic_entries)
{ {
/* /*
* for now plain restore of previous settings. * for now plain restore of previous settings.
...@@ -940,7 +959,17 @@ void reinit_intr_remapped_IO_APIC(int intr_remapping) ...@@ -940,7 +959,17 @@ void reinit_intr_remapped_IO_APIC(int intr_remapping)
* table entries. for now, do a plain restore, and wait for * table entries. for now, do a plain restore, and wait for
* the setup_IO_APIC_irqs() to do proper initialization. * the setup_IO_APIC_irqs() to do proper initialization.
*/ */
restore_IO_APIC_setup(); restore_IO_APIC_setup(ioapic_entries);
}
void free_ioapic_entries(struct IO_APIC_route_entry **ioapic_entries)
{
int apic;
for (apic = 0; apic < nr_ioapics; apic++)
kfree(ioapic_entries[apic]);
kfree(ioapic_entries);
} }
#endif #endif
...@@ -2495,7 +2524,7 @@ static void irq_complete_move(struct irq_desc **descp) ...@@ -2495,7 +2524,7 @@ static void irq_complete_move(struct irq_desc **descp)
static inline void irq_complete_move(struct irq_desc **descp) {} static inline void irq_complete_move(struct irq_desc **descp) {}
#endif #endif
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_X86_X2APIC
static void __eoi_ioapic_irq(unsigned int irq, struct irq_cfg *cfg) static void __eoi_ioapic_irq(unsigned int irq, struct irq_cfg *cfg)
{ {
int apic, pin; int apic, pin;
...@@ -2540,7 +2569,6 @@ static void ack_x2apic_edge(unsigned int irq) ...@@ -2540,7 +2569,6 @@ static void ack_x2apic_edge(unsigned int irq)
{ {
ack_x2APIC_irq(); ack_x2APIC_irq();
} }
#endif #endif
static void ack_apic_edge(unsigned int irq) static void ack_apic_edge(unsigned int irq)
...@@ -2651,6 +2679,26 @@ static void ack_apic_level(unsigned int irq) ...@@ -2651,6 +2679,26 @@ static void ack_apic_level(unsigned int irq)
#endif #endif
} }
#ifdef CONFIG_INTR_REMAP
static void ir_ack_apic_edge(unsigned int irq)
{
#ifdef CONFIG_X86_X2APIC
if (x2apic_enabled())
return ack_x2apic_edge(irq);
#endif
return ack_apic_edge(irq);
}
static void ir_ack_apic_level(unsigned int irq)
{
#ifdef CONFIG_X86_X2APIC
if (x2apic_enabled())
return ack_x2apic_level(irq);
#endif
return ack_apic_level(irq);
}
#endif /* CONFIG_INTR_REMAP */
static struct irq_chip ioapic_chip __read_mostly = { static struct irq_chip ioapic_chip __read_mostly = {
.name = "IO-APIC", .name = "IO-APIC",
.startup = startup_ioapic_irq, .startup = startup_ioapic_irq,
...@@ -2670,8 +2718,8 @@ static struct irq_chip ir_ioapic_chip __read_mostly = { ...@@ -2670,8 +2718,8 @@ static struct irq_chip ir_ioapic_chip __read_mostly = {
.mask = mask_IO_APIC_irq, .mask = mask_IO_APIC_irq,
.unmask = unmask_IO_APIC_irq, .unmask = unmask_IO_APIC_irq,
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_INTR_REMAP
.ack = ack_x2apic_edge, .ack = ir_ack_apic_edge,
.eoi = ack_x2apic_level, .eoi = ir_ack_apic_level,
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
.set_affinity = set_ir_ioapic_affinity_irq, .set_affinity = set_ir_ioapic_affinity_irq,
#endif #endif
...@@ -3397,7 +3445,7 @@ static struct irq_chip msi_ir_chip = { ...@@ -3397,7 +3445,7 @@ static struct irq_chip msi_ir_chip = {
.unmask = unmask_msi_irq, .unmask = unmask_msi_irq,
.mask = mask_msi_irq, .mask = mask_msi_irq,
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_INTR_REMAP
.ack = ack_x2apic_edge, .ack = ir_ack_apic_edge,
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
.set_affinity = ir_set_msi_irq_affinity, .set_affinity = ir_set_msi_irq_affinity,
#endif #endif
......
...@@ -180,6 +180,7 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) ...@@ -180,6 +180,7 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
dmaru->hdr = header; dmaru->hdr = header;
drhd = (struct acpi_dmar_hardware_unit *)header; drhd = (struct acpi_dmar_hardware_unit *)header;
dmaru->reg_base_addr = drhd->address; dmaru->reg_base_addr = drhd->address;
dmaru->segment = drhd->segment;
dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
ret = alloc_iommu(dmaru); ret = alloc_iommu(dmaru);
...@@ -789,6 +790,35 @@ void dmar_disable_qi(struct intel_iommu *iommu) ...@@ -789,6 +790,35 @@ void dmar_disable_qi(struct intel_iommu *iommu)
spin_unlock_irqrestore(&iommu->register_lock, flags); spin_unlock_irqrestore(&iommu->register_lock, flags);
} }
/*
* Enable queued invalidation.
*/
static void __dmar_enable_qi(struct intel_iommu *iommu)
{
u32 cmd, sts;
unsigned long flags;
struct q_inval *qi = iommu->qi;
qi->free_head = qi->free_tail = 0;
qi->free_cnt = QI_LENGTH;
spin_lock_irqsave(&iommu->register_lock, flags);
/* write zero to the tail reg */
writel(0, iommu->reg + DMAR_IQT_REG);
dmar_writeq(iommu->reg + DMAR_IQA_REG, virt_to_phys(qi->desc));
cmd = iommu->gcmd | DMA_GCMD_QIE;
iommu->gcmd |= DMA_GCMD_QIE;
writel(cmd, iommu->reg + DMAR_GCMD_REG);
/* Make sure hardware complete it */
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts);
spin_unlock_irqrestore(&iommu->register_lock, flags);
}
/* /*
* Enable Queued Invalidation interface. This is a must to support * Enable Queued Invalidation interface. This is a must to support
* interrupt-remapping. Also used by DMA-remapping, which replaces * interrupt-remapping. Also used by DMA-remapping, which replaces
...@@ -796,8 +826,6 @@ void dmar_disable_qi(struct intel_iommu *iommu) ...@@ -796,8 +826,6 @@ void dmar_disable_qi(struct intel_iommu *iommu)
*/ */
int dmar_enable_qi(struct intel_iommu *iommu) int dmar_enable_qi(struct intel_iommu *iommu)
{ {
u32 cmd, sts;
unsigned long flags;
struct q_inval *qi; struct q_inval *qi;
if (!ecap_qis(iommu->ecap)) if (!ecap_qis(iommu->ecap))
...@@ -835,19 +863,7 @@ int dmar_enable_qi(struct intel_iommu *iommu) ...@@ -835,19 +863,7 @@ int dmar_enable_qi(struct intel_iommu *iommu)
spin_lock_init(&qi->q_lock); spin_lock_init(&qi->q_lock);
spin_lock_irqsave(&iommu->register_lock, flags); __dmar_enable_qi(iommu);
/* write zero to the tail reg */
writel(0, iommu->reg + DMAR_IQT_REG);
dmar_writeq(iommu->reg + DMAR_IQA_REG, virt_to_phys(qi->desc));
cmd = iommu->gcmd | DMA_GCMD_QIE;
iommu->gcmd |= DMA_GCMD_QIE;
writel(cmd, iommu->reg + DMAR_GCMD_REG);
/* Make sure hardware complete it */
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts);
spin_unlock_irqrestore(&iommu->register_lock, flags);
return 0; return 0;
} }
...@@ -1102,3 +1118,28 @@ int __init enable_drhd_fault_handling(void) ...@@ -1102,3 +1118,28 @@ int __init enable_drhd_fault_handling(void)
return 0; return 0;
} }
/*
* Re-enable Queued Invalidation interface.
*/
int dmar_reenable_qi(struct intel_iommu *iommu)
{
if (!ecap_qis(iommu->ecap))
return -ENOENT;
if (!iommu->qi)
return -ENOENT;
/*
* First disable queued invalidation.
*/
dmar_disable_qi(iommu);
/*
* Then enable queued invalidation again. Since there is no pending
* invalidation requests now, it's safe to re-enable queued
* invalidation.
*/
__dmar_enable_qi(iommu);
return 0;
}
This diff is collapsed.
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <asm/cpu.h> #include <asm/cpu.h>
#include <linux/intel-iommu.h> #include <linux/intel-iommu.h>
#include "intr_remapping.h" #include "intr_remapping.h"
#include <acpi/acpi.h>
static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
static int ir_ioapic_num; static int ir_ioapic_num;
...@@ -415,12 +416,27 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) ...@@ -415,12 +416,27 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
/* Set interrupt-remapping table pointer */ /* Set interrupt-remapping table pointer */
cmd = iommu->gcmd | DMA_GCMD_SIRTP; cmd = iommu->gcmd | DMA_GCMD_SIRTP;
iommu->gcmd |= DMA_GCMD_SIRTP;
writel(cmd, iommu->reg + DMAR_GCMD_REG); writel(cmd, iommu->reg + DMAR_GCMD_REG);
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
readl, (sts & DMA_GSTS_IRTPS), sts); readl, (sts & DMA_GSTS_IRTPS), sts);
spin_unlock_irqrestore(&iommu->register_lock, flags); spin_unlock_irqrestore(&iommu->register_lock, flags);
if (mode == 0) {
spin_lock_irqsave(&iommu->register_lock, flags);
/* enable comaptiblity format interrupt pass through */
cmd = iommu->gcmd | DMA_GCMD_CFI;
iommu->gcmd |= DMA_GCMD_CFI;
writel(cmd, iommu->reg + DMAR_GCMD_REG);
IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
readl, (sts & DMA_GSTS_CFIS), sts);
spin_unlock_irqrestore(&iommu->register_lock, flags);
}
/* /*
* global invalidation of interrupt entry cache before enabling * global invalidation of interrupt entry cache before enabling
* interrupt-remapping. * interrupt-remapping.
...@@ -470,7 +486,7 @@ static int setup_intr_remapping(struct intel_iommu *iommu, int mode) ...@@ -470,7 +486,7 @@ static int setup_intr_remapping(struct intel_iommu *iommu, int mode)
/* /*
* Disable Interrupt Remapping. * Disable Interrupt Remapping.
*/ */
static void disable_intr_remapping(struct intel_iommu *iommu) static void iommu_disable_intr_remapping(struct intel_iommu *iommu)
{ {
unsigned long flags; unsigned long flags;
u32 sts; u32 sts;
...@@ -478,6 +494,12 @@ static void disable_intr_remapping(struct intel_iommu *iommu) ...@@ -478,6 +494,12 @@ static void disable_intr_remapping(struct intel_iommu *iommu)
if (!ecap_ir_support(iommu->ecap)) if (!ecap_ir_support(iommu->ecap))
return; return;
/*
* global invalidation of interrupt entry cache before disabling
* interrupt-remapping.
*/
qi_global_iec(iommu);
spin_lock_irqsave(&iommu->register_lock, flags); spin_lock_irqsave(&iommu->register_lock, flags);
sts = dmar_readq(iommu->reg + DMAR_GSTS_REG); sts = dmar_readq(iommu->reg + DMAR_GSTS_REG);
...@@ -502,6 +524,13 @@ int __init enable_intr_remapping(int eim) ...@@ -502,6 +524,13 @@ int __init enable_intr_remapping(int eim)
for_each_drhd_unit(drhd) { for_each_drhd_unit(drhd) {
struct intel_iommu *iommu = drhd->iommu; struct intel_iommu *iommu = drhd->iommu;
/*
* If the queued invalidation is already initialized,
* shouldn't disable it.
*/
if (iommu->qi)
continue;
/* /*
* Clear previous faults. * Clear previous faults.
*/ */
...@@ -511,7 +540,7 @@ int __init enable_intr_remapping(int eim) ...@@ -511,7 +540,7 @@ int __init enable_intr_remapping(int eim)
* Disable intr remapping and queued invalidation, if already * Disable intr remapping and queued invalidation, if already
* enabled prior to OS handover. * enabled prior to OS handover.
*/ */
disable_intr_remapping(iommu); iommu_disable_intr_remapping(iommu);
dmar_disable_qi(iommu); dmar_disable_qi(iommu);
} }
...@@ -639,3 +668,54 @@ int __init parse_ioapics_under_ir(void) ...@@ -639,3 +668,54 @@ int __init parse_ioapics_under_ir(void)
return ir_supported; return ir_supported;
} }
void disable_intr_remapping(void)
{
struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu = NULL;
/*
* Disable Interrupt-remapping for all the DRHD's now.
*/
for_each_iommu(iommu, drhd) {
if (!ecap_ir_support(iommu->ecap))
continue;
iommu_disable_intr_remapping(iommu);
}
}
int reenable_intr_remapping(int eim)
{
struct dmar_drhd_unit *drhd;
int setup = 0;
struct intel_iommu *iommu = NULL;
for_each_iommu(iommu, drhd)
if (iommu->qi)
dmar_reenable_qi(iommu);
/*
* Setup Interrupt-remapping for all the DRHD's now.
*/
for_each_iommu(iommu, drhd) {
if (!ecap_ir_support(iommu->ecap))
continue;
/* Set up interrupt remapping for iommu.*/
iommu_set_intr_remapping(iommu, eim);
setup = 1;
}
if (!setup)
goto error;
return 0;
error:
/*
* handle error condition gracefully here!
*/
return -1;
}
...@@ -34,6 +34,7 @@ struct dmar_drhd_unit { ...@@ -34,6 +34,7 @@ struct dmar_drhd_unit {
u64 reg_base_addr; /* register base address*/ u64 reg_base_addr; /* register base address*/
struct pci_dev **devices; /* target device array */ struct pci_dev **devices; /* target device array */
int devices_cnt; /* target device count */ int devices_cnt; /* target device count */
u16 segment; /* PCI domain */
u8 ignored:1; /* ignore drhd */ u8 ignored:1; /* ignore drhd */
u8 include_all:1; u8 include_all:1;
struct intel_iommu *iommu; struct intel_iommu *iommu;
...@@ -44,6 +45,14 @@ extern struct list_head dmar_drhd_units; ...@@ -44,6 +45,14 @@ extern struct list_head dmar_drhd_units;
#define for_each_drhd_unit(drhd) \ #define for_each_drhd_unit(drhd) \
list_for_each_entry(drhd, &dmar_drhd_units, list) list_for_each_entry(drhd, &dmar_drhd_units, list)
#define for_each_active_iommu(i, drhd) \
list_for_each_entry(drhd, &dmar_drhd_units, list) \
if (i=drhd->iommu, drhd->ignored) {} else
#define for_each_iommu(i, drhd) \
list_for_each_entry(drhd, &dmar_drhd_units, list) \
if (i=drhd->iommu, 0) {} else
extern int dmar_table_init(void); extern int dmar_table_init(void);
extern int dmar_dev_scope_init(void); extern int dmar_dev_scope_init(void);
...@@ -100,6 +109,8 @@ struct irte { ...@@ -100,6 +109,8 @@ struct irte {
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_INTR_REMAP
extern int intr_remapping_enabled; extern int intr_remapping_enabled;
extern int enable_intr_remapping(int); extern int enable_intr_remapping(int);
extern void disable_intr_remapping(void);
extern int reenable_intr_remapping(int);
extern int get_irte(int irq, struct irte *entry); extern int get_irte(int irq, struct irte *entry);
extern int modify_irte(int irq, struct irte *irte_modified); extern int modify_irte(int irq, struct irte *irte_modified);
......
...@@ -164,6 +164,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) ...@@ -164,6 +164,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
#define DMA_GCMD_QIE (((u32)1) << 26) #define DMA_GCMD_QIE (((u32)1) << 26)
#define DMA_GCMD_SIRTP (((u32)1) << 24) #define DMA_GCMD_SIRTP (((u32)1) << 24)
#define DMA_GCMD_IRE (((u32) 1) << 25) #define DMA_GCMD_IRE (((u32) 1) << 25)
#define DMA_GCMD_CFI (((u32) 1) << 23)
/* GSTS_REG */ /* GSTS_REG */
#define DMA_GSTS_TES (((u32)1) << 31) #define DMA_GSTS_TES (((u32)1) << 31)
...@@ -174,6 +175,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) ...@@ -174,6 +175,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
#define DMA_GSTS_QIES (((u32)1) << 26) #define DMA_GSTS_QIES (((u32)1) << 26)
#define DMA_GSTS_IRTPS (((u32)1) << 24) #define DMA_GSTS_IRTPS (((u32)1) << 24)
#define DMA_GSTS_IRES (((u32)1) << 25) #define DMA_GSTS_IRES (((u32)1) << 25)
#define DMA_GSTS_CFIS (((u32)1) << 23)
/* CCMD_REG */ /* CCMD_REG */
#define DMA_CCMD_ICC (((u64)1) << 63) #define DMA_CCMD_ICC (((u64)1) << 63)
...@@ -284,6 +286,14 @@ struct iommu_flush { ...@@ -284,6 +286,14 @@ struct iommu_flush {
unsigned int size_order, u64 type, int non_present_entry_flush); unsigned int size_order, u64 type, int non_present_entry_flush);
}; };
enum {
SR_DMAR_FECTL_REG,
SR_DMAR_FEDATA_REG,
SR_DMAR_FEADDR_REG,
SR_DMAR_FEUADDR_REG,
MAX_SR_DMAR_REGS
};
struct intel_iommu { struct intel_iommu {
void __iomem *reg; /* Pointer to hardware regs, virtual addr */ void __iomem *reg; /* Pointer to hardware regs, virtual addr */
u64 cap; u64 cap;
...@@ -304,6 +314,8 @@ struct intel_iommu { ...@@ -304,6 +314,8 @@ struct intel_iommu {
struct iommu_flush flush; struct iommu_flush flush;
#endif #endif
struct q_inval *qi; /* Queued invalidation info */ struct q_inval *qi; /* Queued invalidation info */
u32 *iommu_state; /* Store iommu states between suspend and resume.*/
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_INTR_REMAP
struct ir_table *ir_table; /* Interrupt remapping info */ struct ir_table *ir_table; /* Interrupt remapping info */
#endif #endif
...@@ -322,6 +334,7 @@ extern int alloc_iommu(struct dmar_drhd_unit *drhd); ...@@ -322,6 +334,7 @@ extern int alloc_iommu(struct dmar_drhd_unit *drhd);
extern void free_iommu(struct intel_iommu *iommu); extern void free_iommu(struct intel_iommu *iommu);
extern int dmar_enable_qi(struct intel_iommu *iommu); extern int dmar_enable_qi(struct intel_iommu *iommu);
extern void dmar_disable_qi(struct intel_iommu *iommu); extern void dmar_disable_qi(struct intel_iommu *iommu);
extern int dmar_reenable_qi(struct intel_iommu *iommu);
extern void qi_global_iec(struct intel_iommu *iommu); extern void qi_global_iec(struct intel_iommu *iommu);
extern int qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, extern int qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment