* [PATCH RFC v1] arm64: Handle traps from accessing CNTVCT/CNTFRQ for CONFIG_COMPAT
@ 2018-01-11 4:43 Nicolin Chen
2018-01-11 8:51 ` Marc Zyngier
0 siblings, 1 reply; 7+ messages in thread
From: Nicolin Chen @ 2018-01-11 4:43 UTC (permalink / raw)
To: linux-arm-kernel
CONFIG_COMPAT allows ARM64 machine to run 32-bit instructions.
Since the ARCH_TIMER_USR_VCT_ACCESS_EN might be disabled if a
timer workaround is detected, accessing cntvct via mrrc will
also trigger a trap.
So this patch adds support to handle this situation.
Tested with a user program generated by 32-bit compiler:
int main()
{
unsigned long long cval;
asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval));
return 0;
}
Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
---
[ I also added cntfrq here for safety as theoretically it could
trigger the trap as well. However, my another test case (with
mrc insturction) doesn't seem to trigger a trap. So I would
drop it in the next version if someone can confirm it's not
required. Thanks -- Nicolin ]
arch/arm64/include/asm/esr.h | 25 +++++++++++++++++++
arch/arm64/include/asm/ptrace.h | 15 ++++++++++++
arch/arm64/kernel/entry.S | 4 ++--
arch/arm64/kernel/traps.c | 53 +++++++++++++++++++++++++++++++++++++++--
4 files changed, 93 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 014d7d8..55dea62 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -220,6 +220,31 @@
(((e) & ESR_ELx_SYS64_ISS_OP2_MASK) >> \
ESR_ELx_SYS64_ISS_OP2_SHIFT))
+/* ISS fields for MRC and MRS are very similar, so reuse SYS64 macros */
+#define ESR_ELx_CP15_32_ISS_MRC_MASK ESR_ELx_SYS64_ISS_SYS_MASK
+#define ESR_ELx_CP15_32_ISS_MRC_CNTFRQ (ESR_ELx_SYS64_ISS_SYS_VAL(0, 0, 0, 14, 0) | \
+ ESR_ELx_SYS64_ISS_DIR_READ)
+
+/* ISS field definitions for CP15 MRRC/MCRR instructions (same for CP14) */
+#define ESR_ELx_CP15_64_ISS_OPC1_SHIFT 16
+#define ESR_ELx_CP15_64_ISS_OPC1_MASK (UL(0xf) << ESR_ELx_CP15_64_ISS_OPC1_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)
+#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_MRRC_MASK (ESR_ELx_CP15_64_ISS_OPC1_MASK | \
+ ESR_ELx_CP15_64_ISS_CRM_MASK| \
+ ESR_ELx_SYS64_ISS_DIR_MASK)
+
+#define ESR_ELx_CP15_64_ISS_MRRC_VAL(opc1, crm) \
+ (((opc1) << ESR_ELx_CP15_64_ISS_OPC1_SHIFT) | \
+ ((crm) << ESR_ELx_CP15_64_ISS_CRM_SHIFT))
+
+#define ESR_ELx_CP15_64_ISS_MRRC_CNTVCT (ESR_ELx_CP15_64_ISS_MRRC_VAL(1, 14) | \
+ ESR_ELx_SYS64_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 6069d66..50caf11 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -243,6 +243,21 @@ static inline void pt_regs_write_reg(struct pt_regs *regs, int r,
regs->regs[r] = val;
}
+/*
+ * Write two registers given architectural register index r and r2.
+ * Used by 32-bit MRRC and MCRR instrutions for 64-bit results
+ */
+static inline void pt_regs_write_regs(struct pt_regs *regs, int r, int r2,
+ unsigned long val)
+{
+ if (r != 31 && r2 != 31) {
+ /* Save lower 32 bits to register r */
+ regs->regs[r] = val & 0xffffffff;
+ /* Save higher 32 bits to register r2 */
+ regs->regs[r2] = val >> 32;
+ }
+}
+
/* Valid only for Kernel mode traps. */
static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
{
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 6d14b8f..9d6cd95 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -636,9 +636,9 @@ el0_sync_compat:
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_sys
cmp x24, #ESR_ELx_EC_CP15_64 // CP15 MRRC/MCRR trap
- b.eq el0_undef
+ b.eq el0_sys
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
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 3d3588f..211cce7 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -454,6 +454,17 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
}
+#ifdef CONFIG_COMPAT
+static void cntvct_read_32_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;
+
+ pt_regs_write_regs(regs, rt, rt2, arch_counter_get_cntvct());
+ arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
+}
+#endif
+
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;
@@ -495,11 +506,49 @@ static struct sys64_hook sys64_hooks[] = {
{},
};
+#ifdef CONFIG_COMPAT
+static struct sys64_hook cp15_32_hooks[] = {
+ {
+ /* Trap read access to CNTFRQ via 32-bit insturction MRC */
+ .esr_mask = ESR_ELx_CP15_32_ISS_MRC_MASK,
+ .esr_val = ESR_ELx_CP15_32_ISS_MRC_CNTFRQ,
+ .handler = cntfrq_read_handler,
+ },
+ {},
+};
+
+static struct sys64_hook cp15_64_hooks[] = {
+ {
+ /* Trap read access to CNTVCT via 32-bit insturction MRRC */
+ .esr_mask = ESR_ELx_CP15_64_ISS_MRRC_MASK,
+ .esr_val = ESR_ELx_CP15_64_ISS_MRRC_CNTVCT,
+ .handler = cntvct_read_32_handler,
+ },
+ {},
+};
+#endif
+
asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs)
{
- struct sys64_hook *hook;
+ struct sys64_hook *hook = NULL;
+
+ switch (ESR_ELx_EC(esr)) {
+#ifdef CONFIG_COMPAT
+ case ESR_ELx_EC_CP15_32:
+ hook = cp15_32_hooks;
+ break;
+ case ESR_ELx_EC_CP15_64:
+ hook = cp15_64_hooks;
+ break;
+#endif
+ case ESR_ELx_EC_SYS64:
+ hook = sys64_hooks;
+ break;
+ default:
+ break;
+ }
- for (hook = sys64_hooks; hook->handler; hook++)
+ for (; hook && hook->handler; hook++)
if ((hook->esr_mask & esr) == hook->esr_val) {
hook->handler(esr, regs);
return;
--
2.1.4
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH RFC v1] arm64: Handle traps from accessing CNTVCT/CNTFRQ for CONFIG_COMPAT
2018-01-11 4:43 [PATCH RFC v1] arm64: Handle traps from accessing CNTVCT/CNTFRQ for CONFIG_COMPAT Nicolin Chen
@ 2018-01-11 8:51 ` Marc Zyngier
[not found] ` <20180116203218.GA6318@Asurada-Nvidia>
0 siblings, 1 reply; 7+ messages in thread
From: Marc Zyngier @ 2018-01-11 8:51 UTC (permalink / raw)
To: linux-arm-kernel
On 11/01/18 04:43, Nicolin Chen wrote:
> CONFIG_COMPAT allows ARM64 machine to run 32-bit instructions.
> Since the ARCH_TIMER_USR_VCT_ACCESS_EN might be disabled if a
> timer workaround is detected, accessing cntvct via mrrc will
> also trigger a trap.
>
> So this patch adds support to handle this situation.
>
> Tested with a user program generated by 32-bit compiler:
> int main()
> {
> unsigned long long cval;
> asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval));
> return 0;
> }
>
> Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
> ---
>
> [ I also added cntfrq here for safety as theoretically it could
> trigger the trap as well. However, my another test case (with
> mrc insturction) doesn't seem to trigger a trap. So I would
> drop it in the next version if someone can confirm it's not
> required. Thanks -- Nicolin ]
See my previous series on this very subject[1] as well as Will's reply.
>
> arch/arm64/include/asm/esr.h | 25 +++++++++++++++++++
> arch/arm64/include/asm/ptrace.h | 15 ++++++++++++
> arch/arm64/kernel/entry.S | 4 ++--
> arch/arm64/kernel/traps.c | 53 +++++++++++++++++++++++++++++++++++++++--
> 4 files changed, 93 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
> index 014d7d8..55dea62 100644
> --- a/arch/arm64/include/asm/esr.h
> +++ b/arch/arm64/include/asm/esr.h
> @@ -220,6 +220,31 @@
> (((e) & ESR_ELx_SYS64_ISS_OP2_MASK) >> \
> ESR_ELx_SYS64_ISS_OP2_SHIFT))
>
> +/* ISS fields for MRC and MRS are very similar, so reuse SYS64 macros */
> +#define ESR_ELx_CP15_32_ISS_MRC_MASK ESR_ELx_SYS64_ISS_SYS_MASK
> +#define ESR_ELx_CP15_32_ISS_MRC_CNTFRQ (ESR_ELx_SYS64_ISS_SYS_VAL(0, 0, 0, 14, 0) | \
> + ESR_ELx_SYS64_ISS_DIR_READ)
> +
> +/* ISS field definitions for CP15 MRRC/MCRR instructions (same for CP14) */
> +#define ESR_ELx_CP15_64_ISS_OPC1_SHIFT 16
> +#define ESR_ELx_CP15_64_ISS_OPC1_MASK (UL(0xf) << ESR_ELx_CP15_64_ISS_OPC1_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)
> +#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_MRRC_MASK (ESR_ELx_CP15_64_ISS_OPC1_MASK | \
> + ESR_ELx_CP15_64_ISS_CRM_MASK| \
> + ESR_ELx_SYS64_ISS_DIR_MASK)
> +
> +#define ESR_ELx_CP15_64_ISS_MRRC_VAL(opc1, crm) \
> + (((opc1) << ESR_ELx_CP15_64_ISS_OPC1_SHIFT) | \
> + ((crm) << ESR_ELx_CP15_64_ISS_CRM_SHIFT))
> +
> +#define ESR_ELx_CP15_64_ISS_MRRC_CNTVCT (ESR_ELx_CP15_64_ISS_MRRC_VAL(1, 14) | \
> + ESR_ELx_SYS64_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 6069d66..50caf11 100644
> --- a/arch/arm64/include/asm/ptrace.h
> +++ b/arch/arm64/include/asm/ptrace.h
> @@ -243,6 +243,21 @@ static inline void pt_regs_write_reg(struct pt_regs *regs, int r,
> regs->regs[r] = val;
> }
>
> +/*
> + * Write two registers given architectural register index r and r2.
> + * Used by 32-bit MRRC and MCRR instrutions for 64-bit results
> + */
> +static inline void pt_regs_write_regs(struct pt_regs *regs, int r, int r2,
> + unsigned long val)
> +{
> + if (r != 31 && r2 != 31) {
> + /* Save lower 32 bits to register r */
> + regs->regs[r] = val & 0xffffffff;
> + /* Save higher 32 bits to register r2 */
> + regs->regs[r2] = val >> 32;
> + }
> +}
> +
> /* Valid only for Kernel mode traps. */
> static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
> {
> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
> index 6d14b8f..9d6cd95 100644
> --- a/arch/arm64/kernel/entry.S
> +++ b/arch/arm64/kernel/entry.S
> @@ -636,9 +636,9 @@ el0_sync_compat:
> 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_sys
> cmp x24, #ESR_ELx_EC_CP15_64 // CP15 MRRC/MCRR trap
> - b.eq el0_undef
> + b.eq el0_sys
> 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
> diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
> index 3d3588f..211cce7 100644
> --- a/arch/arm64/kernel/traps.c
> +++ b/arch/arm64/kernel/traps.c
> @@ -454,6 +454,17 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
> arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
> }
>
> +#ifdef CONFIG_COMPAT
> +static void cntvct_read_32_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;
> +
> + pt_regs_write_regs(regs, rt, rt2, arch_counter_get_cntvct());
> + arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
> +}
> +#endif
> +
> 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;
> @@ -495,11 +506,49 @@ static struct sys64_hook sys64_hooks[] = {
> {},
> };
>
> +#ifdef CONFIG_COMPAT
> +static struct sys64_hook cp15_32_hooks[] = {
> + {
> + /* Trap read access to CNTFRQ via 32-bit insturction MRC */
> + .esr_mask = ESR_ELx_CP15_32_ISS_MRC_MASK,
> + .esr_val = ESR_ELx_CP15_32_ISS_MRC_CNTFRQ,
> + .handler = cntfrq_read_handler,
> + },
> + {},
> +};
> +
> +static struct sys64_hook cp15_64_hooks[] = {
> + {
> + /* Trap read access to CNTVCT via 32-bit insturction MRRC */
> + .esr_mask = ESR_ELx_CP15_64_ISS_MRRC_MASK,
> + .esr_val = ESR_ELx_CP15_64_ISS_MRRC_CNTVCT,
> + .handler = cntvct_read_32_handler,
> + },
> + {},
> +};
> +#endif
> +
> asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs)
> {
> - struct sys64_hook *hook;
> + struct sys64_hook *hook = NULL;
> +
> + switch (ESR_ELx_EC(esr)) {
> +#ifdef CONFIG_COMPAT
> + case ESR_ELx_EC_CP15_32:
> + hook = cp15_32_hooks;
> + break;
> + case ESR_ELx_EC_CP15_64:
> + hook = cp15_64_hooks;
> + break;
> +#endif
> + case ESR_ELx_EC_SYS64:
> + hook = sys64_hooks;
> + break;
> + default:
> + break;
> + }
>
> - for (hook = sys64_hooks; hook->handler; hook++)
> + for (; hook && hook->handler; hook++)
> if ((hook->esr_mask & esr) == hook->esr_val) {
> hook->handler(esr, regs);
> return;
>
Also, this code is fairly broken in its handling of conditional
instructions.
Thanks,
M.
[1]
http://lists.infradead.org/pipermail/linux-arm-kernel/2017-July/520426.html
--
Jazz is not dead. It just smells funny...
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2018-01-17 23:39 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-01-11 4:43 [PATCH RFC v1] arm64: Handle traps from accessing CNTVCT/CNTFRQ for CONFIG_COMPAT Nicolin Chen
2018-01-11 8:51 ` Marc Zyngier
[not found] ` <20180116203218.GA6318@Asurada-Nvidia>
[not found] ` <86r2qpec32.wl-marc.zyngier@arm.com>
[not found] ` <20180116213745.GA9545@Asurada-Nvidia>
2018-01-17 2:13 ` Nicolin Chen
2018-01-17 9:03 ` Marc Zyngier
2018-01-17 20:41 ` Nicolin Chen
2018-01-17 23:35 ` Robin Murphy
2018-01-17 23:39 ` Nicolin Chen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).