Skip to contentSkip to conversation

iommu/arm-smmu-v3: Fix device crash on kdump kernel

PATCH Superseded Nicolin Chen 2 participants 9 messages Apr 9 – Apr 15

Subsystems (3)

v1 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

cover iommu/arm-smmu-v3: Fix device crash on kdump kernel

When transitioning to a kdump kernel, the primary kernel might have crashed while endpoint devices were actively bus-mastering DMA. Currently, the SMMU driver aggressively resets the hardware during probe by clearing CR0_SMMUEN and setting the Global Bypass Attribute (GBPA) to ABORT.

In a kdump scenario, this aggressive reset is highly destructive:
a) If GBPA is set to ABORT, in-flight DMA will be aborted, generating fatal PCIe AER or SErrors that may panic the kdump kernel
b) If GBPA is set to BYPASS, in-flight DMA targeting some IOVAs will bypass the SMMU and corrupt the physical memory at those 1:1 mapped IOVAs.

To safely absorb in-flight DMA, the kdump kernel must leave SMMUEN=1 intact and avoid modifying STRTAB_BASE. This allows HW to continue translating in- flight DMA using the crashed kernel's page tables until the endpoint device drivers probe and quiesce their respective hardware.

However, the ARM SMMUv3 architecture specification states that updating the SMMU_STRTAB_BASE register while SMMUEN == 1 is UNPREDICTABLE or ignored.

This leaves a kdump kernel no choice but to adopt the stream table from the crashed kernel.

In this series:
- Introduce an ARM_SMMU_OPT_KDUMP
- Skip SMMUEN and STRTAB_BASE resets in arm_smmu_device_reset()
- Map the crashed kernel's stream tables into the kdump kernel [*]
- Defer any default domain attachment to retain STEs until device drivers explicitly request it.

[*] This is implemented via memremap, which only works on a coherent SMMU.

Note that the entire series requires Jason's work that was merged in v6.12:
85196f5 ("iommu/arm-smmu-v3: Reorganize struct arm_smmu_strtab_cfg"). I have a backported version that is verified with a v6.8 kernel. I can send if we see a strong need after this version is accepted.

This is on Github:
https://github.com/nicolinc/iommufd/commits/smmuv3_kdump-v1

Nicolin Chen (4):
iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump iommu/arm-smmu-v3: Retain SMMUEN during kdump device reset iommu/arm-smmu-v3: Detect ARM_SMMU_OPT_KDUMP in
arm_smmu_device_hw_probe()

 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |   1 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 176 +++++++++++++++++++-
 2 files changed, 174 insertions(+), 3 deletions(-)
Signature · 1 line
2.43.0
#1 lore raw

PATCH 1/4 iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump

When transitioning to a kdump kernel, the primary kernel might have crashed while endpoint devices were actively bus-mastering DMA. Currently, the SMMU driver aggressively resets the hardware during probe by clearing CR0_SMMUEN and setting the Global Bypass Attribute (GBPA) to ABORT.

In a kdump scenario, this aggressive reset is highly destructive:
a) If GBPA is set to ABORT, in-flight DMA will be aborted, generating fatal PCIe AER or SErrors that may panic the kdump kernel
b) If GBPA is set to BYPASS, in-flight DMA targeting some IOVAs will bypass the SMMU and corrupt the physical memory at those 1:1 mapped IOVAs.

To safely absorb in-flight DMA, the kdump kernel must leave SMMUEN=1 intact and avoid modifying STRTAB_BASE. This allows HW to continue translating in- flight DMA using the crashed kernel's page tables until the endpoint device drivers probe and quiesce their respective hardware.

However, the ARM SMMUv3 architecture specification states that updating the SMMU_STRTAB_BASE register while SMMUEN == 1 is UNPREDICTABLE or ignored.

This leaves a kdump kernel no choice but to adopt the stream table from the crashed kernel.

Introduce ARM_SMMU_OPT_KDUMP and arm_smmu_adopt_strtab() that does memremap on all the stream tables extracted from STRTAB_BASE and STRTAB_BASE_CFG.

The option will be set in arm_smmu_device_hw_probe().

Fixes b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
Cc [email protected] # v6.12+
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  1 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 99 ++++++++++++++++++++-
 2 files changed, 99 insertions(+), 1 deletion(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1 -0
index ef42df4753ec4..74950d98ba09f 100644
@@ -861,6 +861,7 @@ struct arm_smmu_device {
 #define ARM_SMMU_OPT_MSIPOLL		(1 << 2)
 #define ARM_SMMU_OPT_CMDQ_FORCE_SYNC	(1 << 3)
 #define ARM_SMMU_OPT_TEGRA241_CMDQV	(1 << 4)
+#define ARM_SMMU_OPT_KDUMP		(1 << 5)
 	u32				options;
 
 	struct arm_smmu_cmdq		cmdq;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +98 -1

109 lines not on this page · raw message

Signature · 1 line
2.43.0
#2 lore raw

Re: [PATCH rc v1 1/4] iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) · line 4643

Quoted 8 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8 -0
+	if (fmt == STRTAB_BASE_CFG_FMT_2LVL) {
+		/* Enforce 2-level feature flag to match the adopted table */
+		smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;
+		ret = arm_smmu_adopt_strtab_2lvl(smmu, cfg_reg, dma);
+	} else if (fmt == STRTAB_BASE_CFG_FMT_LINEAR) {
+		/* Force linear feature flag to match the adopted table */
+		smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
+		ret = arm_smmu_adopt_strtab_linear(smmu, cfg_reg, dma);

Made a small fix here. Including it in v2.

@@ -4662,11 +4662,18 @@ static int arm_smmu_adopt_strtab(struct arm_smmu_device *smmu)

        dev_info(smmu->dev, "kdump: adopting crashed kernel's stream table\n");

        if (fmt == STRTAB_BASE_CFG_FMT_2LVL) {
-               /* Enforce 2-level feature flag to match the adopted table */
-               smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;
+               /*
+                * Both kernels run on the same hardware, so it's impossible for
+                * kdump kernel to see the support for linear stream table only.
+                */
+               if (WARN_ON(!(smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)))
+                       return -EINVAL;
                ret = arm_smmu_adopt_strtab_2lvl(smmu, cfg_reg, dma);
        } else if (fmt == STRTAB_BASE_CFG_FMT_LINEAR) {
-               /* Force linear feature flag to match the adopted table */
+               /*
+                * In case that the old kernel for some reason used the linear
+                * format, enforce the same format to match the adopted table.
+                */
                smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
                ret = arm_smmu_adopt_strtab_linear(smmu, cfg_reg, dma);
        } else {

Nicolin

#3 lore raw

PATCH 2/4 iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump

In reply to Nicolin Chen

Though the kdump kernel adopts the crashed kernel's stream table, the iommu core will still try to attach each probed device to a default domain, which overwrites the adopted STE and breaks in-flight DMA from that device.

Implement an is_attach_deferred() callback to prevent this. For each device that has STE.V=1 in the adopted table, defer the default domain attachment, until the device driver explicitly requests it.

Fixes b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
Cc [email protected] # v6.12+
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 28 +++++++++++++++++++++
 1 file changed, 28 insertions(+)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +28 -0
index 8a1de3a67f78c..ff3c1beb3739e 100644
@@ -4212,6 +4212,33 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
 	kfree(master->build_invs);
 }
 
+static bool arm_smmu_is_attach_deferred(struct device *dev)
+{
+	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+	struct arm_smmu_device *smmu = master->smmu;
+	int i;
+
+	if (!(smmu->options & ARM_SMMU_OPT_KDUMP))
+		return false;
+
+	for (i = 0; i < master->num_streams; i++) {
+		u32 sid = master->streams[i].id;
+		struct arm_smmu_ste *step;
+
+		/* Guard against unpopulated L2 entries in the adopted table */
+		if ((smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) &&
+		    !smmu->strtab_cfg.l2.l2ptrs[arm_smmu_strtab_l1_idx(sid)])
+			continue;
+
+		step = arm_smmu_get_step_for_sid(smmu, sid);
+		/* If the STE has the Valid bit set, defer the attach */
+		if (le64_to_cpu(step->data[0]) & STRTAB_STE_0_V)
+			return true;
+	}
+
+	return false;
+}
+
 static struct iommu_device *arm_smmu_probe_device(struct device *dev)
 {
 	int ret;
@@ -4374,6 +4401,7 @@ static const struct iommu_ops arm_smmu_ops = {
 	.hw_info		= arm_smmu_hw_info,
 	.domain_alloc_sva       = arm_smmu_sva_domain_alloc,
 	.domain_alloc_paging_flags = arm_smmu_domain_alloc_paging_flags,
+	.is_attach_deferred	= arm_smmu_is_attach_deferred,
 	.probe_device		= arm_smmu_probe_device,
 	.release_device		= arm_smmu_release_device,
 	.device_group		= arm_smmu_device_group,
Signature · 1 line
2.43.0
#4 lore raw

PATCH 3/4 iommu/arm-smmu-v3: Retain SMMUEN during kdump device reset

In reply to Nicolin Chen

When ARM_SMMU_OPT_KDUMP is set, skip the GBPA/disable/CR1/CR2/STRTAB_BASE update sequence in arm_smmu_device_reset(). Those register writes are all CONSTRAINED UNPREDICTABLE while SMMUEN==1, so leaving them untouched lets in-flight DMA continue to be translated by the adopted stream table.

Initialize 'enables' to 0 so it can carry CR0_SMMUEN in kdump case. Then, preserve that when enabling the command queue.

Also add a comment explaining why EVTQ/PRIQ are disabled in kdump cases.

Fixes b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
Cc [email protected] # v6.12+
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 35 +++++++++++++++++++--
 1 file changed, 33 insertions(+), 2 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +33 -2
index ff3c1beb3739e..d0ef8fb876978 100644
@@ -4931,13 +4931,28 @@ static void arm_smmu_write_strtab(struct arm_smmu_device *smmu)
 static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 {
 	int ret;
-	u32 reg, enables;
+	u32 reg, enables = 0;
 	struct arm_smmu_cmdq_ent cmd;
 
 	/* Clear CR0 and sync (disables SMMU and queue processing) */
 	reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
 	if (reg & CR0_SMMUEN) {
 		dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
+
+		/*
+		 * In a kdump case, retain SMMUEN to avoid transiently aborting
+		 * in-flight DMA. According to spec, updating STRTAB_BASE, CR1,
+		 * or CR2 while SMMUEN==1 is CONSTRAINED UNPREDICTABLE. So skip
+		 * those register updates and rely on the adopted stream table
+		 * from the crashed kernel.
+		 */
+		if (smmu->options & ARM_SMMU_OPT_KDUMP) {
+			dev_info(smmu->dev,
+				 "kdump: retaining SMMUEN for in-flight DMA\n");
+			enables = CR0_SMMUEN;
+			goto reset_queues;
+		}
+
 		arm_smmu_update_gbpa(smmu, GBPA_ABORT, 0);
 	}
 
@@ -4965,12 +4980,23 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 	/* Stream table */
 	arm_smmu_write_strtab(smmu);
 
+reset_queues:
+	if (smmu->options & ARM_SMMU_OPT_KDUMP) {
+		/* Disable queues since arm_smmu_device_disable() was skipped */
+		ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+					      ARM_SMMU_CR0ACK);
+		if (ret) {
+			dev_err(smmu->dev, "failed to disable queues\n");
+			return ret;
+		}
+	}
+
 	/* Command queue */
 	writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE);
 	writel_relaxed(smmu->cmdq.q.llq.prod, smmu->base + ARM_SMMU_CMDQ_PROD);
 	writel_relaxed(smmu->cmdq.q.llq.cons, smmu->base + ARM_SMMU_CMDQ_CONS);
 
-	enables = CR0_CMDQEN;
+	enables |= CR0_CMDQEN;
 	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
 				      ARM_SMMU_CR0ACK);
 	if (ret) {
@@ -5038,6 +5064,11 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 		return ret;
 	}
 
+	/*
+	 * Disable EVTQ and PRIQ in kdump kernel. The old kernel's CDs and page
+	 * tables may be corrupted, which could trigger event spamming. PRIQ is
+	 * also useless since we cannot service page requests during kdump.
+	 */
 	if (is_kdump_kernel())
 		enables &= ~(CR0_EVTQEN | CR0_PRIQEN);
 
Signature · 1 line
2.43.0
#5 lore raw

RE: [PATCH rc v1 3/4] iommu/arm-smmu-v3: Retain SMMUEN during kdump device reset

Quoted 8 lines

From: Nicolin Chen <[email protected]>
Sent: Friday, April 10, 2026 3:47 AM

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0 -0
 	/* Clear CR0 and sync (disables SMMU and queue processing) */
 	reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
 	if (reg & CR0_SMMUEN) {
 		dev_warn(smmu->dev, "SMMU currently enabled!

Resetting...\n");

move to after the check of kdump kernel

Quoted 11 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2 -0
@@ -5038,6 +5064,11 @@ static int arm_smmu_device_reset(struct
 arm_smmu_device *smmu)
 		return ret;
 	}
 
+	/*
+	 * Disable EVTQ and PRIQ in kdump kernel. The old kernel's CDs and

page

(quoted diff) +1 -0
+	 * tables may be corrupted, which could trigger event spamming.

PRIQ is

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_device_reset(struct arm_smmu_device *smmu) · line 5073

Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2 -0
+	 * also useless since we cannot service page requests during kdump.
+	 */
 	if (is_kdump_kernel())
 		enables &= ~(CR0_EVTQEN | CR0_PRIQEN);

then just don't enable them in earlier lines?

#6 lore raw

Re: [PATCH rc v1 3/4] iommu/arm-smmu-v3: Retain SMMUEN during kdump device reset

Quoted 16 lines · Kevin
Quoted 8 lines

From: Nicolin Chen <[email protected]>
Sent: Friday, April 10, 2026 3:47 AM

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0 -0
 	/* Clear CR0 and sync (disables SMMU and queue processing) */
 	reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
 	if (reg & CR0_SMMUEN) {
 		dev_warn(smmu->dev, "SMMU currently enabled!

Resetting...\n");

move to after the check of kdump kernel

Quoted 5 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0 -0
@@ -5038,6 +5064,11 @@ static int arm_smmu_device_reset(struct
 arm_smmu_device *smmu)
 		return ret;
 	}
 
Quoted 12 lines

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_device_reset(struct arm_smmu_device *smmu) · line 5073

Quoted 10 lines
(quoted diff) +2 -0
+	/*
+	 * Disable EVTQ and PRIQ in kdump kernel. The old kernel's CDs and

page

(quoted diff) +1 -0
+	 * tables may be corrupted, which could trigger event spamming.

PRIQ is

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2 -0
+	 * also useless since we cannot service page requests during kdump.
+	 */
 	if (is_kdump_kernel())
 		enables &= ~(CR0_EVTQEN | CR0_PRIQEN);

then just don't enable them in earlier lines?

I will incorporate these (and the other comments) into v2.

Thanks!
Nicolin

#7 lore raw

PATCH 4/4 iommu/arm-smmu-v3: Detect ARM_SMMU_OPT_KDUMP in arm_smmu_device_hw_probe()

In reply to Nicolin Chen

arm_smmu_device_hw_probe() runs before arm_smmu_init_structures(), so it's natural to decide whether the kdump kernel must adopt the crashed kernel's stream table.

Given that memremap is used to adopt the old stream table, set this option only on a coherent SMMU.

Fixes b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
Cc [email protected] # v6.12+
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +14 -0
index d0ef8fb876978..d92b5fa4bac17 100644
@@ -5376,6 +5376,20 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
 
 	dev_info(smmu->dev, "oas %lu-bit (features 0x%08x)\n",
 		 smmu->oas, smmu->features);
+
+	/*
+	 * If SMMU is already active in kdump case, there could be in-flight DMA
+	 * from devices initiated by the crashed kernel. Mark ARM_SMMU_OPT_KDUMP
+	 * to let the init functions adopt the crashed kernel's stream table.
+	 *
+	 * Note that arm_smmu_adopt_strtab() uses memremap that can only work on
+	 * a coherent SMMU. A non-coherent SMMU has no choice but to continue to
+	 * abort any in-flight DMA.
+	 */
+	if (is_kdump_kernel() && coherent &&
+	    (readl_relaxed(smmu->base + ARM_SMMU_CR0) & CR0_SMMUEN))
+		smmu->options |= ARM_SMMU_OPT_KDUMP;
+
 	return 0;
 }
 
Signature · 1 line
2.43.0
#8 lore raw

RE: [PATCH rc v1 4/4] iommu/arm-smmu-v3: Detect ARM_SMMU_OPT_KDUMP in arm_smmu_device_hw_probe()

Quoted 19 lines

From: Nicolin Chen <[email protected]>
Sent: Friday, April 10, 2026 3:47 AM

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +3 -0
+
+	/*
+	 * If SMMU is already active in kdump case, there could be in-flight

DMA

(quoted diff) +1 -0
+	 * from devices initiated by the crashed kernel. Mark

ARM_SMMU_OPT_KDUMP

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +3 -0
+	 * to let the init functions adopt the crashed kernel's stream table.
+	 *
+	 * Note that arm_smmu_adopt_strtab() uses memremap that can

only work on

(quoted diff) +1 -0
+	 * a coherent SMMU. A non-coherent SMMU has no choice but to

continue to

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +4 -0
+	 * abort any in-flight DMA.
+	 */
+	if (is_kdump_kernel() && coherent &&
+	    (readl_relaxed(smmu->base + ARM_SMMU_CR0) &

CR0_SMMUEN))

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu) · line 5393

Quoted 3 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2 -0
+		smmu->options |= ARM_SMMU_OPT_KDUMP;
+
 	return 0;

A warning message for the non-coherent case?