Commit a7391ad3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'iomm-fixes-v5.18-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu

Pull iommu fixes from Joerg Roedel:
 "IOMMU core:

   - Fix for a regression which could cause NULL-ptr dereferences

  Arm SMMU:

   - Fix off-by-one in SMMUv3 SVA TLB invalidation

   - Disable large mappings to workaround nvidia erratum

  Intel VT-d:

   - Handle PCI stop marker messages in IOMMU driver to meet the
     requirement of I/O page fault handling framework.

   - Calculate a feasible mask for non-aligned page-selective IOTLB
     invalidation.

  Apple DART IOMMU:

   - Fix potential NULL-ptr dereference

   - Set module owner"

* tag 'iomm-fixes-v5.18-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu:
  iommu: Make sysfs robust for non-API groups
  iommu/dart: Add missing module owner to ops structure
  iommu/dart: check return value after calling platform_get_resource()
  iommu/vt-d: Drop stop marker messages
  iommu/vt-d: Calculate mask for non-aligned flushes
  iommu: arm-smmu: disable large page mappings for Nvidia arm-smmu
  iommu/arm-smmu-v3: Fix size calculation in arm_smmu_mm_invalidate_range()
parents 3118d7ab 392bf519
...@@ -773,6 +773,7 @@ static const struct iommu_ops apple_dart_iommu_ops = { ...@@ -773,6 +773,7 @@ static const struct iommu_ops apple_dart_iommu_ops = {
.get_resv_regions = apple_dart_get_resv_regions, .get_resv_regions = apple_dart_get_resv_regions,
.put_resv_regions = generic_iommu_put_resv_regions, .put_resv_regions = generic_iommu_put_resv_regions,
.pgsize_bitmap = -1UL, /* Restricted during dart probe */ .pgsize_bitmap = -1UL, /* Restricted during dart probe */
.owner = THIS_MODULE,
.default_domain_ops = &(const struct iommu_domain_ops) { .default_domain_ops = &(const struct iommu_domain_ops) {
.attach_dev = apple_dart_attach_dev, .attach_dev = apple_dart_attach_dev,
.detach_dev = apple_dart_detach_dev, .detach_dev = apple_dart_detach_dev,
...@@ -859,16 +860,15 @@ static int apple_dart_probe(struct platform_device *pdev) ...@@ -859,16 +860,15 @@ static int apple_dart_probe(struct platform_device *pdev)
dart->dev = dev; dart->dev = dev;
spin_lock_init(&dart->lock); spin_lock_init(&dart->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dart->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(dart->regs))
return PTR_ERR(dart->regs);
if (resource_size(res) < 0x4000) { if (resource_size(res) < 0x4000) {
dev_err(dev, "MMIO region too small (%pr)\n", res); dev_err(dev, "MMIO region too small (%pr)\n", res);
return -EINVAL; return -EINVAL;
} }
dart->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(dart->regs))
return PTR_ERR(dart->regs);
dart->irq = platform_get_irq(pdev, 0); dart->irq = platform_get_irq(pdev, 0);
if (dart->irq < 0) if (dart->irq < 0)
return -ENODEV; return -ENODEV;
......
...@@ -183,7 +183,14 @@ static void arm_smmu_mm_invalidate_range(struct mmu_notifier *mn, ...@@ -183,7 +183,14 @@ static void arm_smmu_mm_invalidate_range(struct mmu_notifier *mn,
{ {
struct arm_smmu_mmu_notifier *smmu_mn = mn_to_smmu(mn); struct arm_smmu_mmu_notifier *smmu_mn = mn_to_smmu(mn);
struct arm_smmu_domain *smmu_domain = smmu_mn->domain; struct arm_smmu_domain *smmu_domain = smmu_mn->domain;
size_t size = end - start + 1; size_t size;
/*
* The mm_types defines vm_end as the first byte after the end address,
* different from IOMMU subsystem using the last address of an address
* range. So do a simple translation here by calculating size correctly.
*/
size = end - start;
if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_BTM)) if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_BTM))
arm_smmu_tlb_inv_range_asid(start, size, smmu_mn->cd->asid, arm_smmu_tlb_inv_range_asid(start, size, smmu_mn->cd->asid,
......
...@@ -258,6 +258,34 @@ static void nvidia_smmu_probe_finalize(struct arm_smmu_device *smmu, struct devi ...@@ -258,6 +258,34 @@ static void nvidia_smmu_probe_finalize(struct arm_smmu_device *smmu, struct devi
dev_name(dev), err); dev_name(dev), err);
} }
static int nvidia_smmu_init_context(struct arm_smmu_domain *smmu_domain,
struct io_pgtable_cfg *pgtbl_cfg,
struct device *dev)
{
struct arm_smmu_device *smmu = smmu_domain->smmu;
const struct device_node *np = smmu->dev->of_node;
/*
* Tegra194 and Tegra234 SoCs have the erratum that causes walk cache
* entries to not be invalidated correctly. The problem is that the walk
* cache index generated for IOVA is not same across translation and
* invalidation requests. This is leading to page faults when PMD entry
* is released during unmap and populated with new PTE table during
* subsequent map request. Disabling large page mappings avoids the
* release of PMD entry and avoid translations seeing stale PMD entry in
* walk cache.
* Fix this by limiting the page mappings to PAGE_SIZE on Tegra194 and
* Tegra234.
*/
if (of_device_is_compatible(np, "nvidia,tegra234-smmu") ||
of_device_is_compatible(np, "nvidia,tegra194-smmu")) {
smmu->pgsize_bitmap = PAGE_SIZE;
pgtbl_cfg->pgsize_bitmap = smmu->pgsize_bitmap;
}
return 0;
}
static const struct arm_smmu_impl nvidia_smmu_impl = { static const struct arm_smmu_impl nvidia_smmu_impl = {
.read_reg = nvidia_smmu_read_reg, .read_reg = nvidia_smmu_read_reg,
.write_reg = nvidia_smmu_write_reg, .write_reg = nvidia_smmu_write_reg,
...@@ -268,10 +296,12 @@ static const struct arm_smmu_impl nvidia_smmu_impl = { ...@@ -268,10 +296,12 @@ static const struct arm_smmu_impl nvidia_smmu_impl = {
.global_fault = nvidia_smmu_global_fault, .global_fault = nvidia_smmu_global_fault,
.context_fault = nvidia_smmu_context_fault, .context_fault = nvidia_smmu_context_fault,
.probe_finalize = nvidia_smmu_probe_finalize, .probe_finalize = nvidia_smmu_probe_finalize,
.init_context = nvidia_smmu_init_context,
}; };
static const struct arm_smmu_impl nvidia_smmu_single_impl = { static const struct arm_smmu_impl nvidia_smmu_single_impl = {
.probe_finalize = nvidia_smmu_probe_finalize, .probe_finalize = nvidia_smmu_probe_finalize,
.init_context = nvidia_smmu_init_context,
}; };
struct arm_smmu_device *nvidia_smmu_impl_init(struct arm_smmu_device *smmu) struct arm_smmu_device *nvidia_smmu_impl_init(struct arm_smmu_device *smmu)
......
...@@ -1588,7 +1588,8 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, ...@@ -1588,7 +1588,8 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,
unsigned long pfn, unsigned int pages, unsigned long pfn, unsigned int pages,
int ih, int map) int ih, int map)
{ {
unsigned int mask = ilog2(__roundup_pow_of_two(pages)); unsigned int aligned_pages = __roundup_pow_of_two(pages);
unsigned int mask = ilog2(aligned_pages);
uint64_t addr = (uint64_t)pfn << VTD_PAGE_SHIFT; uint64_t addr = (uint64_t)pfn << VTD_PAGE_SHIFT;
u16 did = domain->iommu_did[iommu->seq_id]; u16 did = domain->iommu_did[iommu->seq_id];
...@@ -1600,10 +1601,30 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, ...@@ -1600,10 +1601,30 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,
if (domain_use_first_level(domain)) { if (domain_use_first_level(domain)) {
qi_flush_piotlb(iommu, did, PASID_RID2PASID, addr, pages, ih); qi_flush_piotlb(iommu, did, PASID_RID2PASID, addr, pages, ih);
} else { } else {
unsigned long bitmask = aligned_pages - 1;
/*
* PSI masks the low order bits of the base address. If the
* address isn't aligned to the mask, then compute a mask value
* needed to ensure the target range is flushed.
*/
if (unlikely(bitmask & pfn)) {
unsigned long end_pfn = pfn + pages - 1, shared_bits;
/*
* Since end_pfn <= pfn + bitmask, the only way bits
* higher than bitmask can differ in pfn and end_pfn is
* by carrying. This means after masking out bitmask,
* high bits starting with the first set bit in
* shared_bits are all equal in both pfn and end_pfn.
*/
shared_bits = ~(pfn ^ end_pfn) & ~bitmask;
mask = shared_bits ? __ffs(shared_bits) : BITS_PER_LONG;
}
/* /*
* Fallback to domain selective flush if no PSI support or * Fallback to domain selective flush if no PSI support or
* the size is too big. PSI requires page size to be 2 ^ x, * the size is too big.
* and the base address is naturally aligned to the size.
*/ */
if (!cap_pgsel_inv(iommu->cap) || if (!cap_pgsel_inv(iommu->cap) ||
mask > cap_max_amask_val(iommu->cap)) mask > cap_max_amask_val(iommu->cap))
......
...@@ -757,6 +757,10 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -757,6 +757,10 @@ static irqreturn_t prq_event_thread(int irq, void *d)
goto bad_req; goto bad_req;
} }
/* Drop Stop Marker message. No need for a response. */
if (unlikely(req->lpig && !req->rd_req && !req->wr_req))
goto prq_advance;
if (!svm || svm->pasid != req->pasid) { if (!svm || svm->pasid != req->pasid) {
/* /*
* It can't go away, because the driver is not permitted * It can't go away, because the driver is not permitted
......
...@@ -506,6 +506,13 @@ int iommu_get_group_resv_regions(struct iommu_group *group, ...@@ -506,6 +506,13 @@ int iommu_get_group_resv_regions(struct iommu_group *group,
list_for_each_entry(device, &group->devices, list) { list_for_each_entry(device, &group->devices, list) {
struct list_head dev_resv_regions; struct list_head dev_resv_regions;
/*
* Non-API groups still expose reserved_regions in sysfs,
* so filter out calls that get here that way.
*/
if (!device->dev->iommu)
break;
INIT_LIST_HEAD(&dev_resv_regions); INIT_LIST_HEAD(&dev_resv_regions);
iommu_get_resv_regions(device->dev, &dev_resv_regions); iommu_get_resv_regions(device->dev, &dev_resv_regions);
ret = iommu_insert_device_resv_regions(&dev_resv_regions, head); ret = iommu_insert_device_resv_regions(&dev_resv_regions, head);
...@@ -3019,7 +3026,7 @@ static ssize_t iommu_group_store_type(struct iommu_group *group, ...@@ -3019,7 +3026,7 @@ static ssize_t iommu_group_store_type(struct iommu_group *group,
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
return -EACCES; return -EACCES;
if (WARN_ON(!group)) if (WARN_ON(!group) || !group->default_domain)
return -EINVAL; return -EINVAL;
if (sysfs_streq(buf, "identity")) if (sysfs_streq(buf, "identity"))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment