[RFC 10/18] iommu/intel: zap context table entries on kexec

From: James Gowans
Date: Mon Feb 05 2024 - 07:07:48 EST


In the next commit the IOMMU shutdown function will be modified to not
actually shut down the IOMMU when doing a kexec. To prevent leaving DMA
mappings for non-persistent devices around during kexec we add a
function to the kexec flow which iterates though all IOMMU domains and
zaps the context entries for the devices belonging to those domain.

A list of domains for the IOMMU is added and maintained.
---
drivers/iommu/intel/dmar.c | 1 +
drivers/iommu/intel/iommu.c | 34 ++++++++++++++++++++++++++++++----
drivers/iommu/intel/iommu.h | 2 ++
3 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index 23cb80d62a9a..00f69f40a4ac 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -1097,6 +1097,7 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
iommu->segment = drhd->segment;

iommu->node = NUMA_NO_NODE;
+ INIT_LIST_HEAD(&iommu->domains);

ver = readl(iommu->reg + DMAR_VER_REG);
pr_info("%s: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 2dd3f055dbce..315c6b7f901c 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -1831,6 +1831,7 @@ static int domain_attach_iommu(struct dmar_domain *domain,
goto err_clear;
}
domain_update_iommu_cap(domain);
+ list_add(&domain->domains, &iommu->domains);

spin_unlock(&iommu->lock);
return 0;
@@ -3608,6 +3609,33 @@ static void intel_disable_iommus(void)
iommu_disable_translation(iommu);
}

+void zap_context_table_entries(struct intel_iommu *iommu)
+{
+ struct context_entry *context;
+ struct dmar_domain *domain;
+ struct device_domain_info *device;
+ int bus, devfn;
+ u16 did_old;
+
+ list_for_each_entry(domain, &iommu->domains, domains) {
+ list_for_each_entry(device, &domain->devices, link) {
+ context = iommu_context_addr(iommu, device->bus, device->devfn, 0);
+ if (!context || !context_present(context))
+ continue;
+ context_domain_id(context);
+ context_clear_entry(context);
+ __iommu_flush_cache(iommu, context, sizeof(*context));
+ iommu->flush.flush_context(iommu,
+ did_old,
+ (((u16)bus) << 8) | devfn,
+ DMA_CCMD_MASK_NOBIT,
+ DMA_CCMD_DEVICE_INVL);
+ iommu->flush.flush_iotlb(iommu, did_old, 0, 0,
+ DMA_TLB_DSI_FLUSH);
+ }
+ }
+}
+
void intel_iommu_shutdown(void)
{
struct dmar_drhd_unit *drhd;
@@ -3620,10 +3648,8 @@ void intel_iommu_shutdown(void)

/* Disable PMRs explicitly here. */
for_each_iommu(iommu, drhd)
- iommu_disable_protect_mem_regions(iommu);
-
- /* Make sure the IOMMUs are switched off */
- intel_disable_iommus();
+ zap_context_table_entries(iommu);
+ return

up_write(&dmar_global_lock);
}
diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
index a2338e398ba3..4a2f163a86f3 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -600,6 +600,7 @@ struct dmar_domain {
spinlock_t lock; /* Protect device tracking lists */
struct list_head devices; /* all devices' list */
struct list_head dev_pasids; /* all attached pasids */
+ struct list_head domains; /* all struct dmar_domains on this IOMMU */

struct dma_pte *pgd; /* virtual address */
int gaw; /* max guest address width */
@@ -700,6 +701,7 @@ struct intel_iommu {
void *perf_statistic;

struct iommu_pmu *pmu;
+ struct list_head domains; /* all struct dmar_domains on this IOMMU */
};

/* PCI domain-device relationship */
--
2.40.1