|
43 | 43 | #include <linux/stacktrace.h> |
44 | 44 |
|
45 | 45 | #include <asm/alternative.h> |
| 46 | +#include <asm/arch_timer.h> |
46 | 47 | #include <asm/compat.h> |
47 | 48 | #include <asm/cpufeature.h> |
48 | 49 | #include <asm/cacheflush.h> |
@@ -472,27 +473,52 @@ static void entry_task_switch(struct task_struct *next) |
472 | 473 | } |
473 | 474 |
|
474 | 475 | /* |
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. |
478 | 480 | */ |
479 | | -static void erratum_1418040_thread_switch(struct task_struct *next) |
| 481 | +static void update_cntkctl_el1(struct task_struct *next) |
480 | 482 | { |
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); |
484 | 484 |
|
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))) |
486 | 490 | sysreg_clear_set(cntkctl_el1, ARCH_TIMER_USR_VCT_ACCESS_EN, 0); |
487 | 491 | else |
488 | 492 | sysreg_clear_set(cntkctl_el1, 0, ARCH_TIMER_USR_VCT_ACCESS_EN); |
489 | 493 | } |
490 | 494 |
|
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) |
492 | 506 | { |
| 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 | + |
493 | 516 | preempt_disable(); |
494 | | - erratum_1418040_thread_switch(current); |
| 517 | + update_thread_flag(TIF_TSC_SIGSEGV, tsc_sigsegv); |
| 518 | + update_cntkctl_el1(current); |
495 | 519 | preempt_enable(); |
| 520 | + |
| 521 | + return 0; |
496 | 522 | } |
497 | 523 |
|
498 | 524 | /* |
@@ -528,7 +554,7 @@ struct task_struct *__switch_to(struct task_struct *prev, |
528 | 554 | contextidr_thread_switch(next); |
529 | 555 | entry_task_switch(next); |
530 | 556 | ssbs_thread_switch(next); |
531 | | - erratum_1418040_thread_switch(next); |
| 557 | + cntkctl_thread_switch(prev, next); |
532 | 558 | ptrauth_thread_switch_user(next); |
533 | 559 |
|
534 | 560 | /* |
@@ -645,7 +671,7 @@ void arch_setup_new_exec(void) |
645 | 671 | current->mm->context.flags = mmflags; |
646 | 672 | ptrauth_thread_init_user(); |
647 | 673 | mte_thread_init_user(); |
648 | | - erratum_1418040_new_exec(); |
| 674 | + do_set_tsc_mode(PR_TSC_ENABLE); |
649 | 675 |
|
650 | 676 | if (task_spec_ssb_noexec(current)) { |
651 | 677 | 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, |
754 | 780 | return prot; |
755 | 781 | } |
756 | 782 | #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 | +} |
0 commit comments