.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * linux/arch/arm/kernel/traps.c |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 1995-2009 Russell King |
---|
5 | 6 | * Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify |
---|
8 | | - * it under the terms of the GNU General Public License version 2 as |
---|
9 | | - * published by the Free Software Foundation. |
---|
10 | 7 | * |
---|
11 | 8 | * 'traps.c' handles hardware exceptions after we have saved some state in |
---|
12 | 9 | * 'linux/arch/arm/lib/traps.S'. Mostly a debugging aid, but will probably |
---|
.. | .. |
---|
33 | 30 | #include <linux/atomic.h> |
---|
34 | 31 | #include <asm/cacheflush.h> |
---|
35 | 32 | #include <asm/exception.h> |
---|
| 33 | +#include <asm/spectre.h> |
---|
36 | 34 | #include <asm/unistd.h> |
---|
37 | 35 | #include <asm/traps.h> |
---|
38 | 36 | #include <asm/ptrace.h> |
---|
.. | .. |
---|
65 | 63 | |
---|
66 | 64 | static void dump_mem(const char *, const char *, unsigned long, unsigned long); |
---|
67 | 65 | |
---|
68 | | -void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame) |
---|
| 66 | +void dump_backtrace_entry(unsigned long where, unsigned long from, |
---|
| 67 | + unsigned long frame, const char *loglvl) |
---|
69 | 68 | { |
---|
70 | 69 | unsigned long end = frame + 4 + sizeof(struct pt_regs); |
---|
71 | 70 | |
---|
72 | 71 | #ifdef CONFIG_KALLSYMS |
---|
73 | | - printk("[<%08lx>] (%ps) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from); |
---|
| 72 | + printk("%s[<%08lx>] (%ps) from [<%08lx>] (%pS)\n", |
---|
| 73 | + loglvl, where, (void *)where, from, (void *)from); |
---|
74 | 74 | #else |
---|
75 | | - printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); |
---|
| 75 | + printk("%sFunction entered at [<%08lx>] from [<%08lx>]\n", |
---|
| 76 | + loglvl, where, from); |
---|
76 | 77 | #endif |
---|
77 | 78 | |
---|
78 | 79 | if (in_entry_text(from) && end <= ALIGN(frame, THREAD_SIZE)) |
---|
79 | | - dump_mem("", "Exception stack", frame + 4, end); |
---|
| 80 | + dump_mem(loglvl, "Exception stack", frame + 4, end); |
---|
80 | 81 | } |
---|
81 | 82 | |
---|
82 | | -void dump_backtrace_stm(u32 *stack, u32 instruction) |
---|
| 83 | +void dump_backtrace_stm(u32 *stack, u32 instruction, const char *loglvl) |
---|
83 | 84 | { |
---|
84 | 85 | char str[80], *p; |
---|
85 | 86 | unsigned int x; |
---|
.. | .. |
---|
91 | 92 | if (++x == 6) { |
---|
92 | 93 | x = 0; |
---|
93 | 94 | p = str; |
---|
94 | | - printk("%s\n", str); |
---|
| 95 | + printk("%s%s\n", loglvl, str); |
---|
95 | 96 | } |
---|
96 | 97 | } |
---|
97 | 98 | } |
---|
98 | 99 | if (p != str) |
---|
99 | | - printk("%s\n", str); |
---|
| 100 | + printk("%s%s\n", loglvl, str); |
---|
100 | 101 | } |
---|
101 | 102 | |
---|
102 | 103 | #ifndef CONFIG_ARM_UNWIND |
---|
.. | .. |
---|
204 | 205 | } |
---|
205 | 206 | |
---|
206 | 207 | #ifdef CONFIG_ARM_UNWIND |
---|
207 | | -static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) |
---|
| 208 | +static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk, |
---|
| 209 | + const char *loglvl) |
---|
208 | 210 | { |
---|
209 | | - unwind_backtrace(regs, tsk); |
---|
| 211 | + unwind_backtrace(regs, tsk, loglvl); |
---|
210 | 212 | } |
---|
211 | 213 | #else |
---|
212 | | -static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) |
---|
| 214 | +static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk, |
---|
| 215 | + const char *loglvl) |
---|
213 | 216 | { |
---|
214 | 217 | unsigned int fp, mode; |
---|
215 | 218 | int ok = 1; |
---|
216 | 219 | |
---|
217 | | - printk("Backtrace: "); |
---|
| 220 | + printk("%sBacktrace: ", loglvl); |
---|
218 | 221 | |
---|
219 | 222 | if (!tsk) |
---|
220 | 223 | tsk = current; |
---|
.. | .. |
---|
241 | 244 | pr_cont("\n"); |
---|
242 | 245 | |
---|
243 | 246 | if (ok) |
---|
244 | | - c_backtrace(fp, mode); |
---|
| 247 | + c_backtrace(fp, mode, loglvl); |
---|
245 | 248 | } |
---|
246 | 249 | #endif |
---|
247 | 250 | |
---|
248 | | -void show_stack(struct task_struct *tsk, unsigned long *sp) |
---|
| 251 | +void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl) |
---|
249 | 252 | { |
---|
250 | | - dump_backtrace(NULL, tsk); |
---|
| 253 | + dump_backtrace(NULL, tsk, loglvl); |
---|
251 | 254 | barrier(); |
---|
252 | 255 | } |
---|
253 | 256 | |
---|
254 | 257 | #ifdef CONFIG_PREEMPT |
---|
255 | 258 | #define S_PREEMPT " PREEMPT" |
---|
| 259 | +#elif defined(CONFIG_PREEMPT_RT) |
---|
| 260 | +#define S_PREEMPT " PREEMPT_RT" |
---|
256 | 261 | #else |
---|
257 | 262 | #define S_PREEMPT "" |
---|
258 | 263 | #endif |
---|
.. | .. |
---|
289 | 294 | if (!user_mode(regs) || in_interrupt()) { |
---|
290 | 295 | dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp, |
---|
291 | 296 | THREAD_SIZE + (unsigned long)task_stack_page(tsk)); |
---|
292 | | - dump_backtrace(regs, tsk); |
---|
| 297 | + dump_backtrace(regs, tsk, KERN_EMERG); |
---|
293 | 298 | dump_instr(KERN_EMERG, regs); |
---|
294 | 299 | } |
---|
295 | 300 | |
---|
.. | .. |
---|
343 | 348 | if (panic_on_oops) |
---|
344 | 349 | panic("Fatal exception"); |
---|
345 | 350 | if (signr) |
---|
346 | | - do_exit(signr); |
---|
| 351 | + make_task_dead(signr); |
---|
347 | 352 | } |
---|
348 | 353 | |
---|
349 | 354 | /* |
---|
.. | .. |
---|
367 | 372 | } |
---|
368 | 373 | |
---|
369 | 374 | void arm_notify_die(const char *str, struct pt_regs *regs, |
---|
370 | | - struct siginfo *info, unsigned long err, unsigned long trap) |
---|
| 375 | + int signo, int si_code, void __user *addr, |
---|
| 376 | + unsigned long err, unsigned long trap) |
---|
371 | 377 | { |
---|
372 | 378 | if (user_mode(regs)) { |
---|
373 | 379 | current->thread.error_code = err; |
---|
374 | 380 | current->thread.trap_no = trap; |
---|
375 | 381 | |
---|
376 | | - force_sig_info(info->si_signo, info, current); |
---|
| 382 | + force_sig_fault(signo, si_code, addr); |
---|
377 | 383 | } else { |
---|
378 | 384 | die(str, regs, err); |
---|
379 | 385 | } |
---|
.. | .. |
---|
391 | 397 | u32 insn = __opcode_to_mem_arm(BUG_INSTR_VALUE); |
---|
392 | 398 | #endif |
---|
393 | 399 | |
---|
394 | | - if (probe_kernel_address((unsigned *)pc, bkpt)) |
---|
| 400 | + if (get_kernel_nofault(bkpt, (void *)pc)) |
---|
395 | 401 | return 0; |
---|
396 | 402 | |
---|
397 | 403 | return bkpt == insn; |
---|
.. | .. |
---|
440 | 446 | asmlinkage void do_undefinstr(struct pt_regs *regs) |
---|
441 | 447 | { |
---|
442 | 448 | unsigned int instr; |
---|
443 | | - siginfo_t info; |
---|
444 | 449 | void __user *pc; |
---|
445 | 450 | |
---|
446 | | - clear_siginfo(&info); |
---|
447 | 451 | pc = (void __user *)instruction_pointer(regs); |
---|
448 | 452 | |
---|
449 | 453 | if (processor_mode(regs) == SVC_MODE) { |
---|
.. | .. |
---|
487 | 491 | dump_instr(KERN_INFO, regs); |
---|
488 | 492 | } |
---|
489 | 493 | #endif |
---|
490 | | - |
---|
491 | | - info.si_signo = SIGILL; |
---|
492 | | - info.si_errno = 0; |
---|
493 | | - info.si_code = ILL_ILLOPC; |
---|
494 | | - info.si_addr = pc; |
---|
495 | | - |
---|
496 | | - arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6); |
---|
| 494 | + arm_notify_die("Oops - undefined instruction", regs, |
---|
| 495 | + SIGILL, ILL_ILLOPC, pc, 0, 6); |
---|
497 | 496 | } |
---|
498 | 497 | NOKPROBE_SYMBOL(do_undefinstr) |
---|
499 | 498 | |
---|
.. | .. |
---|
541 | 540 | |
---|
542 | 541 | static int bad_syscall(int n, struct pt_regs *regs) |
---|
543 | 542 | { |
---|
544 | | - siginfo_t info; |
---|
545 | | - |
---|
546 | | - clear_siginfo(&info); |
---|
547 | 543 | if ((current->personality & PER_MASK) != PER_LINUX) { |
---|
548 | 544 | send_sig(SIGSEGV, current, 1); |
---|
549 | 545 | return regs->ARM_r0; |
---|
.. | .. |
---|
557 | 553 | } |
---|
558 | 554 | #endif |
---|
559 | 555 | |
---|
560 | | - info.si_signo = SIGILL; |
---|
561 | | - info.si_errno = 0; |
---|
562 | | - info.si_code = ILL_ILLTRP; |
---|
563 | | - info.si_addr = (void __user *)instruction_pointer(regs) - |
---|
564 | | - (thumb_mode(regs) ? 2 : 4); |
---|
565 | | - |
---|
566 | | - arm_notify_die("Oops - bad syscall", regs, &info, n, 0); |
---|
| 556 | + arm_notify_die("Oops - bad syscall", regs, SIGILL, ILL_ILLTRP, |
---|
| 557 | + (void __user *)instruction_pointer(regs) - |
---|
| 558 | + (thumb_mode(regs) ? 2 : 4), |
---|
| 559 | + n, 0); |
---|
567 | 560 | |
---|
568 | 561 | return regs->ARM_r0; |
---|
569 | 562 | } |
---|
.. | .. |
---|
579 | 572 | if (fatal_signal_pending(current)) |
---|
580 | 573 | return 0; |
---|
581 | 574 | |
---|
582 | | - ret = flush_cache_user_range(start, start + chunk); |
---|
| 575 | + ret = flush_icache_user_range(start, start + chunk); |
---|
583 | 576 | if (ret) |
---|
584 | 577 | return ret; |
---|
585 | 578 | |
---|
.. | .. |
---|
596 | 589 | if (end < start || flags) |
---|
597 | 590 | return -EINVAL; |
---|
598 | 591 | |
---|
599 | | - if (!access_ok(VERIFY_READ, start, end - start)) |
---|
| 592 | + if (!access_ok((void __user *)start, end - start)) |
---|
600 | 593 | return -EFAULT; |
---|
601 | 594 | |
---|
602 | 595 | return __do_cache_op(start, end); |
---|
.. | .. |
---|
609 | 602 | #define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE) |
---|
610 | 603 | asmlinkage int arm_syscall(int no, struct pt_regs *regs) |
---|
611 | 604 | { |
---|
612 | | - siginfo_t info; |
---|
613 | | - |
---|
614 | | - clear_siginfo(&info); |
---|
615 | 605 | if ((no >> 16) != (__ARM_NR_BASE>> 16)) |
---|
616 | 606 | return bad_syscall(no, regs); |
---|
617 | 607 | |
---|
618 | 608 | switch (no & 0xffff) { |
---|
619 | 609 | case 0: /* branch through 0 */ |
---|
620 | | - info.si_signo = SIGSEGV; |
---|
621 | | - info.si_errno = 0; |
---|
622 | | - info.si_code = SEGV_MAPERR; |
---|
623 | | - info.si_addr = NULL; |
---|
624 | | - |
---|
625 | | - arm_notify_die("branch through zero", regs, &info, 0, 0); |
---|
| 610 | + arm_notify_die("branch through zero", regs, |
---|
| 611 | + SIGSEGV, SEGV_MAPERR, NULL, 0, 0); |
---|
626 | 612 | return 0; |
---|
627 | 613 | |
---|
628 | 614 | case NR(breakpoint): /* SWI BREAK_POINT */ |
---|
629 | 615 | regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; |
---|
630 | | - ptrace_break(current, regs); |
---|
| 616 | + ptrace_break(regs); |
---|
631 | 617 | return regs->ARM_r0; |
---|
632 | 618 | |
---|
633 | 619 | /* |
---|
.. | .. |
---|
683 | 669 | if (user_debug & UDBG_SYSCALL) { |
---|
684 | 670 | pr_err("[%d] %s: arm syscall %d\n", |
---|
685 | 671 | task_pid_nr(current), current->comm, no); |
---|
686 | | - dump_instr("", regs); |
---|
| 672 | + dump_instr(KERN_ERR, regs); |
---|
687 | 673 | if (user_mode(regs)) { |
---|
688 | 674 | __show_regs(regs); |
---|
689 | | - c_backtrace(frame_pointer(regs), processor_mode(regs)); |
---|
| 675 | + c_backtrace(frame_pointer(regs), processor_mode(regs), KERN_ERR); |
---|
690 | 676 | } |
---|
691 | 677 | } |
---|
692 | 678 | #endif |
---|
693 | | - info.si_signo = SIGILL; |
---|
694 | | - info.si_errno = 0; |
---|
695 | | - info.si_code = ILL_ILLTRP; |
---|
696 | | - info.si_addr = (void __user *)instruction_pointer(regs) - |
---|
697 | | - (thumb_mode(regs) ? 2 : 4); |
---|
698 | | - |
---|
699 | | - arm_notify_die("Oops - bad syscall(2)", regs, &info, no, 0); |
---|
| 679 | + arm_notify_die("Oops - bad syscall(2)", regs, SIGILL, ILL_ILLTRP, |
---|
| 680 | + (void __user *)instruction_pointer(regs) - |
---|
| 681 | + (thumb_mode(regs) ? 2 : 4), |
---|
| 682 | + no, 0); |
---|
700 | 683 | return 0; |
---|
701 | 684 | } |
---|
702 | 685 | |
---|
.. | .. |
---|
746 | 729 | baddataabort(int code, unsigned long instr, struct pt_regs *regs) |
---|
747 | 730 | { |
---|
748 | 731 | unsigned long addr = instruction_pointer(regs); |
---|
749 | | - siginfo_t info; |
---|
750 | | - |
---|
751 | | - clear_siginfo(&info); |
---|
752 | 732 | |
---|
753 | 733 | #ifdef CONFIG_DEBUG_USER |
---|
754 | 734 | if (user_debug & UDBG_BADABORT) { |
---|
| 735 | + pr_err("8<--- cut here ---\n"); |
---|
755 | 736 | pr_err("[%d] %s: bad data abort: code %d instr 0x%08lx\n", |
---|
756 | 737 | task_pid_nr(current), current->comm, code, instr); |
---|
757 | 738 | dump_instr(KERN_ERR, regs); |
---|
758 | | - show_pte(current->mm, addr); |
---|
| 739 | + show_pte(KERN_ERR, current->mm, addr); |
---|
759 | 740 | } |
---|
760 | 741 | #endif |
---|
761 | 742 | |
---|
762 | | - info.si_signo = SIGILL; |
---|
763 | | - info.si_errno = 0; |
---|
764 | | - info.si_code = ILL_ILLOPC; |
---|
765 | | - info.si_addr = (void __user *)addr; |
---|
766 | | - |
---|
767 | | - arm_notify_die("unknown data abort code", regs, &info, instr, 0); |
---|
| 743 | + arm_notify_die("unknown data abort code", regs, |
---|
| 744 | + SIGILL, ILL_ILLOPC, (void __user *)addr, instr, 0); |
---|
768 | 745 | } |
---|
769 | 746 | |
---|
770 | 747 | void __readwrite_bug(const char *fn) |
---|
.. | .. |
---|
830 | 807 | } |
---|
831 | 808 | #endif |
---|
832 | 809 | |
---|
| 810 | +#ifndef CONFIG_CPU_V7M |
---|
| 811 | +static void copy_from_lma(void *vma, void *lma_start, void *lma_end) |
---|
| 812 | +{ |
---|
| 813 | + memcpy(vma, lma_start, lma_end - lma_start); |
---|
| 814 | +} |
---|
| 815 | + |
---|
| 816 | +static void flush_vectors(void *vma, size_t offset, size_t size) |
---|
| 817 | +{ |
---|
| 818 | + unsigned long start = (unsigned long)vma + offset; |
---|
| 819 | + unsigned long end = start + size; |
---|
| 820 | + |
---|
| 821 | + flush_icache_range(start, end); |
---|
| 822 | +} |
---|
| 823 | + |
---|
| 824 | +#ifdef CONFIG_HARDEN_BRANCH_HISTORY |
---|
| 825 | +int spectre_bhb_update_vectors(unsigned int method) |
---|
| 826 | +{ |
---|
| 827 | + extern char __vectors_bhb_bpiall_start[], __vectors_bhb_bpiall_end[]; |
---|
| 828 | + extern char __vectors_bhb_loop8_start[], __vectors_bhb_loop8_end[]; |
---|
| 829 | + void *vec_start, *vec_end; |
---|
| 830 | + |
---|
| 831 | + if (system_state > SYSTEM_SCHEDULING) { |
---|
| 832 | + pr_err("CPU%u: Spectre BHB workaround too late - system vulnerable\n", |
---|
| 833 | + smp_processor_id()); |
---|
| 834 | + return SPECTRE_VULNERABLE; |
---|
| 835 | + } |
---|
| 836 | + |
---|
| 837 | + switch (method) { |
---|
| 838 | + case SPECTRE_V2_METHOD_LOOP8: |
---|
| 839 | + vec_start = __vectors_bhb_loop8_start; |
---|
| 840 | + vec_end = __vectors_bhb_loop8_end; |
---|
| 841 | + break; |
---|
| 842 | + |
---|
| 843 | + case SPECTRE_V2_METHOD_BPIALL: |
---|
| 844 | + vec_start = __vectors_bhb_bpiall_start; |
---|
| 845 | + vec_end = __vectors_bhb_bpiall_end; |
---|
| 846 | + break; |
---|
| 847 | + |
---|
| 848 | + default: |
---|
| 849 | + pr_err("CPU%u: unknown Spectre BHB state %d\n", |
---|
| 850 | + smp_processor_id(), method); |
---|
| 851 | + return SPECTRE_VULNERABLE; |
---|
| 852 | + } |
---|
| 853 | + |
---|
| 854 | + copy_from_lma(vectors_page, vec_start, vec_end); |
---|
| 855 | + flush_vectors(vectors_page, 0, vec_end - vec_start); |
---|
| 856 | + |
---|
| 857 | + return SPECTRE_MITIGATED; |
---|
| 858 | +} |
---|
| 859 | +#endif |
---|
| 860 | + |
---|
833 | 861 | void __init early_trap_init(void *vectors_base) |
---|
834 | 862 | { |
---|
835 | | -#ifndef CONFIG_CPU_V7M |
---|
836 | | - unsigned long vectors = (unsigned long)vectors_base; |
---|
837 | 863 | extern char __stubs_start[], __stubs_end[]; |
---|
838 | 864 | extern char __vectors_start[], __vectors_end[]; |
---|
839 | 865 | unsigned i; |
---|
.. | .. |
---|
854 | 880 | * into the vector page, mapped at 0xffff0000, and ensure these |
---|
855 | 881 | * are visible to the instruction stream. |
---|
856 | 882 | */ |
---|
857 | | - memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); |
---|
858 | | - memcpy((void *)vectors + 0x1000, __stubs_start, __stubs_end - __stubs_start); |
---|
| 883 | + copy_from_lma(vectors_base, __vectors_start, __vectors_end); |
---|
| 884 | + copy_from_lma(vectors_base + 0x1000, __stubs_start, __stubs_end); |
---|
859 | 885 | |
---|
860 | 886 | kuser_init(vectors_base); |
---|
861 | 887 | |
---|
862 | | - flush_icache_range(vectors, vectors + PAGE_SIZE * 2); |
---|
| 888 | + flush_vectors(vectors_base, 0, PAGE_SIZE * 2); |
---|
| 889 | +} |
---|
863 | 890 | #else /* ifndef CONFIG_CPU_V7M */ |
---|
| 891 | +void __init early_trap_init(void *vectors_base) |
---|
| 892 | +{ |
---|
864 | 893 | /* |
---|
865 | 894 | * on V7-M there is no need to copy the vector table to a dedicated |
---|
866 | 895 | * memory area. The address is configurable and so a table in the kernel |
---|
867 | 896 | * image can be used. |
---|
868 | 897 | */ |
---|
869 | | -#endif |
---|
870 | 898 | } |
---|
| 899 | +#endif |
---|