|
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