Skip to content

Commit 3e9e67e

Browse files
pccwilldeacon
authored andcommitted
arm64: Implement prctl(PR_{G,S}ET_TSC)
On arm64, this prctl controls access to CNTVCT_EL0, CNTVCTSS_EL0 and CNTFRQ_EL0 via CNTKCTL_EL1.EL0VCTEN. Since this bit is also used to implement various erratum workarounds, check whether the CPU needs a workaround whenever we potentially need to change it. This is needed for a correct implementation of non-instrumenting record-replay debugging on arm64 (i.e. rr; https://rr-project.org/). rr must trap and record any sources of non-determinism from the userspace program's perspective so it can be replayed later. This includes the results of syscalls as well as the results of access to architected timers exposed directly to the program. This prctl was originally added for x86 by commit 8fb402b ("generic, x86: add prctl commands PR_GET_TSC and PR_SET_TSC"), and rr uses it to trap RDTSC on x86 for the same reason. We also considered exposing this as a PTRACE_EVENT. However, prctl seems like a better choice for these reasons: 1) In general an in-process control seems more useful than an out-of-process control, since anything that you would be able to do with ptrace could also be done with prctl (tracer can inject a call to the prctl and handle signal-delivery-stops), and it avoids needing an additional process (which will complicate debugging of the ptraced process since it cannot have more than one tracer, and will be incompatible with ptrace_scope=3) in cases where that is not otherwise necessary. 2) Consistency with x86_64. Note that on x86_64, RDTSC has been there since the start, so it's the same situation as on arm64. Signed-off-by: Peter Collingbourne <[email protected]> Link: https://linux-review.googlesource.com/id/I233a1867d1ccebe2933a347552e7eae862344421 Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent 7c626ce commit 3e9e67e

File tree

4 files changed

+82
-18
lines changed

4 files changed

+82
-18
lines changed

arch/arm64/include/asm/processor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,5 +402,10 @@ long get_tagged_addr_ctrl(struct task_struct *task);
402402
#define GET_TAGGED_ADDR_CTRL() get_tagged_addr_ctrl(current)
403403
#endif
404404

405+
int get_tsc_mode(unsigned long adr);
406+
int set_tsc_mode(unsigned int val);
407+
#define GET_TSC_CTL(adr) get_tsc_mode((adr))
408+
#define SET_TSC_CTL(val) set_tsc_mode((val))
409+
405410
#endif /* __ASSEMBLY__ */
406411
#endif /* __ASM_PROCESSOR_H */

arch/arm64/include/asm/thread_info.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ void arch_setup_new_exec(void);
8181
#define TIF_SME 27 /* SME in use */
8282
#define TIF_SME_VL_INHERIT 28 /* Inherit SME vl_onexec across exec */
8383
#define TIF_KERNEL_FPSTATE 29 /* Task is in a kernel mode FPSIMD section */
84+
#define TIF_TSC_SIGSEGV 30 /* SIGSEGV on counter-timer access */
8485

8586
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
8687
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
@@ -97,6 +98,7 @@ void arch_setup_new_exec(void);
9798
#define _TIF_SVE (1 << TIF_SVE)
9899
#define _TIF_MTE_ASYNC_FAULT (1 << TIF_MTE_ASYNC_FAULT)
99100
#define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL)
101+
#define _TIF_TSC_SIGSEGV (1 << TIF_TSC_SIGSEGV)
100102

101103
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
102104
_TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \

arch/arm64/kernel/process.c

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <linux/stacktrace.h>
4444

4545
#include <asm/alternative.h>
46+
#include <asm/arch_timer.h>
4647
#include <asm/compat.h>
4748
#include <asm/cpufeature.h>
4849
#include <asm/cacheflush.h>
@@ -472,27 +473,52 @@ static void entry_task_switch(struct task_struct *next)
472473
}
473474

474475
/*
475-
* ARM erratum 1418040 handling, affecting the 32bit view of CNTVCT.
476-
* Ensure access is disabled when switching to a 32bit task, ensure
477-
* access is enabled when switching to a 64bit task.
476+
* Handle sysreg updates for ARM erratum 1418040 which affects the 32bit view of
477+
* CNTVCT, various other errata which require trapping all CNTVCT{,_EL0}
478+
* accesses and prctl(PR_SET_TSC). Ensure access is disabled iff a workaround is
479+
* required or PR_TSC_SIGSEGV is set.
478480
*/
479-
static void erratum_1418040_thread_switch(struct task_struct *next)
481+
static void update_cntkctl_el1(struct task_struct *next)
480482
{
481-
if (!IS_ENABLED(CONFIG_ARM64_ERRATUM_1418040) ||
482-
!this_cpu_has_cap(ARM64_WORKAROUND_1418040))
483-
return;
483+
struct thread_info *ti = task_thread_info(next);
484484

485-
if (is_compat_thread(task_thread_info(next)))
485+
if (test_ti_thread_flag(ti, TIF_TSC_SIGSEGV) ||
486+
has_erratum_handler(read_cntvct_el0) ||
487+
(IS_ENABLED(CONFIG_ARM64_ERRATUM_1418040) &&
488+
this_cpu_has_cap(ARM64_WORKAROUND_1418040) &&
489+
is_compat_thread(ti)))
486490
sysreg_clear_set(cntkctl_el1, ARCH_TIMER_USR_VCT_ACCESS_EN, 0);
487491
else
488492
sysreg_clear_set(cntkctl_el1, 0, ARCH_TIMER_USR_VCT_ACCESS_EN);
489493
}
490494

491-
static void erratum_1418040_new_exec(void)
495+
static void cntkctl_thread_switch(struct task_struct *prev,
496+
struct task_struct *next)
497+
{
498+
if ((read_ti_thread_flags(task_thread_info(prev)) &
499+
(_TIF_32BIT | _TIF_TSC_SIGSEGV)) !=
500+
(read_ti_thread_flags(task_thread_info(next)) &
501+
(_TIF_32BIT | _TIF_TSC_SIGSEGV)))
502+
update_cntkctl_el1(next);
503+
}
504+
505+
static int do_set_tsc_mode(unsigned int val)
492506
{
507+
bool tsc_sigsegv;
508+
509+
if (val == PR_TSC_SIGSEGV)
510+
tsc_sigsegv = true;
511+
else if (val == PR_TSC_ENABLE)
512+
tsc_sigsegv = false;
513+
else
514+
return -EINVAL;
515+
493516
preempt_disable();
494-
erratum_1418040_thread_switch(current);
517+
update_thread_flag(TIF_TSC_SIGSEGV, tsc_sigsegv);
518+
update_cntkctl_el1(current);
495519
preempt_enable();
520+
521+
return 0;
496522
}
497523

498524
/*
@@ -528,7 +554,7 @@ struct task_struct *__switch_to(struct task_struct *prev,
528554
contextidr_thread_switch(next);
529555
entry_task_switch(next);
530556
ssbs_thread_switch(next);
531-
erratum_1418040_thread_switch(next);
557+
cntkctl_thread_switch(prev, next);
532558
ptrauth_thread_switch_user(next);
533559

534560
/*
@@ -645,7 +671,7 @@ void arch_setup_new_exec(void)
645671
current->mm->context.flags = mmflags;
646672
ptrauth_thread_init_user();
647673
mte_thread_init_user();
648-
erratum_1418040_new_exec();
674+
do_set_tsc_mode(PR_TSC_ENABLE);
649675

650676
if (task_spec_ssb_noexec(current)) {
651677
arch_prctl_spec_ctrl_set(current, PR_SPEC_STORE_BYPASS,
@@ -754,3 +780,26 @@ int arch_elf_adjust_prot(int prot, const struct arch_elf_state *state,
754780
return prot;
755781
}
756782
#endif
783+
784+
int get_tsc_mode(unsigned long adr)
785+
{
786+
unsigned int val;
787+
788+
if (is_compat_task())
789+
return -EINVAL;
790+
791+
if (test_thread_flag(TIF_TSC_SIGSEGV))
792+
val = PR_TSC_SIGSEGV;
793+
else
794+
val = PR_TSC_ENABLE;
795+
796+
return put_user(val, (unsigned int __user *)adr);
797+
}
798+
799+
int set_tsc_mode(unsigned int val)
800+
{
801+
if (is_compat_task())
802+
return -EINVAL;
803+
804+
return do_set_tsc_mode(val);
805+
}

arch/arm64/kernel/traps.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -601,18 +601,26 @@ static void ctr_read_handler(unsigned long esr, struct pt_regs *regs)
601601

602602
static void cntvct_read_handler(unsigned long esr, struct pt_regs *regs)
603603
{
604-
int rt = ESR_ELx_SYS64_ISS_RT(esr);
604+
if (test_thread_flag(TIF_TSC_SIGSEGV)) {
605+
force_sig(SIGSEGV);
606+
} else {
607+
int rt = ESR_ELx_SYS64_ISS_RT(esr);
605608

606-
pt_regs_write_reg(regs, rt, arch_timer_read_counter());
607-
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
609+
pt_regs_write_reg(regs, rt, arch_timer_read_counter());
610+
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
611+
}
608612
}
609613

610614
static void cntfrq_read_handler(unsigned long esr, struct pt_regs *regs)
611615
{
612-
int rt = ESR_ELx_SYS64_ISS_RT(esr);
616+
if (test_thread_flag(TIF_TSC_SIGSEGV)) {
617+
force_sig(SIGSEGV);
618+
} else {
619+
int rt = ESR_ELx_SYS64_ISS_RT(esr);
613620

614-
pt_regs_write_reg(regs, rt, arch_timer_get_rate());
615-
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
621+
pt_regs_write_reg(regs, rt, arch_timer_get_rate());
622+
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
623+
}
616624
}
617625

618626
static void mrs_handler(unsigned long esr, struct pt_regs *regs)

0 commit comments

Comments
 (0)