arm64: Handle traps from accessing CNTVCT/CNTFRQ via 32-bit instructions
Nicolin Chen [Wed, 10 Jan 2018 02:59:20 +0000 (18:59 -0800)]
CNTVCT and CNTFRQ can be accessed via 32-bit instructions (mrrc/mrc).

So the trap handler should take care of these two situations as well.
Otherwise, it will trigger an "undefined instruction" state and file
a SIGILL back to user space without caring about which application.

Bug 2044346

Change-Id: I39de0a3f332c405042bb181ccdf616eeb96b1608
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1635304
(cherry picked from commit c4f4342ab3dc8c3185820bc55e08feeed0240c0a)
Reviewed-on: https://git-master.nvidia.com/r/1648572
Reviewed-by: Jeetesh Burman <jburman@nvidia.com>
Tested-by: Jeetesh Burman <jburman@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
GVS: Gerrit_Virtual_Submit

arch/arm64/include/asm/esr.h
arch/arm64/kernel/entry.S
arch/arm64/kernel/traps.c

index 3146d0c..4323603 100644 (file)
 #define ESR_ELx_SYS64_ISS_SYS_CNTFRQ   (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 0, 14, 0) | \
                                         ESR_ELx_SYS64_ISS_DIR_READ)
 
+/* ESR_EL1_EC_CP15_64 -- MRRC/MCRR traps ISS field */
+#define ESR_ELx_CP15_64_ISS_OPC_SHIFT  16
+#define ESR_ELx_CP15_64_ISS_OPC_MASK   (UL(0x7) << ESR_ELx_CP15_64_ISS_OPC_SHIFT)
+
+#define ESR_ELx_CP15_64_ISS_OPC_CNTVCT (1 << ESR_ELx_CP15_64_ISS_OPC_SHIFT)
+
+#define ESR_ELx_CP15_64_ISS_RT2_SHIFT  10
+#define ESR_ELx_CP15_64_ISS_RT2_MASK   (UL(0x1f) << ESR_ELx_CP15_64_ISS_RT2_SHIFT)
+#define ESR_ELx_CP15_64_ISS_RT_SHIFT   5
+#define ESR_ELx_CP15_64_ISS_RT_MASK    (UL(0x1f) << ESR_ELx_CP15_64_ISS_RT_SHIFT)
+
+/* ESR_EL1_EC_CP15_32 -- MRC/MCR traps ISS field */
+#define ESR_ELx_CP15_32_ISS_OPC_SHIFT  14
+#define ESR_ELx_CP15_32_ISS_OPC_MASK   (UL(0x3F) << ESR_ELx_CP15_32_ISS_OPC_SHIFT)
+
+#define ESR_ELx_CP15_32_ISS_OPC_CNTFRQ (0 << ESR_ELx_CP15_32_ISS_OPC_SHIFT)
+
 #endif /* __ASM_ESR_H */
index 53bb667..b43e144 100644 (file)
@@ -423,10 +423,10 @@ el0_sync_compat:
        adr     x26, el0_undef
        cmp     x24, #ESR_EL1_EC_UNKNOWN        // unknown exception in EL0
        b.eq    el0_sync_tramp
-       adr     x26, el0_undef
+       adr     x26, el0_sys
        cmp     x24, #ESR_EL1_EC_CP15_32        // CP15 MRC/MCR trap
        b.eq    el0_sync_tramp
-       adr     x26, el0_undef
+       adr     x26, el0_sys
        cmp     x24, #ESR_EL1_EC_CP15_64        // CP15 MRRC/MCRR trap
        b.eq    el0_sync_tramp
        adr     x26, el0_undef
index 02c068f..f5e7fc8 100644 (file)
@@ -338,6 +338,26 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
        regs->pc += 4;
 }
 
+/*
+ * ARM32 instruction uses two RT registers to hold a 64-bit result.
+ */
+static void cntvct_read32_handler(unsigned int esr, struct pt_regs *regs)
+{
+       int rt2 = (esr & ESR_ELx_CP15_64_ISS_RT2_MASK) >> ESR_ELx_CP15_64_ISS_RT2_SHIFT;
+       int rt = (esr & ESR_ELx_CP15_64_ISS_RT_MASK) >> ESR_ELx_CP15_64_ISS_RT_SHIFT;
+       u64 val;
+
+       isb();
+       if (rt != 31) {
+               val = arch_counter_get_cntvct();
+               /* Put lower 32 bits in the Rt */
+               regs->regs[rt] = val & 0xffffffff;
+               /* Put higher 32 bits in the Rt2 */
+               regs->regs[rt2] = val >> 32;
+       }
+       regs->pc += 4;
+}
+
 static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
 {
        int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
@@ -357,6 +377,21 @@ asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs)
                return;
        }
 
+       /*
+        * CNTVCT can be accessed via 32-bit mrrc instruction while CNTFRQ
+        * can be accessed via 32-bit mrc instruction. If VCT_ACCESS bit is
+        * disabled, these two instructions will trigger the trap as well.
+        */
+       if ((esr >> ESR_EL1_EC_SHIFT) == ESR_EL1_EC_CP15_64 &&
+           (esr & ESR_ELx_CP15_64_ISS_OPC_MASK) == ESR_ELx_CP15_64_ISS_OPC_CNTVCT) {
+               cntvct_read32_handler(esr, regs);
+               return;
+       } else if ((esr >> ESR_EL1_EC_SHIFT) == ESR_EL1_EC_CP15_32 &&
+                  (esr & ESR_ELx_CP15_32_ISS_OPC_MASK) == ESR_ELx_CP15_32_ISS_OPC_CNTFRQ) {
+               cntfrq_read_handler(esr, regs);
+               return;
+       }
+
        do_undefinstr(regs);
 }