Skip to content

Commit dc87a98

Browse files
rmurphy-armwildea01
authored andcommitted
iommu/arm-smmu: Fall back to global bypass
Unlike SMMUv2, SMMUv3 has no easy way to bypass unknown stream IDs, other than allocating and filling in the entire stream table with bypass entries, which for some configurations would waste *gigabytes* of RAM. Otherwise, all transactions on unknown stream IDs will simply be aborted with a C_BAD_STREAMID event. Rather than render the system unusable in the case of an invalid DT, avoid enabling the SMMU altogether such that everything bypasses (though letting the explicit disable_bypass option take precedence). Signed-off-by: Robin Murphy <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent b9bc881 commit dc87a98

File tree

1 file changed

+44
-4
lines changed

1 file changed

+44
-4
lines changed

drivers/iommu/arm-smmu-v3.c

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@
123123
#define CR2_RECINVSID (1 << 1)
124124
#define CR2_E2H (1 << 0)
125125

126+
#define ARM_SMMU_GBPA 0x44
127+
#define GBPA_ABORT (1 << 20)
128+
#define GBPA_UPDATE (1 << 31)
129+
126130
#define ARM_SMMU_IRQ_CTRL 0x50
127131
#define IRQ_CTRL_EVTQ_IRQEN (1 << 2)
128132
#define IRQ_CTRL_PRIQ_IRQEN (1 << 1)
@@ -2124,6 +2128,24 @@ static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
21242128
1, ARM_SMMU_POLL_TIMEOUT_US);
21252129
}
21262130

2131+
/* GBPA is "special" */
2132+
static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
2133+
{
2134+
int ret;
2135+
u32 reg, __iomem *gbpa = smmu->base + ARM_SMMU_GBPA;
2136+
2137+
ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
2138+
1, ARM_SMMU_POLL_TIMEOUT_US);
2139+
if (ret)
2140+
return ret;
2141+
2142+
reg &= ~clr;
2143+
reg |= set;
2144+
writel_relaxed(reg | GBPA_UPDATE, gbpa);
2145+
return readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
2146+
1, ARM_SMMU_POLL_TIMEOUT_US);
2147+
}
2148+
21272149
static void arm_smmu_free_msis(void *data)
21282150
{
21292151
struct device *dev = data;
@@ -2269,7 +2291,7 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
22692291
return ret;
22702292
}
22712293

2272-
static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
2294+
static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
22732295
{
22742296
int ret;
22752297
u32 reg, enables;
@@ -2370,8 +2392,17 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
23702392
return ret;
23712393
}
23722394

2373-
/* Enable the SMMU interface */
2374-
enables |= CR0_SMMUEN;
2395+
2396+
/* Enable the SMMU interface, or ensure bypass */
2397+
if (!bypass || disable_bypass) {
2398+
enables |= CR0_SMMUEN;
2399+
} else {
2400+
ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
2401+
if (ret) {
2402+
dev_err(smmu->dev, "GBPA not responding to update\n");
2403+
return ret;
2404+
}
2405+
}
23752406
ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
23762407
ARM_SMMU_CR0ACK);
23772408
if (ret) {
@@ -2570,6 +2601,15 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
25702601
struct resource *res;
25712602
struct arm_smmu_device *smmu;
25722603
struct device *dev = &pdev->dev;
2604+
bool bypass = true;
2605+
u32 cells;
2606+
2607+
if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells))
2608+
dev_err(dev, "missing #iommu-cells property\n");
2609+
else if (cells != 1)
2610+
dev_err(dev, "invalid #iommu-cells value (%d)\n", cells);
2611+
else
2612+
bypass = false;
25732613

25742614
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
25752615
if (!smmu) {
@@ -2622,7 +2662,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
26222662
platform_set_drvdata(pdev, smmu);
26232663

26242664
/* Reset the device */
2625-
return arm_smmu_device_reset(smmu);
2665+
return arm_smmu_device_reset(smmu, bypass);
26262666
}
26272667

26282668
static int arm_smmu_device_remove(struct platform_device *pdev)

0 commit comments

Comments
 (0)