[PATCH v2 2/2] dma-iommu: Check that swiotlb is active before trying to use it
If the IOMMU is in use and an untrusted device is connected to an external facing port but the address requested isn't page aligned will cause the kernel to attempt to use bounce buffers. If for some reason the bounce buffers have not been allocated this is a problem that should be made apparent to the user. Signed-off-by: Mario Limonciello --- v1->v2: * Move error message into the caller drivers/iommu/dma-iommu.c | 5 + 1 file changed, 5 insertions(+) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 09f6e1c0f9c0..1ca85d37eeab 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -971,6 +971,11 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page, void *padding_start; size_t padding_size, aligned_size; + if (!is_swiotlb_active(dev)) { + dev_warn_once(dev, "DMA bounce buffers are inactive, unable to map unaligned transaction.\n"); + return DMA_MAPPING_ERROR; + } + aligned_size = iova_align(iovad, size); phys = swiotlb_tbl_map_single(dev, phys, size, aligned_size, iova_mask(iovad), dir, attrs); -- 2.34.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 0/2] Fix issues with untrusted devices and AMD IOMMU
It's been observed that plugging in a TBT3 NVME device to a port marked with ExternalFacingPort that some DMA transactions occur that are not a full page and so the DMA API attempts to use software bounce buffers instead of relying upon the IOMMU translation. This doesn't work and leads to messaging like: swiotlb buffer is full (sz: 4096 bytes), total 0 (slots), used 0 (slots) The bounce buffers were originally set up, but torn down during the boot process. * This happens because as part of IOMMU initialization `amd_iommu_init_dma_ops` gets called and resets the global swiotlb to 0. * When late_init gets called `pci_swiotlb_late_init` `swiotlb_exit` is called and the buffers are torn down. This can be observed in the logs: ``` [0.407286] AMD-Vi: Extended features (0x246577efa2254afa): PPR NX GT [5] IA GA PC GA_vAPIC [0.407291] AMD-Vi: Interrupt remapping enabled [0.407292] AMD-Vi: Virtual APIC enabled [0.407872] software IO TLB: tearing down default memory pool ``` This series fixes the behavior of AMD IOMMU to enable swiotlb so that non-page aligned DMA goes through a bounce buffer. It also adds a message to help with debugging similar problems in the future. Mario Limonciello (2): iommu/amd: Enable swiotlb in all cases dma-iommu: Check that swiotlb is active before trying to use it drivers/iommu/amd/iommu.c | 7 --- drivers/iommu/dma-iommu.c | 5 + 2 files changed, 5 insertions(+), 7 deletions(-) -- 2.34.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 1/2] iommu/amd: Enable swiotlb in all cases
Previously the AMD IOMMU would only enable SWIOTLB in certain circumstances: * IOMMU in passthrough mode * SME enabled This logic however doesn't work when an untrusted device is plugged in that doesn't do page aligned DMA transactions. The expectation is that a bounce buffer is used for those transactions. This fails like this: swiotlb buffer is full (sz: 4096 bytes), total 0 (slots), used 0 (slots) That happens because the bounce buffers have been allocated, followed by freed during startup but the bounce buffering code expects that all IOMMUs have left it enabled. Remove the criteria to set up bounce buffers on AMD systems to ensure they're always available for supporting untrusted devices. Fixes: 82612d66d51d ("iommu: Allow the dma-iommu api to use bounce buffers") Suggested-by: Christoph Hellwig Signed-off-by: Mario Limonciello --- v1->v2: * Enable swiotlb for AMD instead of ignoring it for inactive drivers/iommu/amd/iommu.c | 7 --- 1 file changed, 7 deletions(-) diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index a1ada7bff44e..079694f894b8 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -1838,17 +1838,10 @@ void amd_iommu_domain_update(struct protection_domain *domain) amd_iommu_domain_flush_complete(domain); } -static void __init amd_iommu_init_dma_ops(void) -{ - swiotlb = (iommu_default_passthrough() || sme_me_mask) ? 1 : 0; -} - int __init amd_iommu_init_api(void) { int err; - amd_iommu_init_dma_ops(); - err = bus_set_iommu(_bus_type, _iommu_ops); if (err) return err; -- 2.34.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 2/2] iommu: Don't use swiotlb unless it's active
The helper function `dev_use_swiotlb` is used for various decision making points for how to handle DMA mapping requests. If the kernel doesn't have any memory allocated for swiotlb to use, then an untrusted device being connected to the system may fail to initialize when a request is made. To avoid this situation, don't mark the use of swiotlb when it has not been set up. Signed-off-by: Mario Limonciello --- drivers/iommu/dma-iommu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 09f6e1c0f9c0..92ca136c8a12 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -504,7 +504,8 @@ static bool dev_is_untrusted(struct device *dev) static bool dev_use_swiotlb(struct device *dev) { - return IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev); + return IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev) \ + && is_swiotlb_active(dev); } /** -- 2.34.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 1/2] swiotlb: Check that slabs have been allocated when requested
If the IOMMU is in use and an untrusted device is connected to an external facing port but the address requested isn't page aligned will cause the kernel to attempt to use bounce buffers. If the bounce buffers have not been allocated however, this leads to messages like this: swiotlb buffer is full (sz: 4096 bytes), total 0 (slots), used 0 (slots) Clarify the error message because the buffer isn't full, it doesn't exist! Signed-off-by: Mario Limonciello --- kernel/dma/swiotlb.c | 5 + 1 file changed, 5 insertions(+) diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index 73a41cec9e38..d2a20cedf0d2 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -591,6 +591,11 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr, if (!mem) panic("Can not allocate SWIOTLB buffer earlier and can't now provide you with the DMA bounce buffer"); + if (!mem->nslabs) { + dev_warn_once(dev, "No slabs have been configured, unable to use SWIOTLB buffer"); + return (phys_addr_t)DMA_MAPPING_ERROR; + } + if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n"); -- 2.34.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 0/2] Fix issues with untrusted devices and AMD IOMMU
It's been observed that plugging in a TBT3 NVME device to a port marked with ExternalFacingPort that some DMA transactions occur that are not a full page and so the DMA API attempts to use software bounce buffers instead of relying upon the IOMMU translation. This doesn't work and leads to messaging like: swiotlb buffer is full (sz: 4096 bytes), total 0 (slots), used 0 (slots) The bounce buffers were originally set up, but torn down during the boot process. * This happens because as part of IOMMU initialization `amd_iommu_init_dma_ops` gets called and resets the global swiotlb to 0. * When late_init gets called `pci_swiotlb_late_init` `swiotlb_exit` is called and the buffers are torn down. This can be observed in the logs: ``` [0.407286] AMD-Vi: Extended features (0x246577efa2254afa): PPR NX GT [5] IA GA PC GA_vAPIC [0.407291] AMD-Vi: Interrupt remapping enabled [0.407292] AMD-Vi: Virtual APIC enabled [0.407872] software IO TLB: tearing down default memory pool ``` This series adds some better messaging in case something like this comes up again and also adds checks that swiotlb really is active before trying to use it. Mario Limonciello (2): swiotlb: Check that slabs have been allocated when requested iommu: Don't use swiotlb unless it's active drivers/iommu/dma-iommu.c | 3 ++- kernel/dma/swiotlb.c | 5 + 2 files changed, 7 insertions(+), 1 deletion(-) -- 2.34.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2] iommu/amd: Add support to indicate whether DMA remap support is enabled
Bit 1 of the IVFS IVInfo field indicates that IOMMU has been used for pre-boot DMA protection. Export this capability to allow other places in the kernel to be able to check for it on AMD systems. Cc: Robin Murphy Link: https://www.amd.com/system/files/TechDocs/48882_IOMMU.pdf Signed-off-by: Mario Limonciello --- changes from v1->v2: * Rebase on top of Robin Murphy's patch series to add generic interface https://lore.kernel.org/linux-usb/cover.1647624084.git.robin.mur...@arm.com/T/#t * Drop changes to Thunderbolt driver Robin, If your patch series revs again, and this looks good suggest to just roll it into your series as a 3rd patch. drivers/iommu/amd/amd_iommu_types.h | 4 drivers/iommu/amd/init.c| 3 +++ drivers/iommu/amd/iommu.c | 2 ++ 3 files changed, 9 insertions(+) diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index 47108ed44fbb..72d0f5e2f651 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -407,6 +407,7 @@ /* IOMMU IVINFO */ #define IOMMU_IVINFO_OFFSET 36 #define IOMMU_IVINFO_EFRSUP BIT(0) +#define IOMMU_IVINFO_DMA_REMAP BIT(1) /* IOMMU Feature Reporting Field (for IVHD type 10h */ #define IOMMU_FEAT_GASUP_SHIFT 6 @@ -449,6 +450,9 @@ extern struct irq_remap_table **irq_lookup_table; /* Interrupt remapping feature used? */ extern bool amd_iommu_irq_remap; +/* IVRS indicates that pre-boot remapping was enabled */ +extern bool amdr_ivrs_remap_support; + /* kmem_cache to get tables with 128 byte alignement */ extern struct kmem_cache *amd_iommu_irq_cache; diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 7bfe37e52e21..fc12ead49a03 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -182,6 +182,7 @@ u32 amd_iommu_max_pasid __read_mostly = ~0; bool amd_iommu_v2_present __read_mostly; static bool amd_iommu_pc_present __read_mostly; +bool amdr_ivrs_remap_support __read_mostly; bool amd_iommu_force_isolation __read_mostly; @@ -326,6 +327,8 @@ static void __init early_iommu_features_init(struct amd_iommu *iommu, { if (amd_iommu_ivinfo & IOMMU_IVINFO_EFRSUP) iommu->features = h->efr_reg; + if (amd_iommu_ivinfo & IOMMU_IVINFO_DMA_REMAP) + amdr_ivrs_remap_support = true; } /* Access to l1 and l2 indexed register spaces */ diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index a18b549951bb..e4b4dad027f7 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -2162,6 +2162,8 @@ static bool amd_iommu_capable(enum iommu_cap cap) return (irq_remapping_enabled == 1); case IOMMU_CAP_NOEXEC: return false; + case IOMMU_CAP_PRE_BOOT_PROTECTION: + return amdr_ivrs_remap_support; default: break; } -- 2.34.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 2/2] thunderbolt: Use pre-boot DMA protection on AMD systems
The information is exported from the IOMMU driver whether or not pre-boot DMA protection has been enabled on AMD systems. Use this information to properly set iomma_dma_protection. Link: https://docs.microsoft.com/en-us/windows-hardware/design/device-experiences/oem-kernel-dma-protection Link: https://www.amd.com/system/files/TechDocs/48882_IOMMU.pdf Signed-off-by: Mario Limonciello --- drivers/thunderbolt/domain.c | 11 --- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 7018d959f775..e03790735c12 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -6,6 +6,7 @@ * Author: Mika Westerberg */ +#include #include #include #include @@ -259,11 +260,15 @@ static ssize_t iommu_dma_protection_show(struct device *dev, { /* * Kernel DMA protection is a feature where Thunderbolt security is -* handled natively using IOMMU. It is enabled when IOMMU is -* enabled and ACPI DMAR table has DMAR_PLATFORM_OPT_IN set. +* handled natively using IOMMU. It is enabled when the IOMMU is +* enabled and either: +* ACPI DMAR table has DMAR_PLATFORM_OPT_IN set +* or +* ACPI IVRS table has DMA_REMAP bitset */ return sprintf(buf, "%d\n", - iommu_present(_bus_type) && dmar_platform_optin()); + iommu_present(_bus_type) && + (dmar_platform_optin() || amd_ivrs_remap_support())); } static DEVICE_ATTR_RO(iommu_dma_protection); -- 2.34.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 1/2] iommu/amd: Add support to indicate whether DMA remap support is enabled
Bit 1 of the IVFS IVInfo field indicates that IOMMU has been used for pre-boot DMA protection. Export this information to the kernel to allow other drivers to use it. Link: https://www.amd.com/system/files/TechDocs/48882_IOMMU.pdf Signed-off-by: Mario Limonciello --- drivers/iommu/amd/amd_iommu_types.h | 1 + drivers/iommu/amd/init.c| 18 ++ include/linux/amd-iommu.h | 5 + 3 files changed, 24 insertions(+) diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index 47108ed44fbb..68e51213f119 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -407,6 +407,7 @@ /* IOMMU IVINFO */ #define IOMMU_IVINFO_OFFSET 36 #define IOMMU_IVINFO_EFRSUP BIT(0) +#define IOMMU_IVINFO_DMA_REMAP BIT(1) /* IOMMU Feature Reporting Field (for IVHD type 10h */ #define IOMMU_FEAT_GASUP_SHIFT 6 diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 7bfe37e52e21..e9b669592dfc 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -182,6 +182,7 @@ u32 amd_iommu_max_pasid __read_mostly = ~0; bool amd_iommu_v2_present __read_mostly; static bool amd_iommu_pc_present __read_mostly; +static bool amdr_ivrs_remap_support __read_mostly; bool amd_iommu_force_isolation __read_mostly; @@ -326,6 +327,8 @@ static void __init early_iommu_features_init(struct amd_iommu *iommu, { if (amd_iommu_ivinfo & IOMMU_IVINFO_EFRSUP) iommu->features = h->efr_reg; + if (amd_iommu_ivinfo & IOMMU_IVINFO_DMA_REMAP) + amdr_ivrs_remap_support = true; } /* Access to l1 and l2 indexed register spaces */ @@ -3269,6 +3272,21 @@ struct amd_iommu *get_amd_iommu(unsigned int idx) return NULL; } +/* + * ivrs_remap_support - Is %IOMMU_IVINFO_DMA_REMAP set in IVRS table + * + * Returns true if the platform has %IOMMU_IVINFO_DMA_REMAP% set in the IOMMU + * IVRS IVInfo field. + * Presence of this flag indicates to the OS/HV that the IOMMU is used for + * Preboot DMA protection and device accessed memory should be remapped after + * the OS has loaded. + */ +bool amd_ivrs_remap_support(void) +{ + return amdr_ivrs_remap_support; +} +EXPORT_SYMBOL_GPL(amd_ivrs_remap_support); + / * * IOMMU EFR Performance Counter support functionality. This code allows diff --git a/include/linux/amd-iommu.h b/include/linux/amd-iommu.h index 58e6c3806c09..d07b9fed6474 100644 --- a/include/linux/amd-iommu.h +++ b/include/linux/amd-iommu.h @@ -170,6 +170,7 @@ amd_iommu_update_ga(int cpu, bool is_run, void *data); extern int amd_iommu_activate_guest_mode(void *data); extern int amd_iommu_deactivate_guest_mode(void *data); +extern bool amd_ivrs_remap_support(void); #else /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */ @@ -194,6 +195,10 @@ static inline int amd_iommu_deactivate_guest_mode(void *data) { return 0; } +static inline bool amd_ivrs_remap_support(void) +{ + return false; +} #endif /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */ int amd_iommu_get_num_iommus(void); -- 2.34.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu