Merge tag 'iommu-updates-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu
Pull IOMMU updates from Joerg Roedel: "The updates are mostly about the x86 IOMMUs this time. Exceptions are the groundwork for the PAMU IOMMU from Freescale (for a PPC platform) and an extension to the IOMMU group interface. On the x86 side this includes a workaround for VT-d to disable interrupt remapping on broken chipsets. On the AMD-Vi side the most important new feature is a kernel command-line interface to override broken information in IVRS ACPI tables and get interrupt remapping working this way. Besides that there are small fixes all over the place." * tag 'iommu-updates-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (24 commits) iommu/tegra: Fix printk formats for dma_addr_t iommu: Add a function to find an iommu group by id iommu/vt-d: Remove warning for HPET scope type iommu: Move swap_pci_ref function to drivers/iommu/pci.h. iommu/vt-d: Disable translation if already enabled iommu/amd: fix error return code in early_amd_iommu_init() iommu/AMD: Per-thread IOMMU Interrupt Handling iommu: Include linux/err.h iommu/amd: Workaround for ERBT1312 iommu/amd: Document ivrs_ioapic and ivrs_hpet parameters iommu/amd: Don't report firmware bugs with cmd-line ivrs overrides iommu/amd: Add ioapic and hpet ivrs override iommu/amd: Add early maps for ioapic and hpet iommu/amd: Extend IVRS special device data structure iommu/amd: Move add_special_device() to __init iommu: Fix compile warnings with forward declarations iommu/amd: Properly initialize irq-table lock iommu/amd: Use AMD specific data structure for irq remapping iommu/amd: Remove map_sg_no_iommu() iommu/vt-d: add quirk for broken interrupt remapping on 55XX chipsets ...
This commit is contained in:
@@ -213,6 +213,14 @@ enum iommu_init_state {
|
||||
IOMMU_INIT_ERROR,
|
||||
};
|
||||
|
||||
/* Early ioapic and hpet maps from kernel command line */
|
||||
#define EARLY_MAP_SIZE 4
|
||||
static struct devid_map __initdata early_ioapic_map[EARLY_MAP_SIZE];
|
||||
static struct devid_map __initdata early_hpet_map[EARLY_MAP_SIZE];
|
||||
static int __initdata early_ioapic_map_size;
|
||||
static int __initdata early_hpet_map_size;
|
||||
static bool __initdata cmdline_maps;
|
||||
|
||||
static enum iommu_init_state init_state = IOMMU_START_STATE;
|
||||
|
||||
static int amd_iommu_enable_interrupts(void);
|
||||
@@ -703,31 +711,66 @@ static void __init set_dev_entry_from_acpi(struct amd_iommu *iommu,
|
||||
set_iommu_for_device(iommu, devid);
|
||||
}
|
||||
|
||||
static int add_special_device(u8 type, u8 id, u16 devid)
|
||||
static int __init add_special_device(u8 type, u8 id, u16 devid, bool cmd_line)
|
||||
{
|
||||
struct devid_map *entry;
|
||||
struct list_head *list;
|
||||
|
||||
if (type != IVHD_SPECIAL_IOAPIC && type != IVHD_SPECIAL_HPET)
|
||||
if (type == IVHD_SPECIAL_IOAPIC)
|
||||
list = &ioapic_map;
|
||||
else if (type == IVHD_SPECIAL_HPET)
|
||||
list = &hpet_map;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(entry, list, list) {
|
||||
if (!(entry->id == id && entry->cmd_line))
|
||||
continue;
|
||||
|
||||
pr_info("AMD-Vi: Command-line override present for %s id %d - ignoring\n",
|
||||
type == IVHD_SPECIAL_IOAPIC ? "IOAPIC" : "HPET", id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
entry->id = id;
|
||||
entry->devid = devid;
|
||||
|
||||
if (type == IVHD_SPECIAL_IOAPIC)
|
||||
list = &ioapic_map;
|
||||
else
|
||||
list = &hpet_map;
|
||||
entry->id = id;
|
||||
entry->devid = devid;
|
||||
entry->cmd_line = cmd_line;
|
||||
|
||||
list_add_tail(&entry->list, list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init add_early_maps(void)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < early_ioapic_map_size; ++i) {
|
||||
ret = add_special_device(IVHD_SPECIAL_IOAPIC,
|
||||
early_ioapic_map[i].id,
|
||||
early_ioapic_map[i].devid,
|
||||
early_ioapic_map[i].cmd_line);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < early_hpet_map_size; ++i) {
|
||||
ret = add_special_device(IVHD_SPECIAL_HPET,
|
||||
early_hpet_map[i].id,
|
||||
early_hpet_map[i].devid,
|
||||
early_hpet_map[i].cmd_line);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the device exclusion range from ACPI and initializes the IOMMU with
|
||||
* it
|
||||
@@ -764,6 +807,12 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,
|
||||
u32 dev_i, ext_flags = 0;
|
||||
bool alias = false;
|
||||
struct ivhd_entry *e;
|
||||
int ret;
|
||||
|
||||
|
||||
ret = add_early_maps();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* First save the recommended feature enable bits from ACPI
|
||||
@@ -929,7 +978,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,
|
||||
PCI_FUNC(devid));
|
||||
|
||||
set_dev_entry_from_acpi(iommu, devid, e->flags, 0);
|
||||
ret = add_special_device(type, handle, devid);
|
||||
ret = add_special_device(type, handle, devid, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
@@ -1275,7 +1324,7 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
|
||||
amd_iommu_int_handler,
|
||||
amd_iommu_int_thread,
|
||||
0, "AMD-Vi",
|
||||
iommu->dev);
|
||||
iommu);
|
||||
|
||||
if (r) {
|
||||
pci_disable_msi(iommu->dev);
|
||||
@@ -1638,18 +1687,28 @@ static void __init free_on_init_error(void)
|
||||
|
||||
static bool __init check_ioapic_information(void)
|
||||
{
|
||||
const char *fw_bug = FW_BUG;
|
||||
bool ret, has_sb_ioapic;
|
||||
int idx;
|
||||
|
||||
has_sb_ioapic = false;
|
||||
ret = false;
|
||||
|
||||
/*
|
||||
* If we have map overrides on the kernel command line the
|
||||
* messages in this function might not describe firmware bugs
|
||||
* anymore - so be careful
|
||||
*/
|
||||
if (cmdline_maps)
|
||||
fw_bug = "";
|
||||
|
||||
for (idx = 0; idx < nr_ioapics; idx++) {
|
||||
int devid, id = mpc_ioapic_id(idx);
|
||||
|
||||
devid = get_ioapic_devid(id);
|
||||
if (devid < 0) {
|
||||
pr_err(FW_BUG "AMD-Vi: IOAPIC[%d] not in IVRS table\n", id);
|
||||
pr_err("%sAMD-Vi: IOAPIC[%d] not in IVRS table\n",
|
||||
fw_bug, id);
|
||||
ret = false;
|
||||
} else if (devid == IOAPIC_SB_DEVID) {
|
||||
has_sb_ioapic = true;
|
||||
@@ -1666,11 +1725,11 @@ static bool __init check_ioapic_information(void)
|
||||
* when the BIOS is buggy and provides us the wrong
|
||||
* device id for the IOAPIC in the system.
|
||||
*/
|
||||
pr_err(FW_BUG "AMD-Vi: No southbridge IOAPIC found in IVRS table\n");
|
||||
pr_err("%sAMD-Vi: No southbridge IOAPIC found\n", fw_bug);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
pr_err("AMD-Vi: Disabling interrupt remapping due to BIOS Bug(s)\n");
|
||||
pr_err("AMD-Vi: Disabling interrupt remapping\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1801,6 +1860,7 @@ static int __init early_amd_iommu_init(void)
|
||||
* Interrupt remapping enabled, create kmem_cache for the
|
||||
* remapping tables.
|
||||
*/
|
||||
ret = -ENOMEM;
|
||||
amd_iommu_irq_cache = kmem_cache_create("irq_remap_cache",
|
||||
MAX_IRQS_PER_TABLE * sizeof(u32),
|
||||
IRQ_TABLE_ALIGNMENT,
|
||||
@@ -2097,8 +2157,70 @@ static int __init parse_amd_iommu_options(char *str)
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("amd_iommu_dump", parse_amd_iommu_dump);
|
||||
__setup("amd_iommu=", parse_amd_iommu_options);
|
||||
static int __init parse_ivrs_ioapic(char *str)
|
||||
{
|
||||
unsigned int bus, dev, fn;
|
||||
int ret, id, i;
|
||||
u16 devid;
|
||||
|
||||
ret = sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn);
|
||||
|
||||
if (ret != 4) {
|
||||
pr_err("AMD-Vi: Invalid command line: ivrs_ioapic%s\n", str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (early_ioapic_map_size == EARLY_MAP_SIZE) {
|
||||
pr_err("AMD-Vi: Early IOAPIC map overflow - ignoring ivrs_ioapic%s\n",
|
||||
str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7);
|
||||
|
||||
cmdline_maps = true;
|
||||
i = early_ioapic_map_size++;
|
||||
early_ioapic_map[i].id = id;
|
||||
early_ioapic_map[i].devid = devid;
|
||||
early_ioapic_map[i].cmd_line = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __init parse_ivrs_hpet(char *str)
|
||||
{
|
||||
unsigned int bus, dev, fn;
|
||||
int ret, id, i;
|
||||
u16 devid;
|
||||
|
||||
ret = sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn);
|
||||
|
||||
if (ret != 4) {
|
||||
pr_err("AMD-Vi: Invalid command line: ivrs_hpet%s\n", str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (early_hpet_map_size == EARLY_MAP_SIZE) {
|
||||
pr_err("AMD-Vi: Early HPET map overflow - ignoring ivrs_hpet%s\n",
|
||||
str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7);
|
||||
|
||||
cmdline_maps = true;
|
||||
i = early_hpet_map_size++;
|
||||
early_hpet_map[i].id = id;
|
||||
early_hpet_map[i].devid = devid;
|
||||
early_hpet_map[i].cmd_line = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("amd_iommu_dump", parse_amd_iommu_dump);
|
||||
__setup("amd_iommu=", parse_amd_iommu_options);
|
||||
__setup("ivrs_ioapic", parse_ivrs_ioapic);
|
||||
__setup("ivrs_hpet", parse_ivrs_hpet);
|
||||
|
||||
IOMMU_INIT_FINISH(amd_iommu_detect,
|
||||
gart_iommu_hole_init,
|
||||
|
Reference in New Issue
Block a user