iommu/arm-smmu-v3: Fix device crash on kdump kernel
Subsystems (3)
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
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().
--- 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
@@ -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
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
PATCH 2/4 iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump
In reply to Nicolin ChenThough 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.
--- 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
@@ -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
PATCH 3/4 iommu/arm-smmu-v3: Retain SMMUEN during kdump device reset
In reply to Nicolin ChenWhen 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.
--- 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
@@ -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
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 AMdrivers/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?
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 AMdrivers/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 andpage
(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
PATCH 4/4 iommu/arm-smmu-v3: Detect ARM_SMMU_OPT_KDUMP in arm_smmu_device_hw_probe()
In reply to Nicolin Chenarm_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.
--- 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
@@ -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
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 AMdrivers/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_KDUMPdrivers/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 tocontinue 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?