Merge branches 'gart/fixes', 'amd-iommu/fixes+cleanups' and 'amd-iommu/fault-handling' into amd-iommu/2.6.32
This commit is contained in:
@@ -198,7 +198,7 @@ extern bool amd_iommu_dump;
|
|||||||
#define DUMP_printk(format, arg...) \
|
#define DUMP_printk(format, arg...) \
|
||||||
do { \
|
do { \
|
||||||
if (amd_iommu_dump) \
|
if (amd_iommu_dump) \
|
||||||
printk(KERN_INFO "AMD IOMMU: " format, ## arg); \
|
printk(KERN_INFO "AMD-Vi: " format, ## arg); \
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -337,6 +337,9 @@ struct amd_iommu {
|
|||||||
/* if one, we need to send a completion wait command */
|
/* if one, we need to send a completion wait command */
|
||||||
bool need_sync;
|
bool need_sync;
|
||||||
|
|
||||||
|
/* becomes true if a command buffer reset is running */
|
||||||
|
bool reset_in_progress;
|
||||||
|
|
||||||
/* default dma_ops domain for that IOMMU */
|
/* default dma_ops domain for that IOMMU */
|
||||||
struct dma_ops_domain *default_dom;
|
struct dma_ops_domain *default_dom;
|
||||||
};
|
};
|
||||||
@@ -457,4 +460,7 @@ static inline void amd_iommu_stats_init(void) { }
|
|||||||
|
|
||||||
#endif /* CONFIG_AMD_IOMMU_STATS */
|
#endif /* CONFIG_AMD_IOMMU_STATS */
|
||||||
|
|
||||||
|
/* some function prototypes */
|
||||||
|
extern void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu);
|
||||||
|
|
||||||
#endif /* _ASM_X86_AMD_IOMMU_TYPES_H */
|
#endif /* _ASM_X86_AMD_IOMMU_TYPES_H */
|
||||||
|
@@ -41,9 +41,7 @@ static DEFINE_RWLOCK(amd_iommu_devtable_lock);
|
|||||||
static LIST_HEAD(iommu_pd_list);
|
static LIST_HEAD(iommu_pd_list);
|
||||||
static DEFINE_SPINLOCK(iommu_pd_list_lock);
|
static DEFINE_SPINLOCK(iommu_pd_list_lock);
|
||||||
|
|
||||||
#ifdef CONFIG_IOMMU_API
|
|
||||||
static struct iommu_ops amd_iommu_ops;
|
static struct iommu_ops amd_iommu_ops;
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* general struct to manage commands send to an IOMMU
|
* general struct to manage commands send to an IOMMU
|
||||||
@@ -61,10 +59,7 @@ static u64* alloc_pte(struct protection_domain *dom,
|
|||||||
static void dma_ops_reserve_addresses(struct dma_ops_domain *dom,
|
static void dma_ops_reserve_addresses(struct dma_ops_domain *dom,
|
||||||
unsigned long start_page,
|
unsigned long start_page,
|
||||||
unsigned int pages);
|
unsigned int pages);
|
||||||
|
static void reset_iommu_command_buffer(struct amd_iommu *iommu);
|
||||||
#ifndef BUS_NOTIFY_UNBOUND_DRIVER
|
|
||||||
#define BUS_NOTIFY_UNBOUND_DRIVER 0x0005
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_AMD_IOMMU_STATS
|
#ifdef CONFIG_AMD_IOMMU_STATS
|
||||||
|
|
||||||
@@ -138,7 +133,25 @@ static int iommu_has_npcache(struct amd_iommu *iommu)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void iommu_print_event(void *__evt)
|
static void dump_dte_entry(u16 devid)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i)
|
||||||
|
pr_err("AMD-Vi: DTE[%d]: %08x\n", i,
|
||||||
|
amd_iommu_dev_table[devid].data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_command(unsigned long phys_addr)
|
||||||
|
{
|
||||||
|
struct iommu_cmd *cmd = phys_to_virt(phys_addr);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; ++i)
|
||||||
|
pr_err("AMD-Vi: CMD[%d]: %08x\n", i, cmd->data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iommu_print_event(struct amd_iommu *iommu, void *__evt)
|
||||||
{
|
{
|
||||||
u32 *event = __evt;
|
u32 *event = __evt;
|
||||||
int type = (event[1] >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK;
|
int type = (event[1] >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK;
|
||||||
@@ -147,7 +160,7 @@ static void iommu_print_event(void *__evt)
|
|||||||
int flags = (event[1] >> EVENT_FLAGS_SHIFT) & EVENT_FLAGS_MASK;
|
int flags = (event[1] >> EVENT_FLAGS_SHIFT) & EVENT_FLAGS_MASK;
|
||||||
u64 address = (u64)(((u64)event[3]) << 32) | event[2];
|
u64 address = (u64)(((u64)event[3]) << 32) | event[2];
|
||||||
|
|
||||||
printk(KERN_ERR "AMD IOMMU: Event logged [");
|
printk(KERN_ERR "AMD-Vi: Event logged [");
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case EVENT_TYPE_ILL_DEV:
|
case EVENT_TYPE_ILL_DEV:
|
||||||
@@ -155,6 +168,7 @@ static void iommu_print_event(void *__evt)
|
|||||||
"address=0x%016llx flags=0x%04x]\n",
|
"address=0x%016llx flags=0x%04x]\n",
|
||||||
PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
|
PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
|
||||||
address, flags);
|
address, flags);
|
||||||
|
dump_dte_entry(devid);
|
||||||
break;
|
break;
|
||||||
case EVENT_TYPE_IO_FAULT:
|
case EVENT_TYPE_IO_FAULT:
|
||||||
printk("IO_PAGE_FAULT device=%02x:%02x.%x "
|
printk("IO_PAGE_FAULT device=%02x:%02x.%x "
|
||||||
@@ -176,6 +190,8 @@ static void iommu_print_event(void *__evt)
|
|||||||
break;
|
break;
|
||||||
case EVENT_TYPE_ILL_CMD:
|
case EVENT_TYPE_ILL_CMD:
|
||||||
printk("ILLEGAL_COMMAND_ERROR address=0x%016llx]\n", address);
|
printk("ILLEGAL_COMMAND_ERROR address=0x%016llx]\n", address);
|
||||||
|
reset_iommu_command_buffer(iommu);
|
||||||
|
dump_command(address);
|
||||||
break;
|
break;
|
||||||
case EVENT_TYPE_CMD_HARD_ERR:
|
case EVENT_TYPE_CMD_HARD_ERR:
|
||||||
printk("COMMAND_HARDWARE_ERROR address=0x%016llx "
|
printk("COMMAND_HARDWARE_ERROR address=0x%016llx "
|
||||||
@@ -209,7 +225,7 @@ static void iommu_poll_events(struct amd_iommu *iommu)
|
|||||||
tail = readl(iommu->mmio_base + MMIO_EVT_TAIL_OFFSET);
|
tail = readl(iommu->mmio_base + MMIO_EVT_TAIL_OFFSET);
|
||||||
|
|
||||||
while (head != tail) {
|
while (head != tail) {
|
||||||
iommu_print_event(iommu->evt_buf + head);
|
iommu_print_event(iommu, iommu->evt_buf + head);
|
||||||
head = (head + EVENT_ENTRY_SIZE) % iommu->evt_buf_size;
|
head = (head + EVENT_ENTRY_SIZE) % iommu->evt_buf_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,8 +312,11 @@ static void __iommu_wait_for_completion(struct amd_iommu *iommu)
|
|||||||
status &= ~MMIO_STATUS_COM_WAIT_INT_MASK;
|
status &= ~MMIO_STATUS_COM_WAIT_INT_MASK;
|
||||||
writel(status, iommu->mmio_base + MMIO_STATUS_OFFSET);
|
writel(status, iommu->mmio_base + MMIO_STATUS_OFFSET);
|
||||||
|
|
||||||
if (unlikely(i == EXIT_LOOP_COUNT))
|
if (unlikely(i == EXIT_LOOP_COUNT)) {
|
||||||
panic("AMD IOMMU: Completion wait loop failed\n");
|
spin_unlock(&iommu->lock);
|
||||||
|
reset_iommu_command_buffer(iommu);
|
||||||
|
spin_lock(&iommu->lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -445,37 +464,67 @@ static void iommu_flush_tlb_pde(struct amd_iommu *iommu, u16 domid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is used to flush the IO/TLB for a given protection domain
|
* This function flushes one domain on one IOMMU
|
||||||
* on every IOMMU in the system
|
|
||||||
*/
|
*/
|
||||||
static void iommu_flush_domain(u16 domid)
|
static void flush_domain_on_iommu(struct amd_iommu *iommu, u16 domid)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
struct amd_iommu *iommu;
|
|
||||||
struct iommu_cmd cmd;
|
struct iommu_cmd cmd;
|
||||||
|
unsigned long flags;
|
||||||
INC_STATS_COUNTER(domain_flush_all);
|
|
||||||
|
|
||||||
__iommu_build_inv_iommu_pages(&cmd, CMD_INV_IOMMU_ALL_PAGES_ADDRESS,
|
__iommu_build_inv_iommu_pages(&cmd, CMD_INV_IOMMU_ALL_PAGES_ADDRESS,
|
||||||
domid, 1, 1);
|
domid, 1, 1);
|
||||||
|
|
||||||
for_each_iommu(iommu) {
|
|
||||||
spin_lock_irqsave(&iommu->lock, flags);
|
spin_lock_irqsave(&iommu->lock, flags);
|
||||||
__iommu_queue_command(iommu, &cmd);
|
__iommu_queue_command(iommu, &cmd);
|
||||||
__iommu_completion_wait(iommu);
|
__iommu_completion_wait(iommu);
|
||||||
__iommu_wait_for_completion(iommu);
|
__iommu_wait_for_completion(iommu);
|
||||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void amd_iommu_flush_all_domains(void)
|
static void flush_all_domains_on_iommu(struct amd_iommu *iommu)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 1; i < MAX_DOMAIN_ID; ++i) {
|
for (i = 1; i < MAX_DOMAIN_ID; ++i) {
|
||||||
if (!test_bit(i, amd_iommu_pd_alloc_bitmap))
|
if (!test_bit(i, amd_iommu_pd_alloc_bitmap))
|
||||||
continue;
|
continue;
|
||||||
iommu_flush_domain(i);
|
flush_domain_on_iommu(iommu, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is used to flush the IO/TLB for a given protection domain
|
||||||
|
* on every IOMMU in the system
|
||||||
|
*/
|
||||||
|
static void iommu_flush_domain(u16 domid)
|
||||||
|
{
|
||||||
|
struct amd_iommu *iommu;
|
||||||
|
|
||||||
|
INC_STATS_COUNTER(domain_flush_all);
|
||||||
|
|
||||||
|
for_each_iommu(iommu)
|
||||||
|
flush_domain_on_iommu(iommu, domid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd_iommu_flush_all_domains(void)
|
||||||
|
{
|
||||||
|
struct amd_iommu *iommu;
|
||||||
|
|
||||||
|
for_each_iommu(iommu)
|
||||||
|
flush_all_domains_on_iommu(iommu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flush_all_devices_for_iommu(struct amd_iommu *iommu)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i <= amd_iommu_last_bdf; ++i) {
|
||||||
|
if (iommu != amd_iommu_rlookup_table[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
iommu_queue_inv_dev_entry(iommu, i);
|
||||||
|
iommu_completion_wait(iommu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,8 +534,6 @@ void amd_iommu_flush_all_devices(void)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i <= amd_iommu_last_bdf; ++i) {
|
for (i = 0; i <= amd_iommu_last_bdf; ++i) {
|
||||||
if (amd_iommu_pd_table[i] == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
iommu = amd_iommu_rlookup_table[i];
|
iommu = amd_iommu_rlookup_table[i];
|
||||||
if (!iommu)
|
if (!iommu)
|
||||||
@@ -497,6 +544,22 @@ void amd_iommu_flush_all_devices(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reset_iommu_command_buffer(struct amd_iommu *iommu)
|
||||||
|
{
|
||||||
|
pr_err("AMD-Vi: Resetting IOMMU command buffer\n");
|
||||||
|
|
||||||
|
if (iommu->reset_in_progress)
|
||||||
|
panic("AMD-Vi: ILLEGAL_COMMAND_ERROR while resetting command buffer\n");
|
||||||
|
|
||||||
|
iommu->reset_in_progress = true;
|
||||||
|
|
||||||
|
amd_iommu_reset_cmd_buffer(iommu);
|
||||||
|
flush_all_devices_for_iommu(iommu);
|
||||||
|
flush_all_domains_on_iommu(iommu);
|
||||||
|
|
||||||
|
iommu->reset_in_progress = false;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
*
|
*
|
||||||
* The functions below are used the create the page table mappings for
|
* The functions below are used the create the page table mappings for
|
||||||
|
@@ -252,7 +252,7 @@ static void __init iommu_feature_disable(struct amd_iommu *iommu, u8 bit)
|
|||||||
/* Function to enable the hardware */
|
/* Function to enable the hardware */
|
||||||
static void iommu_enable(struct amd_iommu *iommu)
|
static void iommu_enable(struct amd_iommu *iommu)
|
||||||
{
|
{
|
||||||
printk(KERN_INFO "AMD IOMMU: Enabling IOMMU at %s cap 0x%hx\n",
|
printk(KERN_INFO "AMD-Vi: Enabling IOMMU at %s cap 0x%hx\n",
|
||||||
dev_name(&iommu->dev->dev), iommu->cap_ptr);
|
dev_name(&iommu->dev->dev), iommu->cap_ptr);
|
||||||
|
|
||||||
iommu_feature_enable(iommu, CONTROL_IOMMU_EN);
|
iommu_feature_enable(iommu, CONTROL_IOMMU_EN);
|
||||||
@@ -434,6 +434,20 @@ static u8 * __init alloc_command_buffer(struct amd_iommu *iommu)
|
|||||||
return cmd_buf;
|
return cmd_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function resets the command buffer if the IOMMU stopped fetching
|
||||||
|
* commands from it.
|
||||||
|
*/
|
||||||
|
void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu)
|
||||||
|
{
|
||||||
|
iommu_feature_disable(iommu, CONTROL_CMDBUF_EN);
|
||||||
|
|
||||||
|
writel(0x00, iommu->mmio_base + MMIO_CMD_HEAD_OFFSET);
|
||||||
|
writel(0x00, iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
|
||||||
|
|
||||||
|
iommu_feature_enable(iommu, CONTROL_CMDBUF_EN);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function writes the command buffer address to the hardware and
|
* This function writes the command buffer address to the hardware and
|
||||||
* enables it.
|
* enables it.
|
||||||
@@ -450,11 +464,7 @@ static void iommu_enable_command_buffer(struct amd_iommu *iommu)
|
|||||||
memcpy_toio(iommu->mmio_base + MMIO_CMD_BUF_OFFSET,
|
memcpy_toio(iommu->mmio_base + MMIO_CMD_BUF_OFFSET,
|
||||||
&entry, sizeof(entry));
|
&entry, sizeof(entry));
|
||||||
|
|
||||||
/* set head and tail to zero manually */
|
amd_iommu_reset_cmd_buffer(iommu);
|
||||||
writel(0x00, iommu->mmio_base + MMIO_CMD_HEAD_OFFSET);
|
|
||||||
writel(0x00, iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
|
|
||||||
|
|
||||||
iommu_feature_enable(iommu, CONTROL_CMDBUF_EN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init free_command_buffer(struct amd_iommu *iommu)
|
static void __init free_command_buffer(struct amd_iommu *iommu)
|
||||||
@@ -858,7 +868,7 @@ static int __init init_iommu_all(struct acpi_table_header *table)
|
|||||||
switch (*p) {
|
switch (*p) {
|
||||||
case ACPI_IVHD_TYPE:
|
case ACPI_IVHD_TYPE:
|
||||||
|
|
||||||
DUMP_printk("IOMMU: device: %02x:%02x.%01x cap: %04x "
|
DUMP_printk("device: %02x:%02x.%01x cap: %04x "
|
||||||
"seg: %d flags: %01x info %04x\n",
|
"seg: %d flags: %01x info %04x\n",
|
||||||
PCI_BUS(h->devid), PCI_SLOT(h->devid),
|
PCI_BUS(h->devid), PCI_SLOT(h->devid),
|
||||||
PCI_FUNC(h->devid), h->cap_ptr,
|
PCI_FUNC(h->devid), h->cap_ptr,
|
||||||
@@ -902,7 +912,7 @@ static int __init iommu_setup_msi(struct amd_iommu *iommu)
|
|||||||
|
|
||||||
r = request_irq(iommu->dev->irq, amd_iommu_int_handler,
|
r = request_irq(iommu->dev->irq, amd_iommu_int_handler,
|
||||||
IRQF_SAMPLE_RANDOM,
|
IRQF_SAMPLE_RANDOM,
|
||||||
"AMD IOMMU",
|
"AMD-Vi",
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (r) {
|
if (r) {
|
||||||
@@ -1150,7 +1160,7 @@ int __init amd_iommu_init(void)
|
|||||||
|
|
||||||
|
|
||||||
if (no_iommu) {
|
if (no_iommu) {
|
||||||
printk(KERN_INFO "AMD IOMMU disabled by kernel command line\n");
|
printk(KERN_INFO "AMD-Vi disabled by kernel command line\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1248,16 +1258,16 @@ int __init amd_iommu_init(void)
|
|||||||
|
|
||||||
enable_iommus();
|
enable_iommus();
|
||||||
|
|
||||||
printk(KERN_INFO "AMD IOMMU: device isolation ");
|
printk(KERN_INFO "AMD-Vi: device isolation ");
|
||||||
if (amd_iommu_isolate)
|
if (amd_iommu_isolate)
|
||||||
printk("enabled\n");
|
printk("enabled\n");
|
||||||
else
|
else
|
||||||
printk("disabled\n");
|
printk("disabled\n");
|
||||||
|
|
||||||
if (amd_iommu_unmap_flush)
|
if (amd_iommu_unmap_flush)
|
||||||
printk(KERN_INFO "AMD IOMMU: IO/TLB flush on unmap enabled\n");
|
printk(KERN_INFO "AMD-Vi: IO/TLB flush on unmap enabled\n");
|
||||||
else
|
else
|
||||||
printk(KERN_INFO "AMD IOMMU: Lazy IO/TLB flushing enabled\n");
|
printk(KERN_INFO "AMD-Vi: Lazy IO/TLB flushing enabled\n");
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
|
Reference in New Issue
Block a user