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>
This commit is contained in:
parent
a71792dee2
commit
b63b3439b8
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/acpi_iort.h>
|
#include <linux/acpi_iort.h>
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/crash_dump.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/dma-iommu.h>
|
#include <linux/dma-iommu.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -2212,8 +2213,12 @@ static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
|
||||||
reg &= ~clr;
|
reg &= ~clr;
|
||||||
reg |= set;
|
reg |= set;
|
||||||
writel_relaxed(reg | GBPA_UPDATE, gbpa);
|
writel_relaxed(reg | GBPA_UPDATE, gbpa);
|
||||||
return readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
|
ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
|
||||||
1, ARM_SMMU_POLL_TIMEOUT_US);
|
1, ARM_SMMU_POLL_TIMEOUT_US);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
dev_err(smmu->dev, "GBPA not responding to update\n");
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void arm_smmu_free_msis(void *data)
|
static void arm_smmu_free_msis(void *data)
|
||||||
|
@ -2393,8 +2398,15 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
|
||||||
|
|
||||||
/* Clear CR0 and sync (disables SMMU and queue processing) */
|
/* Clear CR0 and sync (disables SMMU and queue processing) */
|
||||||
reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
|
reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
|
||||||
if (reg & CR0_SMMUEN)
|
if (reg & CR0_SMMUEN) {
|
||||||
|
if (is_kdump_kernel()) {
|
||||||
|
arm_smmu_update_gbpa(smmu, GBPA_ABORT, 0);
|
||||||
|
arm_smmu_device_disable(smmu);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
|
dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
|
||||||
|
}
|
||||||
|
|
||||||
ret = arm_smmu_device_disable(smmu);
|
ret = arm_smmu_device_disable(smmu);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -2492,11 +2504,9 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
|
||||||
enables |= CR0_SMMUEN;
|
enables |= CR0_SMMUEN;
|
||||||
} else {
|
} else {
|
||||||
ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
|
ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(smmu->dev, "GBPA not responding to update\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
|
ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
|
||||||
ARM_SMMU_CR0ACK);
|
ARM_SMMU_CR0ACK);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
Loading…
Reference in New Issue