In case the AMD IOMMU driver fails to enable interrupt, it currently results in failure to boot due to invalid dma_ops for buses and devices previously set when they were added to the IOMMU.
Therefore, fix this by unsetting the bus's iommu, and setup end-point devices to use dma_direct_ops. Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpa...@amd.com> --- drivers/iommu/amd_iommu.c | 14 +++++++++++++- drivers/iommu/amd_iommu_init.c | 19 +++++++++++++++++-- drivers/iommu/amd_iommu_proto.h | 2 ++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 5337fb2..3446763 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -381,6 +381,9 @@ static bool check_device(struct device *dev) { int devid; + if (amd_iommu_failed_initialize()) + return false; + if (!dev || !dev->dma_mask) return false; @@ -486,7 +489,7 @@ static void iommu_uninit_device(struct device *dev) iommu_group_remove_device(dev); /* Remove dma-ops */ - dev->dma_ops = NULL; + dev->dma_ops = &dma_direct_ops; /* * We keep dev_data around for unplugged devices and reuse it when the @@ -2722,6 +2725,15 @@ static int init_reserved_iova_ranges(void) return 0; } +void __init amd_iommu_uninit_api(void) +{ + bus_unset_iommu(&pci_bus_type, &amd_iommu_ops); +#ifdef CONFIG_ARM_AMBA + bus_unset_iommu(&amba_bustype, &amd_iommu_ops); +#endif + bus_unset_iommu(&platform_bus_type, &amd_iommu_ops); +} + int __init amd_iommu_init_api(void) { int ret, err = 0; diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 09d271e..d3f9cb3 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -245,6 +245,7 @@ enum iommu_init_state { IOMMU_INITIALIZED, IOMMU_NOT_FOUND, IOMMU_INIT_ERROR, + IOMMU_RESOURCE_FREE, IOMMU_CMDLINE_DISABLED, }; @@ -307,6 +308,11 @@ int amd_iommu_get_num_iommus(void) return amd_iommus_present; } +bool amd_iommu_failed_initialize(void) +{ + return init_state == IOMMU_RESOURCE_FREE; +} + /* Access to l1 and l2 indexed register spaces */ static u32 iommu_read_l1(struct amd_iommu *iommu, u16 l1, u8 address) @@ -2331,10 +2337,14 @@ static void __init free_iommu_resources(void) free_iommu_all(); + /* + * We failed to initialize the AMD IOMMU driver, + * and all resources are freed. + */ + init_state = IOMMU_RESOURCE_FREE; #ifdef CONFIG_GART_IOMMU /* - * We failed to initialize the AMD IOMMU - try fallback to GART - * if possible. + * try fallback to GART if possible. */ gart_iommu_init(); @@ -2583,6 +2593,8 @@ static int amd_iommu_enable_interrupts(void) } out: + if (ret) + pr_err("AMD-Vi: Failed to enable IOMMU interrupts.\n"); return ret; } @@ -2665,6 +2677,7 @@ static int __init state_next(void) break; case IOMMU_NOT_FOUND: case IOMMU_INIT_ERROR: + case IOMMU_RESOURCE_FREE: case IOMMU_CMDLINE_DISABLED: /* Error states => do nothing */ ret = -EINVAL; @@ -2684,6 +2697,7 @@ static int __init iommu_go_to_state(enum iommu_init_state state) while (init_state != state) { if (init_state == IOMMU_NOT_FOUND || init_state == IOMMU_INIT_ERROR || + init_state == IOMMU_RESOURCE_FREE || init_state == IOMMU_CMDLINE_DISABLED) break; ret = state_next(); @@ -2747,6 +2761,7 @@ static int __init amd_iommu_init(void) ret = iommu_go_to_state(IOMMU_INITIALIZED); if (ret) { + amd_iommu_uninit_api(); free_dma_resources(); if (!irq_remapping_enabled) { disable_iommus(); diff --git a/drivers/iommu/amd_iommu_proto.h b/drivers/iommu/amd_iommu_proto.h index 640c286..7d81254 100644 --- a/drivers/iommu/amd_iommu_proto.h +++ b/drivers/iommu/amd_iommu_proto.h @@ -32,6 +32,8 @@ extern int amd_iommu_init_devices(void); extern void amd_iommu_uninit_devices(void); extern void amd_iommu_init_notifier(void); extern int amd_iommu_init_api(void); +extern void amd_iommu_uninit_api(void); +extern bool amd_iommu_failed_initialize(void); /* Needed for interrupt remapping */ extern int amd_iommu_prepare(void); -- 2.7.4 _______________________________________________ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu