thunderbolt: Make iommu_dma_protection more accurate
Subsystems (8)
Robin Murphy
reviewerv3 cover thunderbolt: Make iommu_dma_protection more accurate
Hi all,
Here's the third and hopefully final outing for this series, addressing Mika's review comments from last time and taking advantage of 5.18-rc1 goodies to be even cleaner and more complete. I've also picked up Mario's AMD IOMMU patch to keep the whole topic together.
Unless there's other pending Thunderbolt development which might conflict, I suspect it would be simplest for the whole lot to go through the IOMMU tree.
Thanks,
Robin.
Mario Limonciello (1):
iommu/amd: Indicate whether DMA remap support is enabled
Robin Murphy (3):
iommu: Introduce device_iommu_capable()
iommu: Add capability for pre-boot DMA protection
thunderbolt: Make iommu_dma_protection more accurate
drivers/iommu/amd/amd_iommu_types.h | 4 ++ drivers/iommu/amd/init.c | 3 ++ drivers/iommu/amd/iommu.c | 4 +- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 2 +- drivers/iommu/arm/arm-smmu/arm-smmu.c | 2 +- drivers/iommu/arm/arm-smmu/qcom_iommu.c | 2 +- drivers/iommu/fsl_pamu_domain.c | 2 +- drivers/iommu/intel/iommu.c | 4 +- drivers/iommu/iommu.c | 25 +++++++++++- drivers/iommu/s390-iommu.c | 2 +- drivers/thunderbolt/domain.c | 12 ++---- drivers/thunderbolt/nhi.c | 44 +++++++++++++++++++++ include/linux/iommu.h | 10 ++++- include/linux/thunderbolt.h | 2 + 14 files changed, 100 insertions(+), 18 deletions(-)
Signature · 6 lines
2.28.0.dirty _______________________________________________ iommu mailing list [email protected] https://lists.linuxfoundation.org/mailman/listinfo/iommu
Robin Murphy
reviewerPATCH v3 1/4 iommu: Introduce device_iommu_capable()
iommu_capable() only really works for systems where all IOMMU instances are completely homogeneous, and all devices are IOMMU-mapped. Implement the new variant which can give an accurate answer for whichever device the caller is actually interested in.
---
v3: New patch; now that the dev_iommu_ops() work has landed we can go
straight to a proper implementation. Also s/dev/device/ to match the precedent of device_iommu_mapped() for the public API.
drivers/iommu/amd/iommu.c | 2 +- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 2 +- drivers/iommu/arm/arm-smmu/arm-smmu.c | 2 +- drivers/iommu/arm/arm-smmu/qcom_iommu.c | 2 +- drivers/iommu/fsl_pamu_domain.c | 2 +- drivers/iommu/intel/iommu.c | 2 +- drivers/iommu/iommu.c | 25 ++++++++++++++++++++- drivers/iommu/s390-iommu.c | 2 +- include/linux/iommu.h | 8 ++++++- 9 files changed, 38 insertions(+), 9 deletions(-)
drivers/iommu/amd/iommu.c +1 -1
@@ -2132,7 +2132,7 @@ static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,return ops->iova_to_phys (ops, iova); } -static bool amd_iommu_capable (enum iommu_cap cap) +static bool amd_iommu_capable (struct device *dev,enum iommu_cap cap) {switch (cap) {case IOMMU_CAP_CACHE_COHERENCY :
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1 -1
@@ -1981,7 +1981,7 @@ static const struct iommu_flush_ops arm_smmu_flush_ops = { };/* IOMMU API */ -static bool arm_smmu_capable (enum iommu_cap cap) +static bool arm_smmu_capable (struct device *dev,enum iommu_cap cap) {switch (cap) {case IOMMU_CAP_CACHE_COHERENCY :
drivers/iommu/arm/arm-smmu/arm-smmu.c +1 -1
@@ -1313,7 +1313,7 @@ static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,return ops->iova_to_phys (ops, iova); } -static bool arm_smmu_capable (enum iommu_cap cap) +static bool arm_smmu_capable (struct device *dev,enum iommu_cap cap) {switch (cap) {case IOMMU_CAP_CACHE_COHERENCY :
drivers/iommu/arm/arm-smmu/qcom_iommu.c +1 -1
@@ -493,7 +493,7 @@ static phys_addr_t qcom_iommu_iova_to_phys(struct iommu_domain *domain,return ret; } -static bool qcom_iommu_capable (enum iommu_cap cap) +static bool qcom_iommu_capable (struct device *dev,enum iommu_cap cap) {switch (cap) {case IOMMU_CAP_CACHE_COHERENCY :
drivers/iommu/fsl_pamu_domain.c +1 -1
@@ -177,7 +177,7 @@ static phys_addr_t fsl_pamu_iova_to_phys(struct iommu_domain *domain,return iova; } -static bool fsl_pamu_capable (enum iommu_cap cap) +static bool fsl_pamu_capable (struct device *dev,enum iommu_cap cap) {return cap ==IOMMU_CAP_CACHE_COHERENCY ; }
drivers/iommu/intel/iommu.c +1 -1
@@ -4544,7 +4544,7 @@ static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,return phys; } -static bool intel_iommu_capable (enum iommu_cap cap) +static bool intel_iommu_capable (struct device *dev,enum iommu_cap cap) {if (cap ==IOMMU_CAP_CACHE_COHERENCY )return domain_update_iommu_snooping (NULL );
drivers/iommu/iommu.c +24 -1
@@ -1889,12 +1889,35 @@ static int iommu_bus_init(struct bus_type *bus)return err; } +/** +* device_iommu_capable() - check for a general IOMMU capability +* @dev: device to which the capability would be relevant, if available +* @cap: IOMMU capability +* +* Return: true if an IOMMU is present and supports the given capability +* for the given device, otherwise false. +*/ +bool device_iommu_capable (struct device *dev,enum iommu_cap cap) +{ +const struct iommu_ops *ops; + +if (!dev->iommu || !dev->iommu->iommu_dev) +return false; + + ops =dev_iommu_ops (dev); +if (!ops->capable) +return false; + +return ops->capable (dev, cap); +} +EXPORT_SYMBOL_GPL (device_iommu_capable); +bool iommu_capable (struct bus_type *bus,enum iommu_cap cap) {if (!bus->iommu_ops || !bus->iommu_ops->capable)return false; -return bus->iommu_ops->capable (cap); +return bus->iommu_ops->capable (NULL , cap); }EXPORT_SYMBOL_GPL (iommu_capable);
drivers/iommu/s390-iommu.c +1 -1
@@ -39,7 +39,7 @@ static struct s390_domain *to_s390_domain(struct iommu_domain *dom) return container_of(dom, struct s390_domain, domain); } -static bool s390_iommu_capable (enum iommu_cap cap) +static bool s390_iommu_capable (struct device *dev,enum iommu_cap cap) {switch (cap) {case IOMMU_CAP_CACHE_COHERENCY :
include/linux/iommu.h +7 -1
@@ -219,7 +219,7 @@ struct iommu_iotlb_gather {* @owner: Driver module providing these ops */ struct iommu_ops { -bool (*capable)(enum iommu_cap ); +bool (*capable)(struct device *dev,enum iommu_cap );/* Domain allocation and freeing by the iommu driver */ struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type);
@@ -415,6 +415,7 @@ static inline const struct iommu_ops *dev_iommu_ops(struct device *dev)#define IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER 6/* Post Driver unbind */ extern int bus_iommu_probe (struct bus_type *bus); +extern bool device_iommu_capable (struct device *dev,enum iommu_cap cap);extern bool iommu_capable (struct bus_type *bus,enum iommu_cap cap);extern struct iommu_domain *iommu_domain_alloc (struct device *dev);extern struct iommu_group *iommu_group_get_by_id (int id);
@@ -682,6 +683,11 @@ struct iommu_device {};struct iommu_fault_param {};struct iommu_iotlb_gather {}; +static inline bool device_iommu_capable (struct device *dev,enum iommu_cap cap) +{ +return false; +} +static inline bool iommu_capable (struct bus_type *bus,enum iommu_cap cap) {return false;
Signature · 6 lines
2.28.0.dirty _______________________________________________ iommu mailing list [email protected] https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v3 1/4] iommu: Introduce device_iommu_capable()
Quoted 11 lines · Robin Murphy
iommu_capable()only really works for systems where all IOMMU instances are completely homogeneous, and all devices are IOMMU-mapped. Implement the new variant which can give an accurate answer for whichever device the caller is actually interested in.Signed-off-by Robin Murphy <[email protected]>---
v3: New patch; now that the
dev_iommu_ops()work has landed we can go
straight to a proper implementation. Also s/dev/device/ to match the precedent ofdevice_iommu_mapped()for the public API.
I'm a little worrited about a method with a parameter than can be NULL.
Also usnic, vmd, and vdpa really want to use your new device_iommu_capable as they check based on a device. Just VFIO is special as usual..
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Robin Murphy
reviewerRe: [PATCH v3 1/4] iommu: Introduce device_iommu_capable()
Quoted 9 lines · Christoph Hellwig
Quoted 8 lines · Robin Murphy
iommu_capable()only really works for systems where all IOMMU instances are completely homogeneous, and all devices are IOMMU-mapped. Implement the new variant which can give an accurate answer for whichever device the caller is actually interested in.Signed-off-by Robin Murphy <[email protected]>---
Quoted 6 lines
Quoted 3 lines
v3: New patch; now that the
dev_iommu_ops()work has landed we can go
straight to a proper implementation. Also s/dev/device/ to match the precedent ofdevice_iommu_mapped()for the public API.I'm a little worrited about a method with a parameter than can be NULL.
FWIW, the intent is that that's only temporary.
Quoted 3 lines
Also usnic, vmd, and vdpa really want to use your new
device_iommu_capableas they check based on a device. Just VFIO is special as usual..
Indeed, I have those patches in my stack already, I'm just waiting for this to land before I think about posting them. Once the DMA ownership series lands in parallel, VFIO can also get converted and iommu_capable() goes away entirely. At that point I have a patch for the Arm SMMU drivers to start actually using the new device argument, but Jason's new proposal now puts a twist on that...
On reflection, there's no real need for the internal method to change in lock-step with the external interface. I've no objection to holding off on adding that new parameter until it's reliably useful, and indeed now that I'm looking at it outside the context of the entire cleanup mission, that does sound like the right thing to do anyway :)
(also it turns out I need to respin this patch regardless since I generated it from the wrong point in my branch, where iommu_present() was already gone from the context in iommu.h...)
Cheers,
Robin.
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Robin Murphy
reviewerPATCH v3 2/4 iommu: Add capability for pre-boot DMA protection
In reply to Robin MurphyVT-d's dmar_platform_optin() actually represents a combination of properties fairly well standardised by Microsoft as "Pre-boot DMA Protection" and "Kernel DMA Protection"[1]. As such, we can provide interested consumers with an abstracted capability rather than driver-specific interfaces that won't scale. We name it for the former aspect since that's what external callers are most likely to be interested in; the latter is for the IOMMU layer to handle itself.
--- drivers/iommu/intel/iommu.c | 2 ++ include/linux/iommu.h | 2 ++ 2 files changed, 4 insertions(+)
drivers/iommu/intel/iommu.c +2 -0
@@ -4550,6 +4550,8 @@ static bool intel_iommu_capable(struct device *dev, enum iommu_cap cap)return domain_update_iommu_snooping (NULL );if (cap ==IOMMU_CAP_INTR_REMAP )return irq_remapping_enabled ==1 ; +if (cap ==IOMMU_CAP_PRE_BOOT_PROTECTION ) +return dmar_platform_optin ();return false; }
include/linux/iommu.h +2 -0
@@ -107,6 +107,8 @@ enum iommu_cap {transactions */ IOMMU_CAP_INTR_REMAP ,/* IOMMU supports interrupt isolation */ IOMMU_CAP_NOEXEC ,/* IOMMU_NOEXEC flag */ + IOMMU_CAP_PRE_BOOT_PROTECTION, /* Firmware says it used the IOMMU for + DMA protection and we should too */ };/* These are the possible reserved region types */
Signature · 6 lines
2.28.0.dirty _______________________________________________ iommu mailing list [email protected] https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v3 2/4] iommu: Add capability for pre-boot DMA protection
Quoted 8 lines · Robin Murphy
VT-d's
dmar_platform_optin()actually represents a combination of properties fairly well standardised by Microsoft as "Pre-boot DMA Protection" and "Kernel DMA Protection"[1]. As such, we can provide interested consumers with an abstracted capability rather than driver-specific interfaces that won't scale. We name it for the former aspect since that's what external callers are most likely to be interested in; the latter is for the IOMMU layer to handle itself.
Quoted 5 lines
Suggested-by Christoph Hellwig <[email protected]>Reviewed-by Christoph Hellwig <[email protected]>Signed-off-by Robin Murphy <[email protected]>
Best regards,
baolu
Quoted 23 lines
--- drivers/iommu/intel/iommu.c | 2 ++ include/linux/iommu.h | 2 ++ 2 files changed, 4 insertions(+)drivers/iommu/intel/iommu.c +2
-0@@ -4550,6 +4550,8 @@ static bool intel_iommu_capable(struct device *dev, enum iommu_cap cap)return domain_update_iommu_snooping (NULL );if (cap ==IOMMU_CAP_INTR_REMAP )return irq_remapping_enabled ==1 ; +if (cap ==IOMMU_CAP_PRE_BOOT_PROTECTION ) +return dmar_platform_optin ();return false; }include/linux/iommu.h +0
-0
include/linux/iommu.h enum iommu_cap { · line 114
Quoted 8 lines
include/linux/iommu.h +2
-0@@ -107,6 +107,8 @@ enum iommu_cap {transactions */ IOMMU_CAP_INTR_REMAP ,/* IOMMU supports interrupt isolation */ IOMMU_CAP_NOEXEC ,/* IOMMU_NOEXEC flag */ + IOMMU_CAP_PRE_BOOT_PROTECTION, /* Firmware says it used the IOMMU for + DMA protection and we should too */ };/* These are the possible reserved region types */
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Robin Murphy
reviewerPATCH v3 3/4 thunderbolt: Make iommu_dma_protection more accurate
In reply to Robin MurphyBetween me trying to get rid of iommu_present() and Mario wanting to support the AMD equivalent of DMAR_PLATFORM_OPT_IN, scrutiny has shown that the iommu_dma_protection attribute is being far too optimistic. Even if an IOMMU might be present for some PCI segment in the system, that doesn't necessarily mean it provides translation for the device(s) we care about. Furthermore, all that DMAR_PLATFORM_OPT_IN really does is tell us that memory was protected before the kernel was loaded, and prevent the user from disabling the intel-iommu driver entirely. While that lets us assume kernel integrity, what matters for actual runtime DMA protection is whether we trust individual devices, based on the "external facing" property that we expect firmware to describe for Thunderbolt ports.
It's proven challenging to determine the appropriate ports accurately given the variety of possible topologies, so while still not getting a perfect answer, by putting enough faith in firmware we can at least get a good bit closer. If we can see that any device near a Thunderbolt NHI has all the requisites for Kernel DMA Protection, chances are that it *is* a relevant port, but moreover that implies that firmware is playing the game overall, so we'll use that to assume that all Thunderbolt ports should be correctly marked and thus will end up fully protected.
---
v3: Check for correct external_facing property, add debug message.
drivers/thunderbolt/domain.c | 12 +++------- drivers/thunderbolt/nhi.c | 44 ++++++++++++++++++++++++++++++++++++ include/linux/thunderbolt.h | 2 ++ 3 files changed, 49 insertions(+), 9 deletions(-)
drivers/thunderbolt/domain.c +3 -9
@@ -7,9 +7,7 @@*/ #include <linux/device.h> -#include <linux/dmar.h> #include <linux/idr.h> -#include <linux/iommu.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/slab.h>
@@ -257,13 +255,9 @@ static ssize_t iommu_dma_protection_show(struct device *dev,struct device_attribute *attr, char *buf) { -/* -* 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. -*/ -return sprintf (buf,"%d\n" , -iommu_present (&pci_bus_type) &&dmar_platform_optin ()); + struct tb *tb = container_of(dev, struct tb, dev); + +return sysfs_emit (buf,"%d\n" , tb->nhi->iommu_dma_protection); }static DEVICE_ATTR_RO (iommu_dma_protection);
drivers/thunderbolt/nhi.c +44 -0
65 lines not on this page · raw message
include/linux/thunderbolt.h +2 -0
@@ -465,6 +465,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc)* @msix_ida: Used to allocate MSI-X vectors for rings * @going_away: The host controller device is about to disappear so when * this flag is set, avoid touching the hardware anymore. +* @iommu_dma_protection: An IOMMU will isolate external-facing ports. * @interrupt_work: Work scheduled to handle ring interrupt when no * MSI-X is used. * @hop_count: Number of rings (end point hops) supported by NHI.
@@ -479,6 +480,7 @@ struct tb_nhi {struct tb_ring **rx_rings;struct ida msix_ida;bool going_away; +bool iommu_dma_protection;struct work_struct interrupt_work;u32 hop_count;unsigned long quirks;
Signature · 6 lines
2.28.0.dirty _______________________________________________ iommu mailing list [email protected] https://lists.linuxfoundation.org/mailman/listinfo/iommu
Quoted 22 lines · Robin Murphy
Between me trying to get rid of
iommu_present()and Mario wanting to support the AMD equivalent ofDMAR_PLATFORM_OPT_IN, scrutiny has shown that theiommu_dma_protectionattribute is being far too optimistic. Even if an IOMMU might be present for some PCI segment in the system, that doesn't necessarily mean it provides translation for the device(s) we care about. Furthermore, all thatDMAR_PLATFORM_OPT_INreally does is tell us that memory was protected before the kernel was loaded, and prevent the user from disabling the intel-iommu driver entirely. While that lets us assume kernel integrity, what matters for actual runtime DMA protection is whether we trust individual devices, based on the "external facing" property that we expect firmware to describe for Thunderbolt ports.It's proven challenging to determine the appropriate ports accurately given the variety of possible topologies, so while still not getting a perfect answer, by putting enough faith in firmware we can at least get a good bit closer. If we can see that any device near a Thunderbolt NHI has all the requisites for Kernel DMA Protection, chances are that it *is* a relevant port, but moreover that implies that firmware is playing the game overall, so we'll use that to assume that all Thunderbolt ports should be correctly marked and thus will end up fully protected.
Quoted 3 lines
CC Mario Limonciello <[email protected]>Reviewed-by Christoph Hellwig <[email protected]>Signed-off-by Robin Murphy <[email protected]>
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Robin Murphy
reviewerPATCH v3 4/4 iommu/amd: Indicate whether DMA remap support is enabled
In reply to Robin MurphyFrom: Mario Limonciello <[email protected]>
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.
---
v4: Added to series from previous posting here:
https://lore.kernel.org/linux-iommu/[email protected]/
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(+)
drivers/iommu/amd/amd_iommu_types.h +4 -0
@@ -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;
drivers/iommu/amd/init.c +3 -0
@@ -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 */
drivers/iommu/amd/iommu.c +2 -0
@@ -2141,6 +2141,8 @@ static bool amd_iommu_capable(struct device *dev, 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 ; }
Signature · 6 lines
2.28.0.dirty _______________________________________________ iommu mailing list [email protected] https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v3 4/4] iommu/amd: Indicate whether DMA remap support is enabled
Looks good:
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu