Merge "arm64: Add CNTVCT_EL0 cp15 AArch32 trap handler"
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 85997c0..e52727c 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -181,6 +181,85 @@
#define ESR_ELx_SYS64_ISS_SYS_CNTFRQ (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 0, 14, 0) | \
ESR_ELx_SYS64_ISS_DIR_READ)
+/* ISS field definitions for CP15 AArch32 access traps */
+#define ESR_ELx_CP15_32_ISS_CV_SHIFT 24
+#define ESR_ELx_CP15_32_ISS_CV_MASK \
+ (UL(0x1) << ESR_ELx_CP15_32_ISS_CV_SHIFT)
+#define ESR_ELx_CP15_32_ISS_DIR_MASK 0x1
+#define ESR_ELx_CP15_32_ISS_DIR_READ 0x1
+#define ESR_ELx_CP15_32_ISS_DIR_WRITE 0x0
+
+#define ESR_ELx_CP15_32_ISS_RT_SHIFT 5
+#define ESR_ELx_CP15_32_ISS_RT_MASK \
+ (UL(0x1f) << ESR_ELx_CP15_32_ISS_RT_SHIFT)
+#define ESR_ELx_CP15_32_ISS_CRM_SHIFT 1
+#define ESR_ELx_CP15_32_ISS_CRM_MASK \
+ (UL(0xf) << ESR_ELx_CP15_32_ISS_CRM_SHIFT)
+#define ESR_ELx_CP15_32_ISS_CRN_SHIFT 10
+#define ESR_ELx_CP15_32_ISS_CRN_MASK \
+ (UL(0xf) << ESR_ELx_CP15_32_ISS_CRN_SHIFT)
+#define ESR_ELx_CP15_32_ISS_OP1_SHIFT 14
+#define ESR_ELx_CP15_32_ISS_OP1_MASK \
+ (UL(0x7) << ESR_ELx_CP15_32_ISS_OP1_SHIFT)
+#define ESR_ELx_CP15_32_ISS_OP2_SHIFT 17
+#define ESR_ELx_CP15_32_ISS_OP2_MASK \
+ (UL(0x7) << ESR_ELx_CP15_32_ISS_OP2_SHIFT)
+#define ESR_ELx_CP15_32_ISS_COND_SHIFT 20
+#define ESR_ELx_CP15_32_ISS_COND_MASK \
+ (UL(0xf) << ESR_ELx_CP15_32_ISS_COND_SHIFT)
+#define ESR_ELx_CP15_32_ISS_SYS_MASK (ESR_ELx_CP15_32_ISS_OP1_MASK | \
+ ESR_ELx_CP15_32_ISS_OP2_MASK | \
+ ESR_ELx_CP15_32_ISS_CRN_MASK | \
+ ESR_ELx_CP15_32_ISS_CRM_MASK)
+#define ESR_ELx_CP15_32_ISS_SYS_VAL(op1, op2, crn, crm) \
+ (((op1) << ESR_ELx_CP15_32_ISS_OP1_SHIFT) | \
+ ((op2) << ESR_ELx_CP15_32_ISS_OP2_SHIFT) | \
+ ((crn) << ESR_ELx_CP15_32_ISS_CRN_SHIFT) | \
+ ((crm) << ESR_ELx_CP15_32_ISS_CRM_SHIFT))
+
+#define ESR_ELx_CP15_32_ISS_SYS_OP_MASK (ESR_ELx_CP15_32_ISS_SYS_MASK | \
+ ESR_ELx_CP15_32_ISS_DIR_MASK)
+
+#define ESR_ELx_CP15_32_ISS_SYS_CNTFRQ \
+ (ESR_ELx_CP15_32_ISS_SYS_VAL(0, 0, 14, 0) | \
+ ESR_ELx_CP15_32_ISS_DIR_READ)
+
+/* ISS field definitions for CP15 AArch32 64-bit access traps */
+#define ESR_ELx_CP15_64_ISS_CV_SHIFT 24
+#define ESR_ELx_CP15_64_ISS_CV_MASK \
+ (UL(0x1) << ESR_ELx_CP15_64_ISS_CV_SHIFT)
+#define ESR_ELx_CP15_64_ISS_DIR_MASK 0x1
+#define ESR_ELx_CP15_64_ISS_DIR_READ 0x1
+#define ESR_ELx_CP15_64_ISS_DIR_WRITE 0x0
+
+#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)
+#define ESR_ELx_CP15_64_ISS_CRM_SHIFT 1
+#define ESR_ELx_CP15_64_ISS_CRM_MASK \
+ (UL(0xf) << ESR_ELx_CP15_64_ISS_CRM_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_OP1_SHIFT 16
+#define ESR_ELx_CP15_64_ISS_OP1_MASK \
+ (UL(0xf) << ESR_ELx_CP15_64_ISS_OP1_SHIFT)
+#define ESR_ELx_CP15_64_ISS_COND_SHIFT 20
+#define ESR_ELx_CP15_64_ISS_COND_MASK \
+ (UL(0xf) << ESR_ELx_CP15_64_ISS_COND_SHIFT)
+#define ESR_ELx_CP15_64_ISS_SYS_MASK (ESR_ELx_CP15_64_ISS_OP1_MASK | \
+ ESR_ELx_CP15_64_ISS_CRM_MASK)
+#define ESR_ELx_CP15_64_ISS_SYS_VAL(op1, crm) \
+ (((op1) << ESR_ELx_CP15_64_ISS_OP1_SHIFT) | \
+ ((crm) << ESR_ELx_CP15_64_ISS_CRM_SHIFT))
+
+#define ESR_ELx_CP15_64_ISS_SYS_OP_MASK (ESR_ELx_CP15_64_ISS_SYS_MASK | \
+ ESR_ELx_CP15_64_ISS_DIR_MASK)
+
+#define ESR_ELx_CP15_64_ISS_SYS_CNTVCT \
+ (ESR_ELx_CP15_64_ISS_SYS_VAL(1, 14) | \
+ ESR_ELx_CP15_64_ISS_DIR_READ)
+
#ifndef __ASSEMBLY__
#include <asm/types.h>
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
index 458773a..69f6cae 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -130,8 +130,12 @@
#ifdef CONFIG_COMPAT
#define compat_thumb_mode(regs) \
(((regs)->pstate & COMPAT_PSR_T_BIT))
+#define compat_arm_instr_set(regs) \
+ (((regs)->pstate & (COMPAT_PSR_T_BIT | COMPAT_PSR_J_BIT)) == 0)
#else
#define compat_thumb_mode(regs) (0)
+#define compat_arm_instr_set(regs) (0)
+
#endif
#define user_mode(regs) \
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 3b22fa3..f8ba35d 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -601,9 +601,9 @@
cmp x24, #ESR_ELx_EC_UNKNOWN // unknown exception in EL0
b.eq el0_undef
cmp x24, #ESR_ELx_EC_CP15_32 // CP15 MRC/MCR trap
- b.eq el0_undef
+ b.eq el0_cp15_32_compat
cmp x24, #ESR_ELx_EC_CP15_64 // CP15 MRRC/MCRR trap
- b.eq el0_undef
+ b.eq el0_cp15_64_compat
cmp x24, #ESR_ELx_EC_CP14_MR // CP14 MRC/MCR trap
b.eq el0_undef
cmp x24, #ESR_ELx_EC_CP14_LS // CP14 LDC/STC trap
@@ -622,6 +622,28 @@
mov sc_nr, #__NR_compat_syscalls
b el0_svc_naked
+el0_cp15_32_compat:
+ /*
+ * AArch32 CP15 MRC/MCR trap handling
+ */
+ enable_dbg_and_irq
+ ct_user_exit
+ mov x0, x25
+ mov x1, sp
+ bl do_cp15_32_instr_compat
+ b ret_to_user
+
+el0_cp15_64_compat:
+ /*
+ * AArch32 CP15 MRRC/MCRR trap handling
+ */
+ enable_dbg_and_irq
+ ct_user_exit
+ mov x0, x25
+ mov x1, sp
+ bl do_cp15_64_instr_compat
+ b ret_to_user
+
.align 6
el0_irq_compat:
kernel_entry 0, 32
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index cd53836..fcd2b33 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -606,6 +606,124 @@
force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
}
+#ifdef CONFIG_COMPAT
+static void cntfrq_cp15_32_read_handler(unsigned int esr, struct pt_regs *regs)
+{
+ int rt =
+ (esr & ESR_ELx_CP15_32_ISS_RT_MASK) >> ESR_ELx_CP15_32_ISS_RT_SHIFT;
+ int cv =
+ (esr & ESR_ELx_CP15_32_ISS_CV_MASK) >> ESR_ELx_CP15_32_ISS_CV_SHIFT;
+ int cond =
+ (esr & ESR_ELx_CP15_32_ISS_COND_MASK) >>
+ ESR_ELx_CP15_32_ISS_COND_SHIFT;
+ bool read_reg = 1;
+
+ if (rt == 13 && !compat_arm_instr_set(regs))
+ read_reg = 0;
+
+ if (cv && cond != 0xf &&
+ !(*aarch32_opcode_cond_checks[cond])(regs->pstate & 0xffffffff))
+ read_reg = 0;
+
+ if (read_reg)
+ regs->regs[rt] = read_sysreg(cntfrq_el0);
+ regs->pc += 4;
+}
+
+struct cp15_32_hook {
+ unsigned int esr_mask;
+ unsigned int esr_val;
+ void (*handler)(unsigned int esr, struct pt_regs *regs);
+};
+
+static struct cp15_32_hook cp15_32_hooks[] = {
+ {
+ /* Trap CP15 AArch32 read access to CNTFRQ_EL0 */
+ .esr_mask = ESR_ELx_CP15_32_ISS_SYS_OP_MASK,
+ .esr_val = ESR_ELx_CP15_32_ISS_SYS_CNTFRQ,
+ .handler = cntfrq_cp15_32_read_handler,
+ },
+ {},
+};
+
+asmlinkage void __exception do_cp15_32_instr_compat(unsigned int esr,
+ struct pt_regs *regs)
+{
+ struct cp15_32_hook *hook;
+
+ for (hook = cp15_32_hooks; hook->handler; hook++)
+ if ((hook->esr_mask & esr) == hook->esr_val) {
+ hook->handler(esr, regs);
+ return;
+ }
+
+ force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
+}
+
+static void cntvct_cp15_64_read_handler(unsigned int esr, struct pt_regs *regs)
+{
+ int rt =
+ (esr & ESR_ELx_CP15_64_ISS_RT_MASK) >> ESR_ELx_CP15_64_ISS_RT_SHIFT;
+ int rt2 =
+ (esr & ESR_ELx_CP15_64_ISS_RT2_MASK) >> ESR_ELx_CP15_64_ISS_RT2_SHIFT;
+ int cv =
+ (esr & ESR_ELx_CP15_64_ISS_CV_MASK) >> ESR_ELx_CP15_64_ISS_CV_SHIFT;
+ int cond =
+ (esr & ESR_ELx_CP15_64_ISS_COND_MASK) >>
+ ESR_ELx_CP15_64_ISS_COND_SHIFT;
+ bool read_reg = 1;
+
+ if (rt == 15 || rt2 == 15 || rt == rt2)
+ read_reg = 0;
+
+ if ((rt == 13 || rt2 == 13) && !compat_arm_instr_set(regs))
+ read_reg = 0;
+
+ if (cv && cond != 0xf &&
+ !(*aarch32_opcode_cond_checks[cond])(regs->pstate & 0xffffffff))
+ read_reg = 0;
+
+ if (read_reg) {
+ u64 cval = arch_counter_get_cntvct();
+
+ regs->regs[rt] = cval & 0xffffffff;
+ regs->regs[rt2] = cval >> 32;
+ }
+ regs->pc += 4;
+}
+
+struct cp15_64_hook {
+ unsigned int esr_mask;
+ unsigned int esr_val;
+ void (*handler)(unsigned int esr, struct pt_regs *regs);
+};
+
+static struct cp15_64_hook cp15_64_hooks[] = {
+ {
+ /* Trap CP15 AArch32 read access to CNTVCT_EL0 */
+ .esr_mask = ESR_ELx_CP15_64_ISS_SYS_OP_MASK,
+ .esr_val = ESR_ELx_CP15_64_ISS_SYS_CNTVCT,
+ .handler = cntvct_cp15_64_read_handler,
+ },
+ {},
+};
+
+asmlinkage void __exception do_cp15_64_instr_compat(unsigned int esr,
+ struct pt_regs *regs)
+{
+ struct cp15_64_hook *hook;
+
+ for (hook = cp15_64_hooks; hook->handler; hook++)
+ if ((hook->esr_mask & esr) == hook->esr_val) {
+ hook->handler(esr, regs);
+ return;
+ }
+
+ force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
+}
+
+#endif
+
long compat_arm_syscall(struct pt_regs *regs);
asmlinkage long do_ni_syscall(struct pt_regs *regs)