162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/arm/kernel/traps.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1995-2009 Russell King 662306a36Sopenharmony_ci * Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * 'traps.c' handles hardware exceptions after we have saved some state in 962306a36Sopenharmony_ci * 'linux/arch/arm/lib/traps.S'. Mostly a debugging aid, but will probably 1062306a36Sopenharmony_ci * kill the offending process. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include <linux/signal.h> 1362306a36Sopenharmony_ci#include <linux/personality.h> 1462306a36Sopenharmony_ci#include <linux/kallsyms.h> 1562306a36Sopenharmony_ci#include <linux/spinlock.h> 1662306a36Sopenharmony_ci#include <linux/uaccess.h> 1762306a36Sopenharmony_ci#include <linux/hardirq.h> 1862306a36Sopenharmony_ci#include <linux/kdebug.h> 1962306a36Sopenharmony_ci#include <linux/kprobes.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/kexec.h> 2262306a36Sopenharmony_ci#include <linux/bug.h> 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci#include <linux/init.h> 2562306a36Sopenharmony_ci#include <linux/sched/signal.h> 2662306a36Sopenharmony_ci#include <linux/sched/debug.h> 2762306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 2862306a36Sopenharmony_ci#include <linux/irq.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <linux/atomic.h> 3162306a36Sopenharmony_ci#include <asm/cacheflush.h> 3262306a36Sopenharmony_ci#include <asm/exception.h> 3362306a36Sopenharmony_ci#include <asm/spectre.h> 3462306a36Sopenharmony_ci#include <asm/unistd.h> 3562306a36Sopenharmony_ci#include <asm/traps.h> 3662306a36Sopenharmony_ci#include <asm/ptrace.h> 3762306a36Sopenharmony_ci#include <asm/unwind.h> 3862306a36Sopenharmony_ci#include <asm/tls.h> 3962306a36Sopenharmony_ci#include <asm/stacktrace.h> 4062306a36Sopenharmony_ci#include <asm/system_misc.h> 4162306a36Sopenharmony_ci#include <asm/opcodes.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const char *handler[]= { 4562306a36Sopenharmony_ci "prefetch abort", 4662306a36Sopenharmony_ci "data abort", 4762306a36Sopenharmony_ci "address exception", 4862306a36Sopenharmony_ci "interrupt", 4962306a36Sopenharmony_ci "undefined instruction", 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_civoid *vectors_page; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_USER 5562306a36Sopenharmony_ciunsigned int user_debug; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int __init user_debug_setup(char *str) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci get_option(&str, &user_debug); 6062306a36Sopenharmony_ci return 1; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci__setup("user_debug=", user_debug_setup); 6362306a36Sopenharmony_ci#endif 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_civoid dump_backtrace_entry(unsigned long where, unsigned long from, 6662306a36Sopenharmony_ci unsigned long frame, const char *loglvl) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci unsigned long end = frame + 4 + sizeof(struct pt_regs); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_UNWINDER_FRAME_POINTER) && 7162306a36Sopenharmony_ci IS_ENABLED(CONFIG_CC_IS_GCC) && 7262306a36Sopenharmony_ci end > ALIGN(frame, THREAD_SIZE)) { 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * If we are walking past the end of the stack, it may be due 7562306a36Sopenharmony_ci * to the fact that we are on an IRQ or overflow stack. In this 7662306a36Sopenharmony_ci * case, we can load the address of the other stack from the 7762306a36Sopenharmony_ci * frame record. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci frame = ((unsigned long *)frame)[-2] - 4; 8062306a36Sopenharmony_ci end = frame + 4 + sizeof(struct pt_regs); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#ifndef CONFIG_KALLSYMS 8462306a36Sopenharmony_ci printk("%sFunction entered at [<%08lx>] from [<%08lx>]\n", 8562306a36Sopenharmony_ci loglvl, where, from); 8662306a36Sopenharmony_ci#elif defined CONFIG_BACKTRACE_VERBOSE 8762306a36Sopenharmony_ci printk("%s[<%08lx>] (%ps) from [<%08lx>] (%pS)\n", 8862306a36Sopenharmony_ci loglvl, where, (void *)where, from, (void *)from); 8962306a36Sopenharmony_ci#else 9062306a36Sopenharmony_ci printk("%s %ps from %pS\n", loglvl, (void *)where, (void *)from); 9162306a36Sopenharmony_ci#endif 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (in_entry_text(from) && end <= ALIGN(frame, THREAD_SIZE)) 9462306a36Sopenharmony_ci dump_mem(loglvl, "Exception stack", frame + 4, end); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_civoid dump_backtrace_stm(u32 *stack, u32 instruction, const char *loglvl) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci char str[80], *p; 10062306a36Sopenharmony_ci unsigned int x; 10162306a36Sopenharmony_ci int reg; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci for (reg = 10, x = 0, p = str; reg >= 0; reg--) { 10462306a36Sopenharmony_ci if (instruction & BIT(reg)) { 10562306a36Sopenharmony_ci p += sprintf(p, " r%d:%08x", reg, *stack--); 10662306a36Sopenharmony_ci if (++x == 6) { 10762306a36Sopenharmony_ci x = 0; 10862306a36Sopenharmony_ci p = str; 10962306a36Sopenharmony_ci printk("%s%s\n", loglvl, str); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci if (p != str) 11462306a36Sopenharmony_ci printk("%s%s\n", loglvl, str); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#ifndef CONFIG_ARM_UNWIND 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * Stack pointers should always be within the kernels view of 12062306a36Sopenharmony_ci * physical memory. If it is not there, then we can't dump 12162306a36Sopenharmony_ci * out any information relating to the stack. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_cistatic int verify_stack(unsigned long sp) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci if (sp < PAGE_OFFSET || 12662306a36Sopenharmony_ci (!IS_ENABLED(CONFIG_VMAP_STACK) && 12762306a36Sopenharmony_ci sp > (unsigned long)high_memory && high_memory != NULL)) 12862306a36Sopenharmony_ci return -EFAULT; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci#endif 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * Dump out the contents of some memory nicely... 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_civoid dump_mem(const char *lvl, const char *str, unsigned long bottom, 13862306a36Sopenharmony_ci unsigned long top) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci unsigned long first; 14162306a36Sopenharmony_ci int i; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci printk("%s%s(0x%08lx to 0x%08lx)\n", lvl, str, bottom, top); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci for (first = bottom & ~31; first < top; first += 32) { 14662306a36Sopenharmony_ci unsigned long p; 14762306a36Sopenharmony_ci char str[sizeof(" 12345678") * 8 + 1]; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci memset(str, ' ', sizeof(str)); 15062306a36Sopenharmony_ci str[sizeof(str) - 1] = '\0'; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for (p = first, i = 0; i < 8 && p < top; i++, p += 4) { 15362306a36Sopenharmony_ci if (p >= bottom && p < top) { 15462306a36Sopenharmony_ci unsigned long val; 15562306a36Sopenharmony_ci if (!get_kernel_nofault(val, (unsigned long *)p)) 15662306a36Sopenharmony_ci sprintf(str + i * 9, " %08lx", val); 15762306a36Sopenharmony_ci else 15862306a36Sopenharmony_ci sprintf(str + i * 9, " ????????"); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci printk("%s%04lx:%s\n", lvl, first & 0xffff, str); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void dump_instr(const char *lvl, struct pt_regs *regs) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci unsigned long addr = instruction_pointer(regs); 16862306a36Sopenharmony_ci const int thumb = thumb_mode(regs); 16962306a36Sopenharmony_ci const int width = thumb ? 4 : 8; 17062306a36Sopenharmony_ci char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; 17162306a36Sopenharmony_ci int i; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * Note that we now dump the code first, just in case the backtrace 17562306a36Sopenharmony_ci * kills us. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci for (i = -4; i < 1 + !!thumb; i++) { 17962306a36Sopenharmony_ci unsigned int val, bad; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (thumb) { 18262306a36Sopenharmony_ci u16 tmp; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (user_mode(regs)) 18562306a36Sopenharmony_ci bad = get_user(tmp, &((u16 __user *)addr)[i]); 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci bad = get_kernel_nofault(tmp, &((u16 *)addr)[i]); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci val = __mem_to_opcode_thumb16(tmp); 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci if (user_mode(regs)) 19262306a36Sopenharmony_ci bad = get_user(val, &((u32 __user *)addr)[i]); 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci bad = get_kernel_nofault(val, &((u32 *)addr)[i]); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci val = __mem_to_opcode_arm(val); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!bad) 20062306a36Sopenharmony_ci p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ", 20162306a36Sopenharmony_ci width, val); 20262306a36Sopenharmony_ci else { 20362306a36Sopenharmony_ci p += sprintf(p, "bad PC value"); 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci printk("%sCode: %s\n", lvl, str); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci#ifdef CONFIG_ARM_UNWIND 21162306a36Sopenharmony_civoid dump_backtrace(struct pt_regs *regs, struct task_struct *tsk, 21262306a36Sopenharmony_ci const char *loglvl) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci unwind_backtrace(regs, tsk, loglvl); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci#else 21762306a36Sopenharmony_civoid dump_backtrace(struct pt_regs *regs, struct task_struct *tsk, 21862306a36Sopenharmony_ci const char *loglvl) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci unsigned int fp, mode; 22162306a36Sopenharmony_ci int ok = 1; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci printk("%sBacktrace: ", loglvl); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!tsk) 22662306a36Sopenharmony_ci tsk = current; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (regs) { 22962306a36Sopenharmony_ci fp = frame_pointer(regs); 23062306a36Sopenharmony_ci mode = processor_mode(regs); 23162306a36Sopenharmony_ci } else if (tsk != current) { 23262306a36Sopenharmony_ci fp = thread_saved_fp(tsk); 23362306a36Sopenharmony_ci mode = 0x10; 23462306a36Sopenharmony_ci } else { 23562306a36Sopenharmony_ci asm("mov %0, fp" : "=r" (fp) : : "cc"); 23662306a36Sopenharmony_ci mode = 0x10; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!fp) { 24062306a36Sopenharmony_ci pr_cont("no frame pointer"); 24162306a36Sopenharmony_ci ok = 0; 24262306a36Sopenharmony_ci } else if (verify_stack(fp)) { 24362306a36Sopenharmony_ci pr_cont("invalid frame pointer 0x%08x", fp); 24462306a36Sopenharmony_ci ok = 0; 24562306a36Sopenharmony_ci } else if (fp < (unsigned long)end_of_stack(tsk)) 24662306a36Sopenharmony_ci pr_cont("frame pointer underflow"); 24762306a36Sopenharmony_ci pr_cont("\n"); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (ok) 25062306a36Sopenharmony_ci c_backtrace(fp, mode, loglvl); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci#endif 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_civoid show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci dump_backtrace(NULL, tsk, loglvl); 25762306a36Sopenharmony_ci barrier(); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci#ifdef CONFIG_PREEMPT 26162306a36Sopenharmony_ci#define S_PREEMPT " PREEMPT" 26262306a36Sopenharmony_ci#elif defined(CONFIG_PREEMPT_RT) 26362306a36Sopenharmony_ci#define S_PREEMPT " PREEMPT_RT" 26462306a36Sopenharmony_ci#else 26562306a36Sopenharmony_ci#define S_PREEMPT "" 26662306a36Sopenharmony_ci#endif 26762306a36Sopenharmony_ci#ifdef CONFIG_SMP 26862306a36Sopenharmony_ci#define S_SMP " SMP" 26962306a36Sopenharmony_ci#else 27062306a36Sopenharmony_ci#define S_SMP "" 27162306a36Sopenharmony_ci#endif 27262306a36Sopenharmony_ci#ifdef CONFIG_THUMB2_KERNEL 27362306a36Sopenharmony_ci#define S_ISA " THUMB2" 27462306a36Sopenharmony_ci#else 27562306a36Sopenharmony_ci#define S_ISA " ARM" 27662306a36Sopenharmony_ci#endif 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int __die(const char *str, int err, struct pt_regs *regs) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct task_struct *tsk = current; 28162306a36Sopenharmony_ci static int die_counter; 28262306a36Sopenharmony_ci int ret; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci pr_emerg("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP S_ISA "\n", 28562306a36Sopenharmony_ci str, err, ++die_counter); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* trap and error numbers are mostly meaningless on ARM */ 28862306a36Sopenharmony_ci ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, SIGSEGV); 28962306a36Sopenharmony_ci if (ret == NOTIFY_STOP) 29062306a36Sopenharmony_ci return 1; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci print_modules(); 29362306a36Sopenharmony_ci __show_regs(regs); 29462306a36Sopenharmony_ci __show_regs_alloc_free(regs); 29562306a36Sopenharmony_ci pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n", 29662306a36Sopenharmony_ci TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), end_of_stack(tsk)); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (!user_mode(regs) || in_interrupt()) { 29962306a36Sopenharmony_ci dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp, 30062306a36Sopenharmony_ci ALIGN(regs->ARM_sp - THREAD_SIZE, THREAD_ALIGN) 30162306a36Sopenharmony_ci + THREAD_SIZE); 30262306a36Sopenharmony_ci dump_backtrace(regs, tsk, KERN_EMERG); 30362306a36Sopenharmony_ci dump_instr(KERN_EMERG, regs); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED; 31062306a36Sopenharmony_cistatic int die_owner = -1; 31162306a36Sopenharmony_cistatic unsigned int die_nest_count; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic unsigned long oops_begin(void) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci int cpu; 31662306a36Sopenharmony_ci unsigned long flags; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci oops_enter(); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* racy, but better than risking deadlock. */ 32162306a36Sopenharmony_ci raw_local_irq_save(flags); 32262306a36Sopenharmony_ci cpu = smp_processor_id(); 32362306a36Sopenharmony_ci if (!arch_spin_trylock(&die_lock)) { 32462306a36Sopenharmony_ci if (cpu == die_owner) 32562306a36Sopenharmony_ci /* nested oops. should stop eventually */; 32662306a36Sopenharmony_ci else 32762306a36Sopenharmony_ci arch_spin_lock(&die_lock); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci die_nest_count++; 33062306a36Sopenharmony_ci die_owner = cpu; 33162306a36Sopenharmony_ci console_verbose(); 33262306a36Sopenharmony_ci bust_spinlocks(1); 33362306a36Sopenharmony_ci return flags; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void oops_end(unsigned long flags, struct pt_regs *regs, int signr) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci if (regs && kexec_should_crash(current)) 33962306a36Sopenharmony_ci crash_kexec(regs); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci bust_spinlocks(0); 34262306a36Sopenharmony_ci die_owner = -1; 34362306a36Sopenharmony_ci add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); 34462306a36Sopenharmony_ci die_nest_count--; 34562306a36Sopenharmony_ci if (!die_nest_count) 34662306a36Sopenharmony_ci /* Nest count reaches zero, release the lock. */ 34762306a36Sopenharmony_ci arch_spin_unlock(&die_lock); 34862306a36Sopenharmony_ci raw_local_irq_restore(flags); 34962306a36Sopenharmony_ci oops_exit(); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (in_interrupt()) 35262306a36Sopenharmony_ci panic("Fatal exception in interrupt"); 35362306a36Sopenharmony_ci if (panic_on_oops) 35462306a36Sopenharmony_ci panic("Fatal exception"); 35562306a36Sopenharmony_ci if (signr) 35662306a36Sopenharmony_ci make_task_dead(signr); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * This function is protected against re-entrancy. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_civoid die(const char *str, struct pt_regs *regs, int err) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE; 36562306a36Sopenharmony_ci unsigned long flags = oops_begin(); 36662306a36Sopenharmony_ci int sig = SIGSEGV; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!user_mode(regs)) 36962306a36Sopenharmony_ci bug_type = report_bug(regs->ARM_pc, regs); 37062306a36Sopenharmony_ci if (bug_type != BUG_TRAP_TYPE_NONE) 37162306a36Sopenharmony_ci str = "Oops - BUG"; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (__die(str, err, regs)) 37462306a36Sopenharmony_ci sig = 0; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci oops_end(flags, regs, sig); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_civoid arm_notify_die(const char *str, struct pt_regs *regs, 38062306a36Sopenharmony_ci int signo, int si_code, void __user *addr, 38162306a36Sopenharmony_ci unsigned long err, unsigned long trap) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci if (user_mode(regs)) { 38462306a36Sopenharmony_ci current->thread.error_code = err; 38562306a36Sopenharmony_ci current->thread.trap_no = trap; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci force_sig_fault(signo, si_code, addr); 38862306a36Sopenharmony_ci } else { 38962306a36Sopenharmony_ci die(str, regs, err); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_BUG 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ciint is_valid_bugaddr(unsigned long pc) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci#ifdef CONFIG_THUMB2_KERNEL 39862306a36Sopenharmony_ci u16 bkpt; 39962306a36Sopenharmony_ci u16 insn = __opcode_to_mem_thumb16(BUG_INSTR_VALUE); 40062306a36Sopenharmony_ci#else 40162306a36Sopenharmony_ci u32 bkpt; 40262306a36Sopenharmony_ci u32 insn = __opcode_to_mem_arm(BUG_INSTR_VALUE); 40362306a36Sopenharmony_ci#endif 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (get_kernel_nofault(bkpt, (void *)pc)) 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return bkpt == insn; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci#endif 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic LIST_HEAD(undef_hook); 41462306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(undef_lock); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_civoid register_undef_hook(struct undef_hook *hook) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci unsigned long flags; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci raw_spin_lock_irqsave(&undef_lock, flags); 42162306a36Sopenharmony_ci list_add(&hook->node, &undef_hook); 42262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&undef_lock, flags); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_civoid unregister_undef_hook(struct undef_hook *hook) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci unsigned long flags; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci raw_spin_lock_irqsave(&undef_lock, flags); 43062306a36Sopenharmony_ci list_del(&hook->node); 43162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&undef_lock, flags); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic nokprobe_inline 43562306a36Sopenharmony_ciint call_undef_hook(struct pt_regs *regs, unsigned int instr) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct undef_hook *hook; 43862306a36Sopenharmony_ci unsigned long flags; 43962306a36Sopenharmony_ci int (*fn)(struct pt_regs *regs, unsigned int instr) = NULL; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci raw_spin_lock_irqsave(&undef_lock, flags); 44262306a36Sopenharmony_ci list_for_each_entry(hook, &undef_hook, node) 44362306a36Sopenharmony_ci if ((instr & hook->instr_mask) == hook->instr_val && 44462306a36Sopenharmony_ci (regs->ARM_cpsr & hook->cpsr_mask) == hook->cpsr_val) 44562306a36Sopenharmony_ci fn = hook->fn; 44662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&undef_lock, flags); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return fn ? fn(regs, instr) : 1; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ciasmlinkage void do_undefinstr(struct pt_regs *regs) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci unsigned int instr; 45462306a36Sopenharmony_ci void __user *pc; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci pc = (void __user *)instruction_pointer(regs); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (processor_mode(regs) == SVC_MODE) { 45962306a36Sopenharmony_ci#ifdef CONFIG_THUMB2_KERNEL 46062306a36Sopenharmony_ci if (thumb_mode(regs)) { 46162306a36Sopenharmony_ci instr = __mem_to_opcode_thumb16(((u16 *)pc)[0]); 46262306a36Sopenharmony_ci if (is_wide_instruction(instr)) { 46362306a36Sopenharmony_ci u16 inst2; 46462306a36Sopenharmony_ci inst2 = __mem_to_opcode_thumb16(((u16 *)pc)[1]); 46562306a36Sopenharmony_ci instr = __opcode_thumb32_compose(instr, inst2); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci } else 46862306a36Sopenharmony_ci#endif 46962306a36Sopenharmony_ci instr = __mem_to_opcode_arm(*(u32 *) pc); 47062306a36Sopenharmony_ci } else if (thumb_mode(regs)) { 47162306a36Sopenharmony_ci if (get_user(instr, (u16 __user *)pc)) 47262306a36Sopenharmony_ci goto die_sig; 47362306a36Sopenharmony_ci instr = __mem_to_opcode_thumb16(instr); 47462306a36Sopenharmony_ci if (is_wide_instruction(instr)) { 47562306a36Sopenharmony_ci unsigned int instr2; 47662306a36Sopenharmony_ci if (get_user(instr2, (u16 __user *)pc+1)) 47762306a36Sopenharmony_ci goto die_sig; 47862306a36Sopenharmony_ci instr2 = __mem_to_opcode_thumb16(instr2); 47962306a36Sopenharmony_ci instr = __opcode_thumb32_compose(instr, instr2); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci } else { 48262306a36Sopenharmony_ci if (get_user(instr, (u32 __user *)pc)) 48362306a36Sopenharmony_ci goto die_sig; 48462306a36Sopenharmony_ci instr = __mem_to_opcode_arm(instr); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (call_undef_hook(regs, instr) == 0) 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cidie_sig: 49162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_USER 49262306a36Sopenharmony_ci if (user_debug & UDBG_UNDEFINED) { 49362306a36Sopenharmony_ci pr_info("%s (%d): undefined instruction: pc=%px\n", 49462306a36Sopenharmony_ci current->comm, task_pid_nr(current), pc); 49562306a36Sopenharmony_ci __show_regs(regs); 49662306a36Sopenharmony_ci dump_instr(KERN_INFO, regs); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci#endif 49962306a36Sopenharmony_ci arm_notify_die("Oops - undefined instruction", regs, 50062306a36Sopenharmony_ci SIGILL, ILL_ILLOPC, pc, 0, 6); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ciNOKPROBE_SYMBOL(do_undefinstr) 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* 50562306a36Sopenharmony_ci * Handle FIQ similarly to NMI on x86 systems. 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * The runtime environment for NMIs is extremely restrictive 50862306a36Sopenharmony_ci * (NMIs can pre-empt critical sections meaning almost all locking is 50962306a36Sopenharmony_ci * forbidden) meaning this default FIQ handling must only be used in 51062306a36Sopenharmony_ci * circumstances where non-maskability improves robustness, such as 51162306a36Sopenharmony_ci * watchdog or debug logic. 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * This handler is not appropriate for general purpose use in drivers 51462306a36Sopenharmony_ci * platform code and can be overrideen using set_fiq_handler. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ciasmlinkage void __exception_irq_entry handle_fiq_as_nmi(struct pt_regs *regs) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct pt_regs *old_regs = set_irq_regs(regs); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci nmi_enter(); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* nop. FIQ handlers for special arch/arm features can be added here. */ 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci nmi_exit(); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci set_irq_regs(old_regs); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* 53062306a36Sopenharmony_ci * bad_mode handles the impossible case in the vectors. If you see one of 53162306a36Sopenharmony_ci * these, then it's extremely serious, and could mean you have buggy hardware. 53262306a36Sopenharmony_ci * It never returns, and never tries to sync. We hope that we can at least 53362306a36Sopenharmony_ci * dump out some state information... 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ciasmlinkage void bad_mode(struct pt_regs *regs, int reason) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci console_verbose(); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci pr_crit("Bad mode in %s handler detected\n", handler[reason]); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci die("Oops - bad mode", regs, 0); 54262306a36Sopenharmony_ci local_irq_disable(); 54362306a36Sopenharmony_ci panic("bad mode"); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int bad_syscall(int n, struct pt_regs *regs) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci if ((current->personality & PER_MASK) != PER_LINUX) { 54962306a36Sopenharmony_ci send_sig(SIGSEGV, current, 1); 55062306a36Sopenharmony_ci return regs->ARM_r0; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_USER 55462306a36Sopenharmony_ci if (user_debug & UDBG_SYSCALL) { 55562306a36Sopenharmony_ci pr_err("[%d] %s: obsolete system call %08x.\n", 55662306a36Sopenharmony_ci task_pid_nr(current), current->comm, n); 55762306a36Sopenharmony_ci dump_instr(KERN_ERR, regs); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci#endif 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci arm_notify_die("Oops - bad syscall", regs, SIGILL, ILL_ILLTRP, 56262306a36Sopenharmony_ci (void __user *)instruction_pointer(regs) - 56362306a36Sopenharmony_ci (thumb_mode(regs) ? 2 : 4), 56462306a36Sopenharmony_ci n, 0); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return regs->ARM_r0; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic inline int 57062306a36Sopenharmony_ci__do_cache_op(unsigned long start, unsigned long end) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci int ret; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci do { 57562306a36Sopenharmony_ci unsigned long chunk = min(PAGE_SIZE, end - start); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (fatal_signal_pending(current)) 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci ret = flush_icache_user_range(start, start + chunk); 58162306a36Sopenharmony_ci if (ret) 58262306a36Sopenharmony_ci return ret; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci cond_resched(); 58562306a36Sopenharmony_ci start += chunk; 58662306a36Sopenharmony_ci } while (start < end); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic inline int 59262306a36Sopenharmony_cido_cache_op(unsigned long start, unsigned long end, int flags) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci if (end < start || flags) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!access_ok((void __user *)start, end - start)) 59862306a36Sopenharmony_ci return -EFAULT; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return __do_cache_op(start, end); 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci/* 60462306a36Sopenharmony_ci * Handle all unrecognised system calls. 60562306a36Sopenharmony_ci * 0x9f0000 - 0x9fffff are some more esoteric system calls 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci#define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE) 60862306a36Sopenharmony_ciasmlinkage int arm_syscall(int no, struct pt_regs *regs) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci if ((no >> 16) != (__ARM_NR_BASE>> 16)) 61162306a36Sopenharmony_ci return bad_syscall(no, regs); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci switch (no & 0xffff) { 61462306a36Sopenharmony_ci case 0: /* branch through 0 */ 61562306a36Sopenharmony_ci arm_notify_die("branch through zero", regs, 61662306a36Sopenharmony_ci SIGSEGV, SEGV_MAPERR, NULL, 0, 0); 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci case NR(breakpoint): /* SWI BREAK_POINT */ 62062306a36Sopenharmony_ci regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; 62162306a36Sopenharmony_ci ptrace_break(regs); 62262306a36Sopenharmony_ci return regs->ARM_r0; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* 62562306a36Sopenharmony_ci * Flush a region from virtual address 'r0' to virtual address 'r1' 62662306a36Sopenharmony_ci * _exclusive_. There is no alignment requirement on either address; 62762306a36Sopenharmony_ci * user space does not need to know the hardware cache layout. 62862306a36Sopenharmony_ci * 62962306a36Sopenharmony_ci * r2 contains flags. It should ALWAYS be passed as ZERO until it 63062306a36Sopenharmony_ci * is defined to be something else. For now we ignore it, but may 63162306a36Sopenharmony_ci * the fires of hell burn in your belly if you break this rule. ;) 63262306a36Sopenharmony_ci * 63362306a36Sopenharmony_ci * (at a later date, we may want to allow this call to not flush 63462306a36Sopenharmony_ci * various aspects of the cache. Passing '0' will guarantee that 63562306a36Sopenharmony_ci * everything necessary gets flushed to maintain consistency in 63662306a36Sopenharmony_ci * the specified region). 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci case NR(cacheflush): 63962306a36Sopenharmony_ci return do_cache_op(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci case NR(usr26): 64262306a36Sopenharmony_ci if (!(elf_hwcap & HWCAP_26BIT)) 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci regs->ARM_cpsr &= ~MODE32_BIT; 64562306a36Sopenharmony_ci return regs->ARM_r0; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci case NR(usr32): 64862306a36Sopenharmony_ci if (!(elf_hwcap & HWCAP_26BIT)) 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci regs->ARM_cpsr |= MODE32_BIT; 65162306a36Sopenharmony_ci return regs->ARM_r0; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci case NR(set_tls): 65462306a36Sopenharmony_ci set_tls(regs->ARM_r0); 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci case NR(get_tls): 65862306a36Sopenharmony_ci return current_thread_info()->tp_value[0]; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci default: 66162306a36Sopenharmony_ci /* Calls 9f00xx..9f07ff are defined to return -ENOSYS 66262306a36Sopenharmony_ci if not implemented, rather than raising SIGILL. This 66362306a36Sopenharmony_ci way the calling program can gracefully determine whether 66462306a36Sopenharmony_ci a feature is supported. */ 66562306a36Sopenharmony_ci if ((no & 0xffff) <= 0x7ff) 66662306a36Sopenharmony_ci return -ENOSYS; 66762306a36Sopenharmony_ci break; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_USER 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * experience shows that these seem to indicate that 67262306a36Sopenharmony_ci * something catastrophic has happened 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ci if (user_debug & UDBG_SYSCALL) { 67562306a36Sopenharmony_ci pr_err("[%d] %s: arm syscall %d\n", 67662306a36Sopenharmony_ci task_pid_nr(current), current->comm, no); 67762306a36Sopenharmony_ci dump_instr(KERN_ERR, regs); 67862306a36Sopenharmony_ci if (user_mode(regs)) { 67962306a36Sopenharmony_ci __show_regs(regs); 68062306a36Sopenharmony_ci c_backtrace(frame_pointer(regs), processor_mode(regs), KERN_ERR); 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci#endif 68462306a36Sopenharmony_ci arm_notify_die("Oops - bad syscall(2)", regs, SIGILL, ILL_ILLTRP, 68562306a36Sopenharmony_ci (void __user *)instruction_pointer(regs) - 68662306a36Sopenharmony_ci (thumb_mode(regs) ? 2 : 4), 68762306a36Sopenharmony_ci no, 0); 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci#ifdef CONFIG_TLS_REG_EMUL 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci/* 69462306a36Sopenharmony_ci * We might be running on an ARMv6+ processor which should have the TLS 69562306a36Sopenharmony_ci * register but for some reason we can't use it, or maybe an SMP system 69662306a36Sopenharmony_ci * using a pre-ARMv6 processor (there are apparently a few prototypes like 69762306a36Sopenharmony_ci * that in existence) and therefore access to that register must be 69862306a36Sopenharmony_ci * emulated. 69962306a36Sopenharmony_ci */ 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int get_tp_trap(struct pt_regs *regs, unsigned int instr) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci int reg = (instr >> 12) & 15; 70462306a36Sopenharmony_ci if (reg == 15) 70562306a36Sopenharmony_ci return 1; 70662306a36Sopenharmony_ci regs->uregs[reg] = current_thread_info()->tp_value[0]; 70762306a36Sopenharmony_ci regs->ARM_pc += 4; 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic struct undef_hook arm_mrc_hook = { 71262306a36Sopenharmony_ci .instr_mask = 0x0fff0fff, 71362306a36Sopenharmony_ci .instr_val = 0x0e1d0f70, 71462306a36Sopenharmony_ci .cpsr_mask = PSR_T_BIT, 71562306a36Sopenharmony_ci .cpsr_val = 0, 71662306a36Sopenharmony_ci .fn = get_tp_trap, 71762306a36Sopenharmony_ci}; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int __init arm_mrc_hook_init(void) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci register_undef_hook(&arm_mrc_hook); 72262306a36Sopenharmony_ci return 0; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cilate_initcall(arm_mrc_hook_init); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci#endif 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci/* 73062306a36Sopenharmony_ci * A data abort trap was taken, but we did not handle the instruction. 73162306a36Sopenharmony_ci * Try to abort the user program, or panic if it was the kernel. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ciasmlinkage void 73462306a36Sopenharmony_cibaddataabort(int code, unsigned long instr, struct pt_regs *regs) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci unsigned long addr = instruction_pointer(regs); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_USER 73962306a36Sopenharmony_ci if (user_debug & UDBG_BADABORT) { 74062306a36Sopenharmony_ci pr_err("8<--- cut here ---\n"); 74162306a36Sopenharmony_ci pr_err("[%d] %s: bad data abort: code %d instr 0x%08lx\n", 74262306a36Sopenharmony_ci task_pid_nr(current), current->comm, code, instr); 74362306a36Sopenharmony_ci dump_instr(KERN_ERR, regs); 74462306a36Sopenharmony_ci show_pte(KERN_ERR, current->mm, addr); 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci#endif 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci arm_notify_die("unknown data abort code", regs, 74962306a36Sopenharmony_ci SIGILL, ILL_ILLOPC, (void __user *)addr, instr, 0); 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_civoid __readwrite_bug(const char *fn) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci pr_err("%s called, but not implemented\n", fn); 75562306a36Sopenharmony_ci BUG(); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ciEXPORT_SYMBOL(__readwrite_bug); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci#ifdef CONFIG_MMU 76062306a36Sopenharmony_civoid __pte_error(const char *file, int line, pte_t pte) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci pr_err("%s:%d: bad pte %08llx.\n", file, line, (long long)pte_val(pte)); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_civoid __pmd_error(const char *file, int line, pmd_t pmd) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci pr_err("%s:%d: bad pmd %08llx.\n", file, line, (long long)pmd_val(pmd)); 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_civoid __pgd_error(const char *file, int line, pgd_t pgd) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci pr_err("%s:%d: bad pgd %08llx.\n", file, line, (long long)pgd_val(pgd)); 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci#endif 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ciasmlinkage void __div0(void) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci pr_err("Division by zero in kernel.\n"); 77962306a36Sopenharmony_ci dump_stack(); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ciEXPORT_SYMBOL(__div0); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_civoid abort(void) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci BUG(); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* if that doesn't kill us, halt */ 78862306a36Sopenharmony_ci panic("Oops failed to kill thread"); 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci#ifdef CONFIG_KUSER_HELPERS 79262306a36Sopenharmony_cistatic void __init kuser_init(void *vectors) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci extern char __kuser_helper_start[], __kuser_helper_end[]; 79562306a36Sopenharmony_ci int kuser_sz = __kuser_helper_end - __kuser_helper_start; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci memcpy(vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* 80062306a36Sopenharmony_ci * vectors + 0xfe0 = __kuser_get_tls 80162306a36Sopenharmony_ci * vectors + 0xfe8 = hardware TLS instruction at 0xffff0fe8 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci if (tls_emu || has_tls_reg) 80462306a36Sopenharmony_ci memcpy(vectors + 0xfe0, vectors + 0xfe8, 4); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci#else 80762306a36Sopenharmony_cistatic inline void __init kuser_init(void *vectors) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci#endif 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci#ifndef CONFIG_CPU_V7M 81362306a36Sopenharmony_cistatic void copy_from_lma(void *vma, void *lma_start, void *lma_end) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci memcpy(vma, lma_start, lma_end - lma_start); 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic void flush_vectors(void *vma, size_t offset, size_t size) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci unsigned long start = (unsigned long)vma + offset; 82162306a36Sopenharmony_ci unsigned long end = start + size; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci flush_icache_range(start, end); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci#ifdef CONFIG_HARDEN_BRANCH_HISTORY 82762306a36Sopenharmony_ciint spectre_bhb_update_vectors(unsigned int method) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci extern char __vectors_bhb_bpiall_start[], __vectors_bhb_bpiall_end[]; 83062306a36Sopenharmony_ci extern char __vectors_bhb_loop8_start[], __vectors_bhb_loop8_end[]; 83162306a36Sopenharmony_ci void *vec_start, *vec_end; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (system_state >= SYSTEM_FREEING_INITMEM) { 83462306a36Sopenharmony_ci pr_err("CPU%u: Spectre BHB workaround too late - system vulnerable\n", 83562306a36Sopenharmony_ci smp_processor_id()); 83662306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci switch (method) { 84062306a36Sopenharmony_ci case SPECTRE_V2_METHOD_LOOP8: 84162306a36Sopenharmony_ci vec_start = __vectors_bhb_loop8_start; 84262306a36Sopenharmony_ci vec_end = __vectors_bhb_loop8_end; 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci case SPECTRE_V2_METHOD_BPIALL: 84662306a36Sopenharmony_ci vec_start = __vectors_bhb_bpiall_start; 84762306a36Sopenharmony_ci vec_end = __vectors_bhb_bpiall_end; 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci default: 85162306a36Sopenharmony_ci pr_err("CPU%u: unknown Spectre BHB state %d\n", 85262306a36Sopenharmony_ci smp_processor_id(), method); 85362306a36Sopenharmony_ci return SPECTRE_VULNERABLE; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci copy_from_lma(vectors_page, vec_start, vec_end); 85762306a36Sopenharmony_ci flush_vectors(vectors_page, 0, vec_end - vec_start); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci return SPECTRE_MITIGATED; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci#endif 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_civoid __init early_trap_init(void *vectors_base) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci extern char __stubs_start[], __stubs_end[]; 86662306a36Sopenharmony_ci extern char __vectors_start[], __vectors_end[]; 86762306a36Sopenharmony_ci unsigned i; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci vectors_page = vectors_base; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* 87262306a36Sopenharmony_ci * Poison the vectors page with an undefined instruction. This 87362306a36Sopenharmony_ci * instruction is chosen to be undefined for both ARM and Thumb 87462306a36Sopenharmony_ci * ISAs. The Thumb version is an undefined instruction with a 87562306a36Sopenharmony_ci * branch back to the undefined instruction. 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_ci for (i = 0; i < PAGE_SIZE / sizeof(u32); i++) 87862306a36Sopenharmony_ci ((u32 *)vectors_base)[i] = 0xe7fddef1; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* 88162306a36Sopenharmony_ci * Copy the vectors, stubs and kuser helpers (in entry-armv.S) 88262306a36Sopenharmony_ci * into the vector page, mapped at 0xffff0000, and ensure these 88362306a36Sopenharmony_ci * are visible to the instruction stream. 88462306a36Sopenharmony_ci */ 88562306a36Sopenharmony_ci copy_from_lma(vectors_base, __vectors_start, __vectors_end); 88662306a36Sopenharmony_ci copy_from_lma(vectors_base + 0x1000, __stubs_start, __stubs_end); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci kuser_init(vectors_base); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci flush_vectors(vectors_base, 0, PAGE_SIZE * 2); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci#else /* ifndef CONFIG_CPU_V7M */ 89362306a36Sopenharmony_civoid __init early_trap_init(void *vectors_base) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci /* 89662306a36Sopenharmony_ci * on V7-M there is no need to copy the vector table to a dedicated 89762306a36Sopenharmony_ci * memory area. The address is configurable and so a table in the kernel 89862306a36Sopenharmony_ci * image can be used. 89962306a36Sopenharmony_ci */ 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci#endif 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci#ifdef CONFIG_VMAP_STACK 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ciDECLARE_PER_CPU(u8 *, irq_stack_ptr); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ciasmlinkage DEFINE_PER_CPU(u8 *, overflow_stack_ptr); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic int __init allocate_overflow_stacks(void) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci u8 *stack; 91262306a36Sopenharmony_ci int cpu; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 91562306a36Sopenharmony_ci stack = (u8 *)__get_free_page(GFP_KERNEL); 91662306a36Sopenharmony_ci if (WARN_ON(!stack)) 91762306a36Sopenharmony_ci return -ENOMEM; 91862306a36Sopenharmony_ci per_cpu(overflow_stack_ptr, cpu) = &stack[OVERFLOW_STACK_SIZE]; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci return 0; 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_ciearly_initcall(allocate_overflow_stacks); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ciasmlinkage void handle_bad_stack(struct pt_regs *regs) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci unsigned long tsk_stk = (unsigned long)current->stack; 92762306a36Sopenharmony_ci#ifdef CONFIG_IRQSTACKS 92862306a36Sopenharmony_ci unsigned long irq_stk = (unsigned long)raw_cpu_read(irq_stack_ptr); 92962306a36Sopenharmony_ci#endif 93062306a36Sopenharmony_ci unsigned long ovf_stk = (unsigned long)raw_cpu_read(overflow_stack_ptr); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci console_verbose(); 93362306a36Sopenharmony_ci pr_emerg("Insufficient stack space to handle exception!"); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci pr_emerg("Task stack: [0x%08lx..0x%08lx]\n", 93662306a36Sopenharmony_ci tsk_stk, tsk_stk + THREAD_SIZE); 93762306a36Sopenharmony_ci#ifdef CONFIG_IRQSTACKS 93862306a36Sopenharmony_ci pr_emerg("IRQ stack: [0x%08lx..0x%08lx]\n", 93962306a36Sopenharmony_ci irq_stk - THREAD_SIZE, irq_stk); 94062306a36Sopenharmony_ci#endif 94162306a36Sopenharmony_ci pr_emerg("Overflow stack: [0x%08lx..0x%08lx]\n", 94262306a36Sopenharmony_ci ovf_stk - OVERFLOW_STACK_SIZE, ovf_stk); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci die("kernel stack overflow", regs, 0); 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci#ifndef CONFIG_ARM_LPAE 94862306a36Sopenharmony_ci/* 94962306a36Sopenharmony_ci * Normally, we rely on the logic in do_translation_fault() to update stale PMD 95062306a36Sopenharmony_ci * entries covering the vmalloc space in a task's page tables when it first 95162306a36Sopenharmony_ci * accesses the region in question. Unfortunately, this is not sufficient when 95262306a36Sopenharmony_ci * the task stack resides in the vmalloc region, as do_translation_fault() is a 95362306a36Sopenharmony_ci * C function that needs a stack to run. 95462306a36Sopenharmony_ci * 95562306a36Sopenharmony_ci * So we need to ensure that these PMD entries are up to date *before* the MM 95662306a36Sopenharmony_ci * switch. As we already have some logic in the MM switch path that takes care 95762306a36Sopenharmony_ci * of this, let's trigger it by bumping the counter every time the core vmalloc 95862306a36Sopenharmony_ci * code modifies a PMD entry in the vmalloc region. Use release semantics on 95962306a36Sopenharmony_ci * the store so that other CPUs observing the counter's new value are 96062306a36Sopenharmony_ci * guaranteed to see the updated page table entries as well. 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_civoid arch_sync_kernel_mappings(unsigned long start, unsigned long end) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci if (start < VMALLOC_END && end > VMALLOC_START) 96562306a36Sopenharmony_ci atomic_inc_return_release(&init_mm.context.vmalloc_seq); 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci#endif 96862306a36Sopenharmony_ci#endif 969