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