aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2018-07-25 10:58:43 -0400
committerWill Deacon <will.deacon@arm.com>2018-07-27 06:12:37 -0400
commitb63b3439b85609338e4faabd5d2588dbda137e5c (patch)
treed11cbaeb0c03b49fd93640667747f5be5587e418 /drivers
parenta71792dee2a33d2e935d4b67dd63924f5ceb203d (diff)
iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel
If we find that the SMMU is enabled during probe, we reset it by re-initialising its registers and either enabling translation or placing it into bypass based on the disable_bypass commandline option. In the case of a kdump kernel, the SMMU won't have been shutdown cleanly by the previous kernel and there may be concurrent DMA through the SMMU. Rather than reset the SMMU to bypass, which would likely lead to rampant data corruption, we can instead configure the SMMU to abort all incoming transactions when we find that it is enabled from within a kdump kernel. Reported-by: Sameer Goel <sgoel@codeaurora.org> Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/iommu/arm-smmu-v3.c22
1 files changed, 16 insertions, 6 deletions
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 7fb5230cd145..446703eeee7a 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -24,6 +24,7 @@
24#include <linux/acpi_iort.h> 24#include <linux/acpi_iort.h>
25#include <linux/bitfield.h> 25#include <linux/bitfield.h>
26#include <linux/bitops.h> 26#include <linux/bitops.h>
27#include <linux/crash_dump.h>
27#include <linux/delay.h> 28#include <linux/delay.h>
28#include <linux/dma-iommu.h> 29#include <linux/dma-iommu.h>
29#include <linux/err.h> 30#include <linux/err.h>
@@ -2212,8 +2213,12 @@ static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
2212 reg &= ~clr; 2213 reg &= ~clr;
2213 reg |= set; 2214 reg |= set;
2214 writel_relaxed(reg | GBPA_UPDATE, gbpa); 2215 writel_relaxed(reg | GBPA_UPDATE, gbpa);
2215 return readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE), 2216 ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
2216 1, ARM_SMMU_POLL_TIMEOUT_US); 2217 1, ARM_SMMU_POLL_TIMEOUT_US);
2218
2219 if (ret)
2220 dev_err(smmu->dev, "GBPA not responding to update\n");
2221 return ret;
2217} 2222}
2218 2223
2219static void arm_smmu_free_msis(void *data) 2224static void arm_smmu_free_msis(void *data)
@@ -2393,8 +2398,15 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
2393 2398
2394 /* Clear CR0 and sync (disables SMMU and queue processing) */ 2399 /* Clear CR0 and sync (disables SMMU and queue processing) */
2395 reg = readl_relaxed(smmu->base + ARM_SMMU_CR0); 2400 reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
2396 if (reg & CR0_SMMUEN) 2401 if (reg & CR0_SMMUEN) {
2402 if (is_kdump_kernel()) {
2403 arm_smmu_update_gbpa(smmu, GBPA_ABORT, 0);
2404 arm_smmu_device_disable(smmu);
2405 return -EBUSY;
2406 }
2407
2397 dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n"); 2408 dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
2409 }
2398 2410
2399 ret = arm_smmu_device_disable(smmu); 2411 ret = arm_smmu_device_disable(smmu);
2400 if (ret) 2412 if (ret)
@@ -2492,10 +2504,8 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
2492 enables |= CR0_SMMUEN; 2504 enables |= CR0_SMMUEN;
2493 } else { 2505 } else {
2494 ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT); 2506 ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
2495 if (ret) { 2507 if (ret)
2496 dev_err(smmu->dev, "GBPA not responding to update\n");
2497 return ret; 2508 return ret;
2498 }
2499 } 2509 }
2500 ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 2510 ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
2501 ARM_SMMU_CR0ACK); 2511 ARM_SMMU_CR0ACK);