aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2017-10-27 04:34:22 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2017-11-02 11:55:44 -0400
commit0962289b1cd91534f7111e763d3e6a17dcd47ecb (patch)
tree4632cbba7bdd33596de6e94c77f2ed0997834e24
parentd4d7b4ad2f05c03fb25252aea66f9f3cd7cfbe06 (diff)
irqchip/gic: Deal with broken firmware exposing only 4kB of GICv2 CPU interface
There is a lot of broken firmware out there that don't really expose the information the kernel requires when it comes with dealing with GICv2: (1) Firmware that only describes the first 4kB of GICv2 (2) Firmware that describe 128kB of CPU interface, while the usable portion of the address space is between 60 and 68kB So far, we only deal with (2). But we have platforms exhibiting behaviour (1), resulting in two sub-cases: (a) The GIC is occupying 8kB, as required by the GICv2 architecture (b) It is actually spread 128kB, and this is likely to be a version of (2) This patch tries to work around both (a) and (b) by poking at the outside of the described memory region, and try to work out what is actually there. This is of course unsafe, and should only be enabled if there is no way to otherwise fix the DT provided by the firmware (we provide a "irqchip.gicv2_force_probe" option to that effect). Note that for the time being, we restrict ourselves to GICv2 implementations provided by ARM, since there I have no knowledge of an alternative implementations. This could be relaxed if such an implementation comes to light on a broken platform. Reviewed-by: Christoffer Dall <cdall@linaro.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r--Documentation/admin-guide/kernel-parameters.txt7
-rw-r--r--drivers/irqchip/irq-gic.c71
2 files changed, 69 insertions, 9 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 05496622b4ef..3daa0a590236 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1713,6 +1713,13 @@
1713 irqaffinity= [SMP] Set the default irq affinity mask 1713 irqaffinity= [SMP] Set the default irq affinity mask
1714 The argument is a cpu list, as described above. 1714 The argument is a cpu list, as described above.
1715 1715
1716 irqchip.gicv2_force_probe=
1717 [ARM, ARM64]
1718 Format: <bool>
1719 Force the kernel to look for the second 4kB page
1720 of a GICv2 controller even if the memory range
1721 exposed by the device tree is too small.
1722
1716 irqfixup [HW] 1723 irqfixup [HW]
1717 When an interrupt is not handled search all handlers 1724 When an interrupt is not handled search all handlers
1718 for it. Intended to get systems with badly broken 1725 for it. Intended to get systems with badly broken
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 651d726e8b12..f641e8e2c78d 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -1256,6 +1256,19 @@ static void gic_teardown(struct gic_chip_data *gic)
1256 1256
1257#ifdef CONFIG_OF 1257#ifdef CONFIG_OF
1258static int gic_cnt __initdata; 1258static int gic_cnt __initdata;
1259static bool gicv2_force_probe;
1260
1261static int __init gicv2_force_probe_cfg(char *buf)
1262{
1263 return strtobool(buf, &gicv2_force_probe);
1264}
1265early_param("irqchip.gicv2_force_probe", gicv2_force_probe_cfg);
1266
1267static bool gic_check_gicv2(void __iomem *base)
1268{
1269 u32 val = readl_relaxed(base + GIC_CPU_IDENT);
1270 return (val & 0xff0fff) == 0x02043B;
1271}
1259 1272
1260static bool gic_check_eoimode(struct device_node *node, void __iomem **base) 1273static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
1261{ 1274{
@@ -1265,20 +1278,60 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
1265 1278
1266 if (!is_hyp_mode_available()) 1279 if (!is_hyp_mode_available())
1267 return false; 1280 return false;
1268 if (resource_size(&cpuif_res) < SZ_8K) 1281 if (resource_size(&cpuif_res) < SZ_8K) {
1269 return false; 1282 void __iomem *alt;
1270 if (resource_size(&cpuif_res) == SZ_128K) { 1283 /*
1271 u32 val_low, val_high; 1284 * Check for a stupid firmware that only exposes the
1285 * first page of a GICv2.
1286 */
1287 if (!gic_check_gicv2(*base))
1288 return false;
1272 1289
1290 if (!gicv2_force_probe) {
1291 pr_warn("GIC: GICv2 detected, but range too small and irqchip.gicv2_force_probe not set\n");
1292 return false;
1293 }
1294
1295 alt = ioremap(cpuif_res.start, SZ_8K);
1296 if (!alt)
1297 return false;
1298 if (!gic_check_gicv2(alt + SZ_4K)) {
1299 /*
1300 * The first page was that of a GICv2, and
1301 * the second was *something*. Let's trust it
1302 * to be a GICv2, and update the mapping.
1303 */
1304 pr_warn("GIC: GICv2 at %pa, but range is too small (broken DT?), assuming 8kB\n",
1305 &cpuif_res.start);
1306 iounmap(*base);
1307 *base = alt;
1308 return true;
1309 }
1310
1311 /*
1312 * We detected *two* initial GICv2 pages in a
1313 * row. Could be a GICv2 aliased over two 64kB
1314 * pages. Update the resource, map the iospace, and
1315 * pray.
1316 */
1317 iounmap(alt);
1318 alt = ioremap(cpuif_res.start, SZ_128K);
1319 if (!alt)
1320 return false;
1321 pr_warn("GIC: Aliased GICv2 at %pa, trying to find the canonical range over 128kB\n",
1322 &cpuif_res.start);
1323 cpuif_res.end = cpuif_res.start + SZ_128K -1;
1324 iounmap(*base);
1325 *base = alt;
1326 }
1327 if (resource_size(&cpuif_res) == SZ_128K) {
1273 /* 1328 /*
1274 * Verify that we have the first 4kB of a GIC400 1329 * Verify that we have the first 4kB of a GICv2
1275 * aliased over the first 64kB by checking the 1330 * aliased over the first 64kB by checking the
1276 * GICC_IIDR register on both ends. 1331 * GICC_IIDR register on both ends.
1277 */ 1332 */
1278 val_low = readl_relaxed(*base + GIC_CPU_IDENT); 1333 if (!gic_check_gicv2(*base) ||
1279 val_high = readl_relaxed(*base + GIC_CPU_IDENT + 0xf000); 1334 !gic_check_gicv2(*base + 0xf000))
1280 if ((val_low & 0xffff0fff) != 0x0202043B ||
1281 val_low != val_high)
1282 return false; 1335 return false;
1283 1336
1284 /* 1337 /*