Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/arch/arm Improve detection of NS vs S views of priorities.
details: https://anonhg.NetBSD.org/src/rev/98d4d3bf09ec
branches: trunk
changeset: 1016596:98d4d3bf09ec
user: jmcneill <jmcneill%NetBSD.org@localhost>
date: Tue Nov 24 23:31:55 2020 +0000
description:
Improve detection of NS vs S views of priorities.
For PMR, write a 0 to bit7 and see if it sticks. This is only possible from
NS EL1 if we have a non-secure view of ICC_PMR_EL1.
For int priorities (GICD/GICR interfaces and LPIs), assume that the
GICD_CTLR.DS bit is telling us the truth.
RK3399 is special here when using the vendor bootloader, so keep the
auto-detection from the previous commit but limit the scope to only run
on RK3399 SOCs.
diffstat:
sys/arch/arm/cortex/gicv3.c | 138 +++++++++++++++++++++++-------------------
sys/arch/arm/cortex/gicv3.h | 5 +-
sys/arch/arm/fdt/gicv3_fdt.c | 23 ++++++-
3 files changed, 99 insertions(+), 67 deletions(-)
diffs (282 lines):
diff -r ff4ca36c6530 -r 98d4d3bf09ec sys/arch/arm/cortex/gicv3.c
--- a/sys/arch/arm/cortex/gicv3.c Tue Nov 24 23:13:09 2020 +0000
+++ b/sys/arch/arm/cortex/gicv3.c Tue Nov 24 23:31:55 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gicv3.c,v 1.34 2020/11/22 20:17:39 jmcneill Exp $ */
+/* $NetBSD: gicv3.c,v 1.35 2020/11/24 23:31:56 jmcneill Exp $ */
/*-
* Copyright (c) 2018 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -31,7 +31,7 @@
#define _INTR_PRIVATE
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gicv3.c,v 1.34 2020/11/22 20:17:39 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gicv3.c,v 1.35 2020/11/24 23:31:56 jmcneill Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
@@ -58,7 +58,9 @@
#define IPL_TO_PRIORITY(sc, ipl) (((0xff - (ipl)) << (sc)->sc_priority_shift) & 0xff)
#define IPL_TO_PMR(sc, ipl) (((0xff - (ipl)) << (sc)->sc_pmr_shift) & 0xff)
-#define IPL_TO_LPIPRIO(sc, ipl) (((0xff - (ipl)) << 4) & 0xff)
+
+#define GIC_PRIO_SHIFT_NS 4
+#define GIC_PRIO_SHIFT_S 3
static struct gicv3_softc *gicv3_softc;
@@ -224,7 +226,9 @@
u_int n;
/* Disable the distributor */
- gicd_write_4(sc, GICD_CTRL, 0);
+ gicd_ctrl = gicd_read_4(sc, GICD_CTRL);
+ gicd_ctrl &= ~(GICD_CTRL_EnableGrp1A | GICD_CTRL_ARE_NS);
+ gicd_write_4(sc, GICD_CTRL, gicd_ctrl);
/* Wait for register write to complete */
while (gicd_read_4(sc, GICD_CTRL) & GICD_CTRL_RWP)
@@ -543,7 +547,7 @@
{
struct gicv3_softc * const sc = LPITOSOFTC(pic);
- sc->sc_lpiconf.base[is->is_irq] = IPL_TO_LPIPRIO(sc, is->is_ipl) | GIC_LPICONF_Res1;
+ sc->sc_lpiconf.base[is->is_irq] = IPL_TO_PRIORITY(sc, is->is_ipl) | GIC_LPICONF_Res1;
if (sc->sc_lpiconf_flush)
cpu_dcache_wb_range((vaddr_t)&sc->sc_lpiconf.base[is->is_irq], 1);
@@ -756,44 +760,67 @@
}
static bool
-gicv3_access_is_secure(struct gicv3_softc *sc)
+gicv3_cpuif_is_nonsecure(struct gicv3_softc *sc)
{
- const uint32_t octlr = gicd_read_4(sc, GICD_CTRL);
- gicd_write_4(sc, GICD_CTRL, octlr ^ GICD_CTRL_EnableGrp1S);
- const uint32_t nctlr = gicd_read_4(sc, GICD_CTRL);
- gicd_write_4(sc, GICD_CTRL, octlr);
-
- return nctlr != octlr;
-}
-
-static uint8_t
-gicv3_get_pmr_bits(struct gicv3_softc *sc)
-{
+ /*
+ * Write 0 to bit7 and see if it sticks. This is only possible if
+ * we have a non-secure view of the PMR register.
+ */
const uint32_t opmr = icc_pmr_read();
- icc_pmr_write(0xff);
+ icc_pmr_write(0);
const uint32_t npmr = icc_pmr_read();
icc_pmr_write(opmr);
- return npmr;
+ return (npmr & GICC_PMR_NONSECURE) == 0;
+}
+
+static bool
+gicv3_dist_is_nonsecure(struct gicv3_softc *sc)
+{
+ const uint32_t gicd_ctrl = gicd_read_4(sc, GICD_CTRL);
+
+ /*
+ * If security is enabled, we have a non-secure view of the IPRIORITYRn
+ * registers and LPI configuration priority fields.
+ */
+ return (gicd_ctrl & GICD_CTRL_DS) == 0;
}
-static uint8_t
-gicv3_get_ipriority_bits(struct gicv3_softc *sc)
+/*
+ * Rockchip RK3399 provides a different view of int priority registers
+ * depending on which firmware is in use. This is hard to detect in
+ * a way that could possibly break other boards, so only do this
+ * detection if we know we are on a RK3399 SoC.
+ */
+static void
+gicv3_quirk_rockchip_rk3399(struct gicv3_softc *sc)
{
- const uint32_t oipriorityr = gicd_read_4(sc, GICD_IPRIORITYRn(8));
- gicd_write_4(sc, GICD_IPRIORITYRn(8), oipriorityr | 0xff);
- const uint32_t nipriorityr = gicd_read_4(sc, GICD_IPRIORITYRn(8));
- gicd_write_4(sc, GICD_IPRIORITYRn(8), oipriorityr);
+ /* Detect the number of supported PMR bits */
+ icc_pmr_write(0xff);
+ const uint8_t pmrbits = icc_pmr_read();
+
+ /* Detect the number of supported IPRIORITYRn bits */
+ const uint32_t oiprio = gicd_read_4(sc, GICD_IPRIORITYRn(8));
+ gicd_write_4(sc, GICD_IPRIORITYRn(8), oiprio | 0xff);
+ const uint8_t pribits = gicd_read_4(sc, GICD_IPRIORITYRn(8)) & 0xff;
+ gicd_write_4(sc, GICD_IPRIORITYRn(8), oiprio);
- return nipriorityr & 0xff;
+ /*
+ * If we see fewer PMR bits than IPRIORITYRn bits here, it means
+ * we have a secure view of IPRIORITYRn (this is not supposed to
+ * happen!).
+ */
+ if (pmrbits < pribits) {
+ aprint_verbose_dev(sc->sc_dev,
+ "buggy RK3399 firmware detected; applying workaround\n");
+ sc->sc_priority_shift = GIC_PRIO_SHIFT_S;
+ }
}
int
gicv3_init(struct gicv3_softc *sc)
{
const uint32_t gicd_typer = gicd_read_4(sc, GICD_TYPER);
- const uint32_t gicd_ctrl = gicd_read_4(sc, GICD_CTRL);
- const bool access_s = gicv3_access_is_secure(sc);
int n;
KASSERT(CPU_IS_PRIMARY(curcpu()));
@@ -803,44 +830,29 @@
for (n = 0; n < MAXCPUS; n++)
sc->sc_irouter[n] = UINT64_MAX;
- sc->sc_priority_shift = 4;
- sc->sc_pmr_shift = 4;
-
- uint8_t pmr_mask = gicv3_get_pmr_bits(sc);
- uint8_t ipriority_mask = gicv3_get_ipriority_bits(sc);
+ /*
+ * We don't alwayst have a consistent view of priorities between the
+ * CPU interface (ICC_PMR_EL1) and the GICD/GICR registers. Detect
+ * if we are making secure or non-secure accesses to each, and adjust
+ * the values that we write to each accordingly.
+ */
+ const bool dist_ns = gicv3_dist_is_nonsecure(sc);
+ sc->sc_priority_shift = dist_ns ? GIC_PRIO_SHIFT_NS : GIC_PRIO_SHIFT_S;
+ const bool cpuif_ns = gicv3_cpuif_is_nonsecure(sc);
+ sc->sc_pmr_shift = cpuif_ns ? GIC_PRIO_SHIFT_NS : GIC_PRIO_SHIFT_S;
- if ((gicd_ctrl & GICD_CTRL_DS) == 0) {
- if (access_s) {
- /*
- * Security is enabled and we appear to have secure access
- * to GIC registers. This is not normal, but has been observed
- * on Rockchip RK3399. To make things worse, with Rockchip
- * TF-A the views of PMR and IPRIORITYRn are different, so we
- * need to compensate for this by shifting writes to
- * IPRIORITYRn right one. Mainline TF-A does not seem to have
- * this problem, so detect it by comparing the number of
- * writable bits in the PMR and IPRIORITYRn registers.
- */
- if (pmr_mask < ipriority_mask) {
- --sc->sc_priority_shift;
- }
- }
- } else {
- /*
- * Security is disabled. QEMU 5.1 reports different views of
- * PMR and IPRIORIRYRn here.
- */
- if (pmr_mask == 0xff && ipriority_mask == 0xff) {
- --sc->sc_priority_shift;
- }
- }
+ if ((sc->sc_quirks & GICV3_QUIRK_RK3399) != 0)
+ gicv3_quirk_rockchip_rk3399(sc);
aprint_verbose_dev(sc->sc_dev,
- "security %sabled%s, priority shift %d (0x%02x), pmr shift %d (0x%02x)\n",
- (gicd_ctrl & GICD_CTRL_DS) != 0 ? "dis" : "en",
- access_s ? ", secure access" : "",
- sc->sc_priority_shift, ipriority_mask,
- sc->sc_pmr_shift, pmr_mask);
+ "iidr 0x%08x, cpuif %ssecure, dist %ssecure, "
+ "priority shift %d, pmr shift %d, quirks %#x\n",
+ gicd_read_4(sc, GICD_IIDR),
+ cpuif_ns ? "non-" : "",
+ dist_ns ? "non-" : "",
+ sc->sc_priority_shift,
+ sc->sc_pmr_shift,
+ sc->sc_quirks);
sc->sc_pic.pic_ops = &gicv3_picops;
sc->sc_pic.pic_maxsources = GICD_TYPER_LINES(gicd_typer);
diff -r ff4ca36c6530 -r 98d4d3bf09ec sys/arch/arm/cortex/gicv3.h
--- a/sys/arch/arm/cortex/gicv3.h Tue Nov 24 23:13:09 2020 +0000
+++ b/sys/arch/arm/cortex/gicv3.h Tue Nov 24 23:31:55 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gicv3.h,v 1.8 2020/02/13 00:42:59 jmcneill Exp $ */
+/* $NetBSD: gicv3.h,v 1.9 2020/11/24 23:31:56 jmcneill Exp $ */
/*-
* Copyright (c) 2018 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -61,6 +61,9 @@
bus_space_handle_t *sc_bsh_r; /* GICR */
u_int sc_bsh_r_count;
+ u_int sc_quirks;
+#define GICV3_QUIRK_RK3399 __BIT(0) /* Rockchip RK3399 GIC-500 */
+
u_int sc_priority_shift;
u_int sc_pmr_shift;
diff -r ff4ca36c6530 -r 98d4d3bf09ec sys/arch/arm/fdt/gicv3_fdt.c
--- a/sys/arch/arm/fdt/gicv3_fdt.c Tue Nov 24 23:13:09 2020 +0000
+++ b/sys/arch/arm/fdt/gicv3_fdt.c Tue Nov 24 23:31:55 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gicv3_fdt.c,v 1.8 2019/07/19 12:14:15 hkenken Exp $ */
+/* $NetBSD: gicv3_fdt.c,v 1.9 2020/11/24 23:31:55 jmcneill Exp $ */
/*-
* Copyright (c) 2015-2018 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -31,7 +31,7 @@
#define _INTR_PRIVATE
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gicv3_fdt.c,v 1.8 2019/07/19 12:14:15 hkenken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gicv3_fdt.c,v 1.9 2020/11/24 23:31:55 jmcneill Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -105,6 +105,15 @@
struct gicv3_fdt_irq *sc_irq[GICV3_MAXIRQ];
};
+struct gicv3_fdt_quirk {
+ const char *compat;
+ u_int quirks;
+};
+
+static const struct gicv3_fdt_quirk gicv3_fdt_quirks[] = {
+ { "rockchip,rk3399", GICV3_QUIRK_RK3399 },
+};
+
CFATTACH_DECL_NEW(gicv3_fdt, sizeof(struct gicv3_fdt_softc),
gicv3_fdt_match, gicv3_fdt_attach, NULL, NULL);
@@ -127,7 +136,7 @@
struct gicv3_fdt_softc * const sc = device_private(self);
struct fdt_attach_args * const faa = aux;
const int phandle = faa->faa_phandle;
- int error;
+ int error, n;
error = fdtbus_register_interrupt_controller(self, phandle,
&gicv3_fdt_funcs);
@@ -152,6 +161,14 @@
aprint_debug_dev(self, "%d redistributors\n", sc->sc_gic.sc_bsh_r_count);
+ /* Apply quirks */
+ for (n = 0; n < __arraycount(gicv3_fdt_quirks); n++) {
+ const char *compat[] = { gicv3_fdt_quirks[n].compat, NULL };
+ if (of_match_compatible(OF_finddevice("/"), compat)) {
+ sc->sc_gic.sc_quirks |= gicv3_fdt_quirks[n].quirks;
+ }
+ }
+
error = gicv3_init(&sc->sc_gic);
if (error) {
aprint_error_dev(self, "failed to initialize GIC: %d\n", error);
Home |
Main Index |
Thread Index |
Old Index