mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 02:09:42 +00:00
iommu/amd: Add routines to manage irq remapping tables
Add routines to: * Alloc remapping tables and single entries from these tables * Change entries in the tables * Free entries in the table Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
This commit is contained in:
parent
7ef2798deb
commit
2b32450634
1 changed files with 228 additions and 0 deletions
|
@ -31,6 +31,12 @@
|
|||
#include <linux/amd-iommu.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/msi.h>
|
||||
#include <asm/irq_remapping.h>
|
||||
#include <asm/io_apic.h>
|
||||
#include <asm/apic.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/msidef.h>
|
||||
#include <asm/proto.h>
|
||||
#include <asm/iommu.h>
|
||||
|
@ -3773,3 +3779,225 @@ int amd_iommu_device_info(struct pci_dev *pdev,
|
|||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(amd_iommu_device_info);
|
||||
|
||||
#ifdef CONFIG_IRQ_REMAP
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Interrupt Remapping Implementation
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
union irte {
|
||||
u32 val;
|
||||
struct {
|
||||
u32 valid : 1,
|
||||
no_fault : 1,
|
||||
int_type : 3,
|
||||
rq_eoi : 1,
|
||||
dm : 1,
|
||||
rsvd_1 : 1,
|
||||
destination : 8,
|
||||
vector : 8,
|
||||
rsvd_2 : 8;
|
||||
} fields;
|
||||
};
|
||||
|
||||
#define DTE_IRQ_PHYS_ADDR_MASK (((1ULL << 45)-1) << 6)
|
||||
#define DTE_IRQ_REMAP_INTCTL (2ULL << 60)
|
||||
#define DTE_IRQ_TABLE_LEN (8ULL << 1)
|
||||
#define DTE_IRQ_REMAP_ENABLE 1ULL
|
||||
|
||||
static void set_dte_irq_entry(u16 devid, struct irq_remap_table *table)
|
||||
{
|
||||
u64 dte;
|
||||
|
||||
dte = amd_iommu_dev_table[devid].data[2];
|
||||
dte &= ~DTE_IRQ_PHYS_ADDR_MASK;
|
||||
dte |= virt_to_phys(table->table);
|
||||
dte |= DTE_IRQ_REMAP_INTCTL;
|
||||
dte |= DTE_IRQ_TABLE_LEN;
|
||||
dte |= DTE_IRQ_REMAP_ENABLE;
|
||||
|
||||
amd_iommu_dev_table[devid].data[2] = dte;
|
||||
}
|
||||
|
||||
#define IRTE_ALLOCATED (~1U)
|
||||
|
||||
static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
|
||||
{
|
||||
struct irq_remap_table *table = NULL;
|
||||
struct amd_iommu *iommu;
|
||||
unsigned long flags;
|
||||
u16 alias;
|
||||
|
||||
write_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
|
||||
iommu = amd_iommu_rlookup_table[devid];
|
||||
if (!iommu)
|
||||
goto out_unlock;
|
||||
|
||||
table = irq_lookup_table[devid];
|
||||
if (table)
|
||||
goto out;
|
||||
|
||||
alias = amd_iommu_alias_table[devid];
|
||||
table = irq_lookup_table[alias];
|
||||
if (table) {
|
||||
irq_lookup_table[devid] = table;
|
||||
set_dte_irq_entry(devid, table);
|
||||
iommu_flush_dte(iommu, devid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Nothing there yet, allocate new irq remapping table */
|
||||
table = kzalloc(sizeof(*table), GFP_ATOMIC);
|
||||
if (!table)
|
||||
goto out;
|
||||
|
||||
if (ioapic)
|
||||
/* Keep the first 32 indexes free for IOAPIC interrupts */
|
||||
table->min_index = 32;
|
||||
|
||||
table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_ATOMIC);
|
||||
if (!table->table) {
|
||||
kfree(table);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(table->table, 0, MAX_IRQS_PER_TABLE * sizeof(u32));
|
||||
|
||||
if (ioapic) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 32; ++i)
|
||||
table->table[i] = IRTE_ALLOCATED;
|
||||
}
|
||||
|
||||
irq_lookup_table[devid] = table;
|
||||
set_dte_irq_entry(devid, table);
|
||||
iommu_flush_dte(iommu, devid);
|
||||
if (devid != alias) {
|
||||
irq_lookup_table[alias] = table;
|
||||
set_dte_irq_entry(devid, table);
|
||||
iommu_flush_dte(iommu, alias);
|
||||
}
|
||||
|
||||
out:
|
||||
iommu_completion_wait(iommu);
|
||||
|
||||
out_unlock:
|
||||
write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
|
||||
{
|
||||
struct irq_remap_table *table;
|
||||
unsigned long flags;
|
||||
int index, c;
|
||||
|
||||
table = get_irq_table(devid, false);
|
||||
if (!table)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&table->lock, flags);
|
||||
|
||||
/* Scan table for free entries */
|
||||
for (c = 0, index = table->min_index;
|
||||
index < MAX_IRQS_PER_TABLE;
|
||||
++index) {
|
||||
if (table->table[index] == 0)
|
||||
c += 1;
|
||||
else
|
||||
c = 0;
|
||||
|
||||
if (c == count) {
|
||||
struct irq_2_iommu *irte_info;
|
||||
|
||||
for (; c != 0; --c)
|
||||
table->table[index - c + 1] = IRTE_ALLOCATED;
|
||||
|
||||
index -= count - 1;
|
||||
|
||||
irte_info = &cfg->irq_2_iommu;
|
||||
irte_info->sub_handle = devid;
|
||||
irte_info->irte_index = index;
|
||||
irte_info->iommu = (void *)cfg;
|
||||
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
index = -ENOSPC;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&table->lock, flags);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int get_irte(u16 devid, int index, union irte *irte)
|
||||
{
|
||||
struct irq_remap_table *table;
|
||||
unsigned long flags;
|
||||
|
||||
table = get_irq_table(devid, false);
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_irqsave(&table->lock, flags);
|
||||
irte->val = table->table[index];
|
||||
spin_unlock_irqrestore(&table->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int modify_irte(u16 devid, int index, union irte irte)
|
||||
{
|
||||
struct irq_remap_table *table;
|
||||
struct amd_iommu *iommu;
|
||||
unsigned long flags;
|
||||
|
||||
iommu = amd_iommu_rlookup_table[devid];
|
||||
if (iommu == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
table = get_irq_table(devid, false);
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_irqsave(&table->lock, flags);
|
||||
table->table[index] = irte.val;
|
||||
spin_unlock_irqrestore(&table->lock, flags);
|
||||
|
||||
iommu_flush_irt(iommu, devid);
|
||||
iommu_completion_wait(iommu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_irte(u16 devid, int index)
|
||||
{
|
||||
struct irq_remap_table *table;
|
||||
struct amd_iommu *iommu;
|
||||
unsigned long flags;
|
||||
|
||||
iommu = amd_iommu_rlookup_table[devid];
|
||||
if (iommu == NULL)
|
||||
return;
|
||||
|
||||
table = get_irq_table(devid, false);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&table->lock, flags);
|
||||
table->table[index] = 0;
|
||||
spin_unlock_irqrestore(&table->lock, flags);
|
||||
|
||||
iommu_flush_irt(iommu, devid);
|
||||
iommu_completion_wait(iommu);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue