Commit b5e33e44 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'iommu-fixes-v5.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu

Pull iommu fixes from Joerg Roedel:

 - A couple more fixes for the Intel VT-d driver for bugs introduced
   during the recent conversion of this driver to use IOMMU core default
   domains.

 - Fix for common dma-iommu code to make sure MSI mappings happen in the
   correct domain for a device.

 - Fix a corner case in the handling of sg-lists in dma-iommu code that
   might cause dma_length to be truncated.

 - Mark a switch as fall-through in arm-smmu code.

* tag 'iommu-fixes-v5.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu:
  iommu/vt-d: Fix possible use-after-free of private domain
  iommu/vt-d: Detach domain before using a private one
  iommu/dma: Handle SG length overflow better
  iommu/vt-d: Correctly check format of page table in debugfs
  iommu/vt-d: Detach domain when move device out of group
  iommu/arm-smmu: Mark expected switch fall-through
  iommu/dma: Handle MSI mappings separately
parents cab6d5b6 3a18844d
...@@ -1186,7 +1186,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, ...@@ -1186,7 +1186,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
ste_live = true; ste_live = true;
break; break;
case STRTAB_STE_0_CFG_ABORT: case STRTAB_STE_0_CFG_ABORT:
if (disable_bypass) BUG_ON(!disable_bypass);
break; break;
default: default:
BUG(); /* STE corruption */ BUG(); /* STE corruption */
......
...@@ -459,13 +459,11 @@ static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys, ...@@ -459,13 +459,11 @@ static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
{ {
struct iommu_domain *domain = iommu_get_dma_domain(dev); struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie; struct iommu_dma_cookie *cookie = domain->iova_cookie;
size_t iova_off = 0; struct iova_domain *iovad = &cookie->iovad;
size_t iova_off = iova_offset(iovad, phys);
dma_addr_t iova; dma_addr_t iova;
if (cookie->type == IOMMU_DMA_IOVA_COOKIE) { size = iova_align(iovad, size + iova_off);
iova_off = iova_offset(&cookie->iovad, phys);
size = iova_align(&cookie->iovad, size + iova_off);
}
iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev); iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
if (!iova) if (!iova)
...@@ -764,7 +762,7 @@ static int __finalise_sg(struct device *dev, struct scatterlist *sg, int nents, ...@@ -764,7 +762,7 @@ static int __finalise_sg(struct device *dev, struct scatterlist *sg, int nents,
* - and wouldn't make the resulting output segment too long * - and wouldn't make the resulting output segment too long
*/ */
if (cur_len && !s_iova_off && (dma_addr & seg_mask) && if (cur_len && !s_iova_off && (dma_addr & seg_mask) &&
(cur_len + s_length <= max_len)) { (max_len - cur_len >= s_length)) {
/* ...then concatenate it with the previous one */ /* ...then concatenate it with the previous one */
cur_len += s_length; cur_len += s_length;
} else { } else {
...@@ -1147,16 +1145,21 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev, ...@@ -1147,16 +1145,21 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
if (!msi_page) if (!msi_page)
return NULL; return NULL;
iova = __iommu_dma_map(dev, msi_addr, size, prot); iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
if (iova == DMA_MAPPING_ERROR) if (!iova)
goto out_free_page; goto out_free_page;
if (iommu_map(domain, iova, msi_addr, size, prot))
goto out_free_iova;
INIT_LIST_HEAD(&msi_page->list); INIT_LIST_HEAD(&msi_page->list);
msi_page->phys = msi_addr; msi_page->phys = msi_addr;
msi_page->iova = iova; msi_page->iova = iova;
list_add(&msi_page->list, &cookie->msi_page_list); list_add(&msi_page->list, &cookie->msi_page_list);
return msi_page; return msi_page;
out_free_iova:
iommu_dma_free_iova(cookie, iova, size);
out_free_page: out_free_page:
kfree(msi_page); kfree(msi_page);
return NULL; return NULL;
......
...@@ -235,7 +235,7 @@ static void ctx_tbl_walk(struct seq_file *m, struct intel_iommu *iommu, u16 bus) ...@@ -235,7 +235,7 @@ static void ctx_tbl_walk(struct seq_file *m, struct intel_iommu *iommu, u16 bus)
tbl_wlk.ctx_entry = context; tbl_wlk.ctx_entry = context;
m->private = &tbl_wlk; m->private = &tbl_wlk;
if (pasid_supported(iommu) && is_pasid_enabled(context)) { if (dmar_readq(iommu->reg + DMAR_RTADDR_REG) & DMA_RTADDR_SMT) {
pasid_dir_ptr = context->lo & VTD_PAGE_MASK; pasid_dir_ptr = context->lo & VTD_PAGE_MASK;
pasid_dir_size = get_pasid_dir_size(context); pasid_dir_size = get_pasid_dir_size(context);
pasid_dir_walk(m, pasid_dir_ptr, pasid_dir_size); pasid_dir_walk(m, pasid_dir_ptr, pasid_dir_size);
......
...@@ -3449,6 +3449,7 @@ static bool iommu_need_mapping(struct device *dev) ...@@ -3449,6 +3449,7 @@ static bool iommu_need_mapping(struct device *dev)
dmar_domain = to_dmar_domain(domain); dmar_domain = to_dmar_domain(domain);
dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN; dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN;
} }
dmar_remove_one_dev_info(dev);
get_private_domain_for_dev(dev); get_private_domain_for_dev(dev);
} }
...@@ -4790,7 +4791,8 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info) ...@@ -4790,7 +4791,8 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info)
/* free the private domain */ /* free the private domain */
if (domain->flags & DOMAIN_FLAG_LOSE_CHILDREN && if (domain->flags & DOMAIN_FLAG_LOSE_CHILDREN &&
!(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY)) !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) &&
list_empty(&domain->devices))
domain_exit(info->domain); domain_exit(info->domain);
free_devinfo_mem(info); free_devinfo_mem(info);
...@@ -4803,6 +4805,7 @@ static void dmar_remove_one_dev_info(struct device *dev) ...@@ -4803,6 +4805,7 @@ static void dmar_remove_one_dev_info(struct device *dev)
spin_lock_irqsave(&device_domain_lock, flags); spin_lock_irqsave(&device_domain_lock, flags);
info = dev->archdata.iommu; info = dev->archdata.iommu;
if (info)
__dmar_remove_one_dev_info(info); __dmar_remove_one_dev_info(info);
spin_unlock_irqrestore(&device_domain_lock, flags); spin_unlock_irqrestore(&device_domain_lock, flags);
} }
...@@ -5281,6 +5284,7 @@ static int intel_iommu_add_device(struct device *dev) ...@@ -5281,6 +5284,7 @@ static int intel_iommu_add_device(struct device *dev)
if (device_def_domain_type(dev) == IOMMU_DOMAIN_IDENTITY) { if (device_def_domain_type(dev) == IOMMU_DOMAIN_IDENTITY) {
ret = iommu_request_dm_for_dev(dev); ret = iommu_request_dm_for_dev(dev);
if (ret) { if (ret) {
dmar_remove_one_dev_info(dev);
dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN; dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN;
domain_add_dev_info(si_domain, dev); domain_add_dev_info(si_domain, dev);
dev_info(dev, dev_info(dev,
...@@ -5291,6 +5295,7 @@ static int intel_iommu_add_device(struct device *dev) ...@@ -5291,6 +5295,7 @@ static int intel_iommu_add_device(struct device *dev)
if (device_def_domain_type(dev) == IOMMU_DOMAIN_DMA) { if (device_def_domain_type(dev) == IOMMU_DOMAIN_DMA) {
ret = iommu_request_dma_domain_for_dev(dev); ret = iommu_request_dma_domain_for_dev(dev);
if (ret) { if (ret) {
dmar_remove_one_dev_info(dev);
dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN; dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN;
if (!get_private_domain_for_dev(dev)) { if (!get_private_domain_for_dev(dev)) {
dev_warn(dev, dev_warn(dev,
...@@ -5316,6 +5321,8 @@ static void intel_iommu_remove_device(struct device *dev) ...@@ -5316,6 +5321,8 @@ static void intel_iommu_remove_device(struct device *dev)
if (!iommu) if (!iommu)
return; return;
dmar_remove_one_dev_info(dev);
iommu_group_remove_device(dev); iommu_group_remove_device(dev);
iommu_device_unlink(&iommu->iommu, dev); iommu_device_unlink(&iommu->iommu, dev);
......
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