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/7145d2668b75
branches:  trunk
changeset: 946364:7145d2668b75
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 cff6f3b560c3 -r 7145d2668b75 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 cff6f3b560c3 -r 7145d2668b75 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 cff6f3b560c3 -r 7145d2668b75 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