18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/arch/parisc/traps.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1991, 1992  Linus Torvalds
68c2ecf20Sopenharmony_ci *  Copyright (C) 1999, 2000  Philipp Rumpf <prumpf@tux.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * 'Traps.c' handles hardware traps and faults after we have saved some
118c2ecf20Sopenharmony_ci * state in 'asm.s'.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/sched.h>
158c2ecf20Sopenharmony_ci#include <linux/sched/debug.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
208c2ecf20Sopenharmony_ci#include <linux/timer.h>
218c2ecf20Sopenharmony_ci#include <linux/delay.h>
228c2ecf20Sopenharmony_ci#include <linux/mm.h>
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <linux/smp.h>
258c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
268c2ecf20Sopenharmony_ci#include <linux/init.h>
278c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
288c2ecf20Sopenharmony_ci#include <linux/console.h>
298c2ecf20Sopenharmony_ci#include <linux/bug.h>
308c2ecf20Sopenharmony_ci#include <linux/ratelimit.h>
318c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
328c2ecf20Sopenharmony_ci#include <linux/kdebug.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <asm/assembly.h>
358c2ecf20Sopenharmony_ci#include <asm/io.h>
368c2ecf20Sopenharmony_ci#include <asm/irq.h>
378c2ecf20Sopenharmony_ci#include <asm/traps.h>
388c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
398c2ecf20Sopenharmony_ci#include <linux/atomic.h>
408c2ecf20Sopenharmony_ci#include <asm/smp.h>
418c2ecf20Sopenharmony_ci#include <asm/pdc.h>
428c2ecf20Sopenharmony_ci#include <asm/pdc_chassis.h>
438c2ecf20Sopenharmony_ci#include <asm/unwind.h>
448c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
458c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
468c2ecf20Sopenharmony_ci#include <linux/kgdb.h>
478c2ecf20Sopenharmony_ci#include <linux/kprobes.h>
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include "../math-emu/math-emu.h"	/* for handle_fpe() */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void parisc_show_stack(struct task_struct *task,
528c2ecf20Sopenharmony_ci	struct pt_regs *regs, const char *loglvl);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int printbinary(char *buf, unsigned long x, int nbits)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	unsigned long mask = 1UL << (nbits - 1);
578c2ecf20Sopenharmony_ci	while (mask != 0) {
588c2ecf20Sopenharmony_ci		*buf++ = (mask & x ? '1' : '0');
598c2ecf20Sopenharmony_ci		mask >>= 1;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	*buf = '\0';
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return nbits;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
678c2ecf20Sopenharmony_ci#define RFMT "%016lx"
688c2ecf20Sopenharmony_ci#else
698c2ecf20Sopenharmony_ci#define RFMT "%08lx"
708c2ecf20Sopenharmony_ci#endif
718c2ecf20Sopenharmony_ci#define FFMT "%016llx"	/* fpregs are 64-bit always */
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define PRINTREGS(lvl,r,f,fmt,x)	\
748c2ecf20Sopenharmony_ci	printk("%s%s%02d-%02d  " fmt " " fmt " " fmt " " fmt "\n",	\
758c2ecf20Sopenharmony_ci		lvl, f, (x), (x+3), (r)[(x)+0], (r)[(x)+1],		\
768c2ecf20Sopenharmony_ci		(r)[(x)+2], (r)[(x)+3])
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void print_gr(const char *level, struct pt_regs *regs)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	int i;
818c2ecf20Sopenharmony_ci	char buf[64];
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	printk("%s\n", level);
848c2ecf20Sopenharmony_ci	printk("%s     YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI\n", level);
858c2ecf20Sopenharmony_ci	printbinary(buf, regs->gr[0], 32);
868c2ecf20Sopenharmony_ci	printk("%sPSW: %s %s\n", level, buf, print_tainted());
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i += 4)
898c2ecf20Sopenharmony_ci		PRINTREGS(level, regs->gr, "r", RFMT, i);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void print_fr(const char *level, struct pt_regs *regs)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	int i;
958c2ecf20Sopenharmony_ci	char buf[64];
968c2ecf20Sopenharmony_ci	struct { u32 sw[2]; } s;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* FR are 64bit everywhere. Need to use asm to get the content
998c2ecf20Sopenharmony_ci	 * of fpsr/fper1, and we assume that we won't have a FP Identify
1008c2ecf20Sopenharmony_ci	 * in our way, otherwise we're screwed.
1018c2ecf20Sopenharmony_ci	 * The fldd is used to restore the T-bit if there was one, as the
1028c2ecf20Sopenharmony_ci	 * store clears it anyway.
1038c2ecf20Sopenharmony_ci	 * PA2.0 book says "thou shall not use fstw on FPSR/FPERs" - T-Bone */
1048c2ecf20Sopenharmony_ci	asm volatile ("fstd %%fr0,0(%1)	\n\t"
1058c2ecf20Sopenharmony_ci		      "fldd 0(%1),%%fr0	\n\t"
1068c2ecf20Sopenharmony_ci		      : "=m" (s) : "r" (&s) : "r0");
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	printk("%s\n", level);
1098c2ecf20Sopenharmony_ci	printk("%s      VZOUICununcqcqcqcqcqcrmunTDVZOUI\n", level);
1108c2ecf20Sopenharmony_ci	printbinary(buf, s.sw[0], 32);
1118c2ecf20Sopenharmony_ci	printk("%sFPSR: %s\n", level, buf);
1128c2ecf20Sopenharmony_ci	printk("%sFPER1: %08x\n", level, s.sw[1]);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* here we'll print fr0 again, tho it'll be meaningless */
1158c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i += 4)
1168c2ecf20Sopenharmony_ci		PRINTREGS(level, regs->fr, "fr", FFMT, i);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_civoid show_regs(struct pt_regs *regs)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	int i, user;
1228c2ecf20Sopenharmony_ci	const char *level;
1238c2ecf20Sopenharmony_ci	unsigned long cr30, cr31;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	user = user_mode(regs);
1268c2ecf20Sopenharmony_ci	level = user ? KERN_DEBUG : KERN_CRIT;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	show_regs_print_info(level);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	print_gr(level, regs);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i += 4)
1338c2ecf20Sopenharmony_ci		PRINTREGS(level, regs->sr, "sr", RFMT, i);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (user)
1368c2ecf20Sopenharmony_ci		print_fr(level, regs);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	cr30 = mfctl(30);
1398c2ecf20Sopenharmony_ci	cr31 = mfctl(31);
1408c2ecf20Sopenharmony_ci	printk("%s\n", level);
1418c2ecf20Sopenharmony_ci	printk("%sIASQ: " RFMT " " RFMT " IAOQ: " RFMT " " RFMT "\n",
1428c2ecf20Sopenharmony_ci	       level, regs->iasq[0], regs->iasq[1], regs->iaoq[0], regs->iaoq[1]);
1438c2ecf20Sopenharmony_ci	printk("%s IIR: %08lx    ISR: " RFMT "  IOR: " RFMT "\n",
1448c2ecf20Sopenharmony_ci	       level, regs->iir, regs->isr, regs->ior);
1458c2ecf20Sopenharmony_ci	printk("%s CPU: %8d   CR30: " RFMT " CR31: " RFMT "\n",
1468c2ecf20Sopenharmony_ci	       level, current_thread_info()->cpu, cr30, cr31);
1478c2ecf20Sopenharmony_ci	printk("%s ORIG_R28: " RFMT "\n", level, regs->orig_r28);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (user) {
1508c2ecf20Sopenharmony_ci		printk("%s IAOQ[0]: " RFMT "\n", level, regs->iaoq[0]);
1518c2ecf20Sopenharmony_ci		printk("%s IAOQ[1]: " RFMT "\n", level, regs->iaoq[1]);
1528c2ecf20Sopenharmony_ci		printk("%s RP(r2): " RFMT "\n", level, regs->gr[2]);
1538c2ecf20Sopenharmony_ci	} else {
1548c2ecf20Sopenharmony_ci		printk("%s IAOQ[0]: %pS\n", level, (void *) regs->iaoq[0]);
1558c2ecf20Sopenharmony_ci		printk("%s IAOQ[1]: %pS\n", level, (void *) regs->iaoq[1]);
1568c2ecf20Sopenharmony_ci		printk("%s RP(r2): %pS\n", level, (void *) regs->gr[2]);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		parisc_show_stack(current, regs, KERN_DEFAULT);
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic DEFINE_RATELIMIT_STATE(_hppa_rs,
1638c2ecf20Sopenharmony_ci	DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci#define parisc_printk_ratelimited(critical, regs, fmt, ...)	{	      \
1668c2ecf20Sopenharmony_ci	if ((critical || show_unhandled_signals) && __ratelimit(&_hppa_rs)) { \
1678c2ecf20Sopenharmony_ci		printk(fmt, ##__VA_ARGS__);				      \
1688c2ecf20Sopenharmony_ci		show_regs(regs);					      \
1698c2ecf20Sopenharmony_ci	}								      \
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void do_show_stack(struct unwind_frame_info *info, const char *loglvl)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	int i = 1;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	printk("%sBacktrace:\n", loglvl);
1788c2ecf20Sopenharmony_ci	while (i <= MAX_UNWIND_ENTRIES) {
1798c2ecf20Sopenharmony_ci		if (unwind_once(info) < 0 || info->ip == 0)
1808c2ecf20Sopenharmony_ci			break;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		if (__kernel_text_address(info->ip)) {
1838c2ecf20Sopenharmony_ci			printk("%s [<" RFMT ">] %pS\n",
1848c2ecf20Sopenharmony_ci				loglvl, info->ip, (void *) info->ip);
1858c2ecf20Sopenharmony_ci			i++;
1868c2ecf20Sopenharmony_ci		}
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci	printk("%s\n", loglvl);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic void parisc_show_stack(struct task_struct *task,
1928c2ecf20Sopenharmony_ci	struct pt_regs *regs, const char *loglvl)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct unwind_frame_info info;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	unwind_frame_init_task(&info, task, regs);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	do_show_stack(&info, loglvl);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_civoid show_stack(struct task_struct *t, unsigned long *sp, const char *loglvl)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	parisc_show_stack(t, NULL, loglvl);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ciint is_valid_bugaddr(unsigned long iaoq)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	return 1;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_civoid die_if_kernel(char *str, struct pt_regs *regs, long err)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	if (user_mode(regs)) {
2148c2ecf20Sopenharmony_ci		if (err == 0)
2158c2ecf20Sopenharmony_ci			return; /* STFU */
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		parisc_printk_ratelimited(1, regs,
2188c2ecf20Sopenharmony_ci			KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n",
2198c2ecf20Sopenharmony_ci			current->comm, task_pid_nr(current), str, err, regs->iaoq[0]);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		return;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	bust_spinlocks(1);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	oops_enter();
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* Amuse the user in a SPARC fashion */
2298c2ecf20Sopenharmony_ci	if (err) printk(KERN_CRIT
2308c2ecf20Sopenharmony_ci			"      _______________________________ \n"
2318c2ecf20Sopenharmony_ci			"     < Your System ate a SPARC! Gah! >\n"
2328c2ecf20Sopenharmony_ci			"      ------------------------------- \n"
2338c2ecf20Sopenharmony_ci			"             \\   ^__^\n"
2348c2ecf20Sopenharmony_ci			"                 (__)\\       )\\/\\\n"
2358c2ecf20Sopenharmony_ci			"                  U  ||----w |\n"
2368c2ecf20Sopenharmony_ci			"                     ||     ||\n");
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* unlock the pdc lock if necessary */
2398c2ecf20Sopenharmony_ci	pdc_emergency_unlock();
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/* maybe the kernel hasn't booted very far yet and hasn't been able
2428c2ecf20Sopenharmony_ci	 * to initialize the serial or STI console. In that case we should
2438c2ecf20Sopenharmony_ci	 * re-enable the pdc console, so that the user will be able to
2448c2ecf20Sopenharmony_ci	 * identify the problem. */
2458c2ecf20Sopenharmony_ci	if (!console_drivers)
2468c2ecf20Sopenharmony_ci		pdc_console_restart();
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (err)
2498c2ecf20Sopenharmony_ci		printk(KERN_CRIT "%s (pid %d): %s (code %ld)\n",
2508c2ecf20Sopenharmony_ci			current->comm, task_pid_nr(current), str, err);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* Wot's wrong wif bein' racy? */
2538c2ecf20Sopenharmony_ci	if (current->thread.flags & PARISC_KERNEL_DEATH) {
2548c2ecf20Sopenharmony_ci		printk(KERN_CRIT "%s() recursion detected.\n", __func__);
2558c2ecf20Sopenharmony_ci		local_irq_enable();
2568c2ecf20Sopenharmony_ci		while (1);
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci	current->thread.flags |= PARISC_KERNEL_DEATH;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	show_regs(regs);
2618c2ecf20Sopenharmony_ci	dump_stack();
2628c2ecf20Sopenharmony_ci	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (in_interrupt())
2658c2ecf20Sopenharmony_ci		panic("Fatal exception in interrupt");
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (panic_on_oops)
2688c2ecf20Sopenharmony_ci		panic("Fatal exception");
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	oops_exit();
2718c2ecf20Sopenharmony_ci	make_task_dead(SIGSEGV);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/* gdb uses break 4,8 */
2758c2ecf20Sopenharmony_ci#define GDB_BREAK_INSN 0x10004
2768c2ecf20Sopenharmony_cistatic void handle_gdb_break(struct pt_regs *regs, int wot)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	force_sig_fault(SIGTRAP, wot,
2798c2ecf20Sopenharmony_ci			(void __user *) (regs->iaoq[0] & ~3));
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic void handle_break(struct pt_regs *regs)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	unsigned iir = regs->iir;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (unlikely(iir == PARISC_BUG_BREAK_INSN && !user_mode(regs))) {
2878c2ecf20Sopenharmony_ci		/* check if a BUG() or WARN() trapped here.  */
2888c2ecf20Sopenharmony_ci		enum bug_trap_type tt;
2898c2ecf20Sopenharmony_ci		tt = report_bug(regs->iaoq[0] & ~3, regs);
2908c2ecf20Sopenharmony_ci		if (tt == BUG_TRAP_TYPE_WARN) {
2918c2ecf20Sopenharmony_ci			regs->iaoq[0] += 4;
2928c2ecf20Sopenharmony_ci			regs->iaoq[1] += 4;
2938c2ecf20Sopenharmony_ci			return; /* return to next instruction when WARN_ON().  */
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci		die_if_kernel("Unknown kernel breakpoint", regs,
2968c2ecf20Sopenharmony_ci			(tt == BUG_TRAP_TYPE_NONE) ? 9 : 0);
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci#ifdef CONFIG_KPROBES
3008c2ecf20Sopenharmony_ci	if (unlikely(iir == PARISC_KPROBES_BREAK_INSN)) {
3018c2ecf20Sopenharmony_ci		parisc_kprobe_break_handler(regs);
3028c2ecf20Sopenharmony_ci		return;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci#endif
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci#ifdef CONFIG_KGDB
3088c2ecf20Sopenharmony_ci	if (unlikely((iir == PARISC_KGDB_COMPILED_BREAK_INSN ||
3098c2ecf20Sopenharmony_ci		iir == PARISC_KGDB_BREAK_INSN)) && !user_mode(regs)) {
3108c2ecf20Sopenharmony_ci		kgdb_handle_exception(9, SIGTRAP, 0, regs);
3118c2ecf20Sopenharmony_ci		return;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci#endif
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (unlikely(iir != GDB_BREAK_INSN))
3168c2ecf20Sopenharmony_ci		parisc_printk_ratelimited(0, regs,
3178c2ecf20Sopenharmony_ci			KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",
3188c2ecf20Sopenharmony_ci			iir & 31, (iir>>13) & ((1<<13)-1),
3198c2ecf20Sopenharmony_ci			task_pid_nr(current), current->comm);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* send standard GDB signal */
3228c2ecf20Sopenharmony_ci	handle_gdb_break(regs, TRAP_BRKPT);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void default_trap(int code, struct pt_regs *regs)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	printk(KERN_ERR "Trap %d on CPU %d\n", code, smp_processor_id());
3288c2ecf20Sopenharmony_ci	show_regs(regs);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_civoid (*cpu_lpmc) (int code, struct pt_regs *regs) __read_mostly = default_trap;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_civoid transfer_pim_to_trap_frame(struct pt_regs *regs)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci    register int i;
3378c2ecf20Sopenharmony_ci    extern unsigned int hpmc_pim_data[];
3388c2ecf20Sopenharmony_ci    struct pdc_hpmc_pim_11 *pim_narrow;
3398c2ecf20Sopenharmony_ci    struct pdc_hpmc_pim_20 *pim_wide;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci    if (boot_cpu_data.cpu_type >= pcxu) {
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	pim_wide = (struct pdc_hpmc_pim_20 *)hpmc_pim_data;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/*
3468c2ecf20Sopenharmony_ci	 * Note: The following code will probably generate a
3478c2ecf20Sopenharmony_ci	 * bunch of truncation error warnings from the compiler.
3488c2ecf20Sopenharmony_ci	 * Could be handled with an ifdef, but perhaps there
3498c2ecf20Sopenharmony_ci	 * is a better way.
3508c2ecf20Sopenharmony_ci	 */
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	regs->gr[0] = pim_wide->cr[22];
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	for (i = 1; i < 32; i++)
3558c2ecf20Sopenharmony_ci	    regs->gr[i] = pim_wide->gr[i];
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i++)
3588c2ecf20Sopenharmony_ci	    regs->fr[i] = pim_wide->fr[i];
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
3618c2ecf20Sopenharmony_ci	    regs->sr[i] = pim_wide->sr[i];
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	regs->iasq[0] = pim_wide->cr[17];
3648c2ecf20Sopenharmony_ci	regs->iasq[1] = pim_wide->iasq_back;
3658c2ecf20Sopenharmony_ci	regs->iaoq[0] = pim_wide->cr[18];
3668c2ecf20Sopenharmony_ci	regs->iaoq[1] = pim_wide->iaoq_back;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	regs->sar  = pim_wide->cr[11];
3698c2ecf20Sopenharmony_ci	regs->iir  = pim_wide->cr[19];
3708c2ecf20Sopenharmony_ci	regs->isr  = pim_wide->cr[20];
3718c2ecf20Sopenharmony_ci	regs->ior  = pim_wide->cr[21];
3728c2ecf20Sopenharmony_ci    }
3738c2ecf20Sopenharmony_ci    else {
3748c2ecf20Sopenharmony_ci	pim_narrow = (struct pdc_hpmc_pim_11 *)hpmc_pim_data;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	regs->gr[0] = pim_narrow->cr[22];
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	for (i = 1; i < 32; i++)
3798c2ecf20Sopenharmony_ci	    regs->gr[i] = pim_narrow->gr[i];
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i++)
3828c2ecf20Sopenharmony_ci	    regs->fr[i] = pim_narrow->fr[i];
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
3858c2ecf20Sopenharmony_ci	    regs->sr[i] = pim_narrow->sr[i];
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	regs->iasq[0] = pim_narrow->cr[17];
3888c2ecf20Sopenharmony_ci	regs->iasq[1] = pim_narrow->iasq_back;
3898c2ecf20Sopenharmony_ci	regs->iaoq[0] = pim_narrow->cr[18];
3908c2ecf20Sopenharmony_ci	regs->iaoq[1] = pim_narrow->iaoq_back;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	regs->sar  = pim_narrow->cr[11];
3938c2ecf20Sopenharmony_ci	regs->iir  = pim_narrow->cr[19];
3948c2ecf20Sopenharmony_ci	regs->isr  = pim_narrow->cr[20];
3958c2ecf20Sopenharmony_ci	regs->ior  = pim_narrow->cr[21];
3968c2ecf20Sopenharmony_ci    }
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci    /*
3998c2ecf20Sopenharmony_ci     * The following fields only have meaning if we came through
4008c2ecf20Sopenharmony_ci     * another path. So just zero them here.
4018c2ecf20Sopenharmony_ci     */
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci    regs->ksp = 0;
4048c2ecf20Sopenharmony_ci    regs->kpc = 0;
4058c2ecf20Sopenharmony_ci    regs->orig_r28 = 0;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci/*
4108c2ecf20Sopenharmony_ci * This routine is called as a last resort when everything else
4118c2ecf20Sopenharmony_ci * has gone clearly wrong. We get called for faults in kernel space,
4128c2ecf20Sopenharmony_ci * and HPMC's.
4138c2ecf20Sopenharmony_ci */
4148c2ecf20Sopenharmony_civoid parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long offset)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	static DEFINE_SPINLOCK(terminate_lock);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	(void)notify_die(DIE_OOPS, msg, regs, 0, code, SIGTRAP);
4198c2ecf20Sopenharmony_ci	bust_spinlocks(1);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	set_eiem(0);
4228c2ecf20Sopenharmony_ci	local_irq_disable();
4238c2ecf20Sopenharmony_ci	spin_lock(&terminate_lock);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* unlock the pdc lock if necessary */
4268c2ecf20Sopenharmony_ci	pdc_emergency_unlock();
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* restart pdc console if necessary */
4298c2ecf20Sopenharmony_ci	if (!console_drivers)
4308c2ecf20Sopenharmony_ci		pdc_console_restart();
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* Not all paths will gutter the processor... */
4338c2ecf20Sopenharmony_ci	switch(code){
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	case 1:
4368c2ecf20Sopenharmony_ci		transfer_pim_to_trap_frame(regs);
4378c2ecf20Sopenharmony_ci		break;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	default:
4408c2ecf20Sopenharmony_ci		break;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	{
4458c2ecf20Sopenharmony_ci		/* show_stack(NULL, (unsigned long *)regs->gr[30]); */
4468c2ecf20Sopenharmony_ci		struct unwind_frame_info info;
4478c2ecf20Sopenharmony_ci		unwind_frame_init(&info, current, regs);
4488c2ecf20Sopenharmony_ci		do_show_stack(&info, KERN_CRIT);
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	printk("\n");
4528c2ecf20Sopenharmony_ci	pr_crit("%s: Code=%d (%s) at addr " RFMT "\n",
4538c2ecf20Sopenharmony_ci		msg, code, trap_name(code), offset);
4548c2ecf20Sopenharmony_ci	show_regs(regs);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	spin_unlock(&terminate_lock);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* put soft power button back under hardware control;
4598c2ecf20Sopenharmony_ci	 * if the user had pressed it once at any time, the
4608c2ecf20Sopenharmony_ci	 * system will shut down immediately right here. */
4618c2ecf20Sopenharmony_ci	pdc_soft_power_button(0);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* Call kernel panic() so reboot timeouts work properly
4648c2ecf20Sopenharmony_ci	 * FIXME: This function should be on the list of
4658c2ecf20Sopenharmony_ci	 * panic notifiers, and we should call panic
4668c2ecf20Sopenharmony_ci	 * directly from the location that we wish.
4678c2ecf20Sopenharmony_ci	 * e.g. We should not call panic from
4688c2ecf20Sopenharmony_ci	 * parisc_terminate, but rather the oter way around.
4698c2ecf20Sopenharmony_ci	 * This hack works, prints the panic message twice,
4708c2ecf20Sopenharmony_ci	 * and it enables reboot timers!
4718c2ecf20Sopenharmony_ci	 */
4728c2ecf20Sopenharmony_ci	panic(msg);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_civoid notrace handle_interruption(int code, struct pt_regs *regs)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	unsigned long fault_address = 0;
4788c2ecf20Sopenharmony_ci	unsigned long fault_space = 0;
4798c2ecf20Sopenharmony_ci	int si_code;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (code == 1)
4828c2ecf20Sopenharmony_ci	    pdc_console_restart();  /* switch back to pdc if HPMC */
4838c2ecf20Sopenharmony_ci	else
4848c2ecf20Sopenharmony_ci	    local_irq_enable();
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* Security check:
4878c2ecf20Sopenharmony_ci	 * If the priority level is still user, and the
4888c2ecf20Sopenharmony_ci	 * faulting space is not equal to the active space
4898c2ecf20Sopenharmony_ci	 * then the user is attempting something in a space
4908c2ecf20Sopenharmony_ci	 * that does not belong to them. Kill the process.
4918c2ecf20Sopenharmony_ci	 *
4928c2ecf20Sopenharmony_ci	 * This is normally the situation when the user
4938c2ecf20Sopenharmony_ci	 * attempts to jump into the kernel space at the
4948c2ecf20Sopenharmony_ci	 * wrong offset, be it at the gateway page or a
4958c2ecf20Sopenharmony_ci	 * random location.
4968c2ecf20Sopenharmony_ci	 *
4978c2ecf20Sopenharmony_ci	 * We cannot normally signal the process because it
4988c2ecf20Sopenharmony_ci	 * could *be* on the gateway page, and processes
4998c2ecf20Sopenharmony_ci	 * executing on the gateway page can't have signals
5008c2ecf20Sopenharmony_ci	 * delivered.
5018c2ecf20Sopenharmony_ci	 *
5028c2ecf20Sopenharmony_ci	 * We merely readjust the address into the users
5038c2ecf20Sopenharmony_ci	 * space, at a destination address of zero, and
5048c2ecf20Sopenharmony_ci	 * allow processing to continue.
5058c2ecf20Sopenharmony_ci	 */
5068c2ecf20Sopenharmony_ci	if (((unsigned long)regs->iaoq[0] & 3) &&
5078c2ecf20Sopenharmony_ci	    ((unsigned long)regs->iasq[0] != (unsigned long)regs->sr[7])) {
5088c2ecf20Sopenharmony_ci		/* Kill the user process later */
5098c2ecf20Sopenharmony_ci		regs->iaoq[0] = 0 | 3;
5108c2ecf20Sopenharmony_ci		regs->iaoq[1] = regs->iaoq[0] + 4;
5118c2ecf20Sopenharmony_ci		regs->iasq[0] = regs->iasq[1] = regs->sr[7];
5128c2ecf20Sopenharmony_ci		regs->gr[0] &= ~PSW_B;
5138c2ecf20Sopenharmony_ci		return;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci#if 0
5178c2ecf20Sopenharmony_ci	printk(KERN_CRIT "Interruption # %d\n", code);
5188c2ecf20Sopenharmony_ci#endif
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	switch(code) {
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	case  1:
5238c2ecf20Sopenharmony_ci		/* High-priority machine check (HPMC) */
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		/* set up a new led state on systems shipped with a LED State panel */
5268c2ecf20Sopenharmony_ci		pdc_chassis_send_status(PDC_CHASSIS_DIRECT_HPMC);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		parisc_terminate("High Priority Machine Check (HPMC)",
5298c2ecf20Sopenharmony_ci				regs, code, 0);
5308c2ecf20Sopenharmony_ci		/* NOT REACHED */
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	case  2:
5338c2ecf20Sopenharmony_ci		/* Power failure interrupt */
5348c2ecf20Sopenharmony_ci		printk(KERN_CRIT "Power failure interrupt !\n");
5358c2ecf20Sopenharmony_ci		return;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	case  3:
5388c2ecf20Sopenharmony_ci		/* Recovery counter trap */
5398c2ecf20Sopenharmony_ci		regs->gr[0] &= ~PSW_R;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci#ifdef CONFIG_KPROBES
5428c2ecf20Sopenharmony_ci		if (parisc_kprobe_ss_handler(regs))
5438c2ecf20Sopenharmony_ci			return;
5448c2ecf20Sopenharmony_ci#endif
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci#ifdef CONFIG_KGDB
5478c2ecf20Sopenharmony_ci		if (kgdb_single_step) {
5488c2ecf20Sopenharmony_ci			kgdb_handle_exception(0, SIGTRAP, 0, regs);
5498c2ecf20Sopenharmony_ci			return;
5508c2ecf20Sopenharmony_ci		}
5518c2ecf20Sopenharmony_ci#endif
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		if (user_space(regs))
5548c2ecf20Sopenharmony_ci			handle_gdb_break(regs, TRAP_TRACE);
5558c2ecf20Sopenharmony_ci		/* else this must be the start of a syscall - just let it run */
5568c2ecf20Sopenharmony_ci		return;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	case  5:
5598c2ecf20Sopenharmony_ci		/* Low-priority machine check */
5608c2ecf20Sopenharmony_ci		pdc_chassis_send_status(PDC_CHASSIS_DIRECT_LPMC);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci		flush_cache_all();
5638c2ecf20Sopenharmony_ci		flush_tlb_all();
5648c2ecf20Sopenharmony_ci		cpu_lpmc(5, regs);
5658c2ecf20Sopenharmony_ci		return;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	case  PARISC_ITLB_TRAP:
5688c2ecf20Sopenharmony_ci		/* Instruction TLB miss fault/Instruction page fault */
5698c2ecf20Sopenharmony_ci		fault_address = regs->iaoq[0];
5708c2ecf20Sopenharmony_ci		fault_space   = regs->iasq[0];
5718c2ecf20Sopenharmony_ci		break;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	case  8:
5748c2ecf20Sopenharmony_ci		/* Illegal instruction trap */
5758c2ecf20Sopenharmony_ci		die_if_kernel("Illegal instruction", regs, code);
5768c2ecf20Sopenharmony_ci		si_code = ILL_ILLOPC;
5778c2ecf20Sopenharmony_ci		goto give_sigill;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	case  9:
5808c2ecf20Sopenharmony_ci		/* Break instruction trap */
5818c2ecf20Sopenharmony_ci		handle_break(regs);
5828c2ecf20Sopenharmony_ci		return;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	case 10:
5858c2ecf20Sopenharmony_ci		/* Privileged operation trap */
5868c2ecf20Sopenharmony_ci		die_if_kernel("Privileged operation", regs, code);
5878c2ecf20Sopenharmony_ci		si_code = ILL_PRVOPC;
5888c2ecf20Sopenharmony_ci		goto give_sigill;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	case 11:
5918c2ecf20Sopenharmony_ci		/* Privileged register trap */
5928c2ecf20Sopenharmony_ci		if ((regs->iir & 0xffdfffe0) == 0x034008a0) {
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci			/* This is a MFCTL cr26/cr27 to gr instruction.
5958c2ecf20Sopenharmony_ci			 * PCXS traps on this, so we need to emulate it.
5968c2ecf20Sopenharmony_ci			 */
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci			if (regs->iir & 0x00200000)
5998c2ecf20Sopenharmony_ci				regs->gr[regs->iir & 0x1f] = mfctl(27);
6008c2ecf20Sopenharmony_ci			else
6018c2ecf20Sopenharmony_ci				regs->gr[regs->iir & 0x1f] = mfctl(26);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci			regs->iaoq[0] = regs->iaoq[1];
6048c2ecf20Sopenharmony_ci			regs->iaoq[1] += 4;
6058c2ecf20Sopenharmony_ci			regs->iasq[0] = regs->iasq[1];
6068c2ecf20Sopenharmony_ci			return;
6078c2ecf20Sopenharmony_ci		}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci		die_if_kernel("Privileged register usage", regs, code);
6108c2ecf20Sopenharmony_ci		si_code = ILL_PRVREG;
6118c2ecf20Sopenharmony_ci	give_sigill:
6128c2ecf20Sopenharmony_ci		force_sig_fault(SIGILL, si_code,
6138c2ecf20Sopenharmony_ci				(void __user *) regs->iaoq[0]);
6148c2ecf20Sopenharmony_ci		return;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	case 12:
6178c2ecf20Sopenharmony_ci		/* Overflow Trap, let the userland signal handler do the cleanup */
6188c2ecf20Sopenharmony_ci		force_sig_fault(SIGFPE, FPE_INTOVF,
6198c2ecf20Sopenharmony_ci				(void __user *) regs->iaoq[0]);
6208c2ecf20Sopenharmony_ci		return;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	case 13:
6238c2ecf20Sopenharmony_ci		/* Conditional Trap
6248c2ecf20Sopenharmony_ci		   The condition succeeds in an instruction which traps
6258c2ecf20Sopenharmony_ci		   on condition  */
6268c2ecf20Sopenharmony_ci		if(user_mode(regs)){
6278c2ecf20Sopenharmony_ci			/* Let userspace app figure it out from the insn pointed
6288c2ecf20Sopenharmony_ci			 * to by si_addr.
6298c2ecf20Sopenharmony_ci			 */
6308c2ecf20Sopenharmony_ci			force_sig_fault(SIGFPE, FPE_CONDTRAP,
6318c2ecf20Sopenharmony_ci					(void __user *) regs->iaoq[0]);
6328c2ecf20Sopenharmony_ci			return;
6338c2ecf20Sopenharmony_ci		}
6348c2ecf20Sopenharmony_ci		/* The kernel doesn't want to handle condition codes */
6358c2ecf20Sopenharmony_ci		break;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	case 14:
6388c2ecf20Sopenharmony_ci		/* Assist Exception Trap, i.e. floating point exception. */
6398c2ecf20Sopenharmony_ci		die_if_kernel("Floating point exception", regs, 0); /* quiet */
6408c2ecf20Sopenharmony_ci		__inc_irq_stat(irq_fpassist_count);
6418c2ecf20Sopenharmony_ci		handle_fpe(regs);
6428c2ecf20Sopenharmony_ci		return;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	case 15:
6458c2ecf20Sopenharmony_ci		/* Data TLB miss fault/Data page fault */
6468c2ecf20Sopenharmony_ci		fallthrough;
6478c2ecf20Sopenharmony_ci	case 16:
6488c2ecf20Sopenharmony_ci		/* Non-access instruction TLB miss fault */
6498c2ecf20Sopenharmony_ci		/* The instruction TLB entry needed for the target address of the FIC
6508c2ecf20Sopenharmony_ci		   is absent, and hardware can't find it, so we get to cleanup */
6518c2ecf20Sopenharmony_ci		fallthrough;
6528c2ecf20Sopenharmony_ci	case 17:
6538c2ecf20Sopenharmony_ci		/* Non-access data TLB miss fault/Non-access data page fault */
6548c2ecf20Sopenharmony_ci		/* FIXME:
6558c2ecf20Sopenharmony_ci			 Still need to add slow path emulation code here!
6568c2ecf20Sopenharmony_ci			 If the insn used a non-shadow register, then the tlb
6578c2ecf20Sopenharmony_ci			 handlers could not have their side-effect (e.g. probe
6588c2ecf20Sopenharmony_ci			 writing to a target register) emulated since rfir would
6598c2ecf20Sopenharmony_ci			 erase the changes to said register. Instead we have to
6608c2ecf20Sopenharmony_ci			 setup everything, call this function we are in, and emulate
6618c2ecf20Sopenharmony_ci			 by hand. Technically we need to emulate:
6628c2ecf20Sopenharmony_ci			 fdc,fdce,pdc,"fic,4f",prober,probeir,probew, probeiw
6638c2ecf20Sopenharmony_ci		*/
6648c2ecf20Sopenharmony_ci		if (code == 17 && handle_nadtlb_fault(regs))
6658c2ecf20Sopenharmony_ci			return;
6668c2ecf20Sopenharmony_ci		fault_address = regs->ior;
6678c2ecf20Sopenharmony_ci		fault_space = regs->isr;
6688c2ecf20Sopenharmony_ci		break;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	case 18:
6718c2ecf20Sopenharmony_ci		/* PCXS only -- later cpu's split this into types 26,27 & 28 */
6728c2ecf20Sopenharmony_ci		/* Check for unaligned access */
6738c2ecf20Sopenharmony_ci		if (check_unaligned(regs)) {
6748c2ecf20Sopenharmony_ci			handle_unaligned(regs);
6758c2ecf20Sopenharmony_ci			return;
6768c2ecf20Sopenharmony_ci		}
6778c2ecf20Sopenharmony_ci		fallthrough;
6788c2ecf20Sopenharmony_ci	case 26:
6798c2ecf20Sopenharmony_ci		/* PCXL: Data memory access rights trap */
6808c2ecf20Sopenharmony_ci		fault_address = regs->ior;
6818c2ecf20Sopenharmony_ci		fault_space   = regs->isr;
6828c2ecf20Sopenharmony_ci		break;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	case 19:
6858c2ecf20Sopenharmony_ci		/* Data memory break trap */
6868c2ecf20Sopenharmony_ci		regs->gr[0] |= PSW_X; /* So we can single-step over the trap */
6878c2ecf20Sopenharmony_ci		fallthrough;
6888c2ecf20Sopenharmony_ci	case 21:
6898c2ecf20Sopenharmony_ci		/* Page reference trap */
6908c2ecf20Sopenharmony_ci		handle_gdb_break(regs, TRAP_HWBKPT);
6918c2ecf20Sopenharmony_ci		return;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	case 25:
6948c2ecf20Sopenharmony_ci		/* Taken branch trap */
6958c2ecf20Sopenharmony_ci		regs->gr[0] &= ~PSW_T;
6968c2ecf20Sopenharmony_ci		if (user_space(regs))
6978c2ecf20Sopenharmony_ci			handle_gdb_break(regs, TRAP_BRANCH);
6988c2ecf20Sopenharmony_ci		/* else this must be the start of a syscall - just let it
6998c2ecf20Sopenharmony_ci		 * run.
7008c2ecf20Sopenharmony_ci		 */
7018c2ecf20Sopenharmony_ci		return;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	case  7:
7048c2ecf20Sopenharmony_ci		/* Instruction access rights */
7058c2ecf20Sopenharmony_ci		/* PCXL: Instruction memory protection trap */
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		/*
7088c2ecf20Sopenharmony_ci		 * This could be caused by either: 1) a process attempting
7098c2ecf20Sopenharmony_ci		 * to execute within a vma that does not have execute
7108c2ecf20Sopenharmony_ci		 * permission, or 2) an access rights violation caused by a
7118c2ecf20Sopenharmony_ci		 * flush only translation set up by ptep_get_and_clear().
7128c2ecf20Sopenharmony_ci		 * So we check the vma permissions to differentiate the two.
7138c2ecf20Sopenharmony_ci		 * If the vma indicates we have execute permission, then
7148c2ecf20Sopenharmony_ci		 * the cause is the latter one. In this case, we need to
7158c2ecf20Sopenharmony_ci		 * call do_page_fault() to fix the problem.
7168c2ecf20Sopenharmony_ci		 */
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci		if (user_mode(regs)) {
7198c2ecf20Sopenharmony_ci			struct vm_area_struct *vma;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci			mmap_read_lock(current->mm);
7228c2ecf20Sopenharmony_ci			vma = find_vma(current->mm,regs->iaoq[0]);
7238c2ecf20Sopenharmony_ci			if (vma && (regs->iaoq[0] >= vma->vm_start)
7248c2ecf20Sopenharmony_ci				&& (vma->vm_flags & VM_EXEC)) {
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci				fault_address = regs->iaoq[0];
7278c2ecf20Sopenharmony_ci				fault_space = regs->iasq[0];
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci				mmap_read_unlock(current->mm);
7308c2ecf20Sopenharmony_ci				break; /* call do_page_fault() */
7318c2ecf20Sopenharmony_ci			}
7328c2ecf20Sopenharmony_ci			mmap_read_unlock(current->mm);
7338c2ecf20Sopenharmony_ci		}
7348c2ecf20Sopenharmony_ci		/* CPU could not fetch instruction, so clear stale IIR value. */
7358c2ecf20Sopenharmony_ci		regs->iir = 0xbaadf00d;
7368c2ecf20Sopenharmony_ci		fallthrough;
7378c2ecf20Sopenharmony_ci	case 27:
7388c2ecf20Sopenharmony_ci		/* Data memory protection ID trap */
7398c2ecf20Sopenharmony_ci		if (code == 27 && !user_mode(regs) &&
7408c2ecf20Sopenharmony_ci			fixup_exception(regs))
7418c2ecf20Sopenharmony_ci			return;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci		die_if_kernel("Protection id trap", regs, code);
7448c2ecf20Sopenharmony_ci		force_sig_fault(SIGSEGV, SEGV_MAPERR,
7458c2ecf20Sopenharmony_ci				(code == 7)?
7468c2ecf20Sopenharmony_ci				((void __user *) regs->iaoq[0]) :
7478c2ecf20Sopenharmony_ci				((void __user *) regs->ior));
7488c2ecf20Sopenharmony_ci		return;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	case 28:
7518c2ecf20Sopenharmony_ci		/* Unaligned data reference trap */
7528c2ecf20Sopenharmony_ci		handle_unaligned(regs);
7538c2ecf20Sopenharmony_ci		return;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	default:
7568c2ecf20Sopenharmony_ci		if (user_mode(regs)) {
7578c2ecf20Sopenharmony_ci			parisc_printk_ratelimited(0, regs, KERN_DEBUG
7588c2ecf20Sopenharmony_ci				"handle_interruption() pid=%d command='%s'\n",
7598c2ecf20Sopenharmony_ci				task_pid_nr(current), current->comm);
7608c2ecf20Sopenharmony_ci			/* SIGBUS, for lack of a better one. */
7618c2ecf20Sopenharmony_ci			force_sig_fault(SIGBUS, BUS_OBJERR,
7628c2ecf20Sopenharmony_ci					(void __user *)regs->ior);
7638c2ecf20Sopenharmony_ci			return;
7648c2ecf20Sopenharmony_ci		}
7658c2ecf20Sopenharmony_ci		pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci		parisc_terminate("Unexpected interruption", regs, code, 0);
7688c2ecf20Sopenharmony_ci		/* NOT REACHED */
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (user_mode(regs)) {
7728c2ecf20Sopenharmony_ci	    if ((fault_space >> SPACEID_SHIFT) != (regs->sr[7] >> SPACEID_SHIFT)) {
7738c2ecf20Sopenharmony_ci		parisc_printk_ratelimited(0, regs, KERN_DEBUG
7748c2ecf20Sopenharmony_ci				"User fault %d on space 0x%08lx, pid=%d command='%s'\n",
7758c2ecf20Sopenharmony_ci				code, fault_space,
7768c2ecf20Sopenharmony_ci				task_pid_nr(current), current->comm);
7778c2ecf20Sopenharmony_ci		force_sig_fault(SIGSEGV, SEGV_MAPERR,
7788c2ecf20Sopenharmony_ci				(void __user *)regs->ior);
7798c2ecf20Sopenharmony_ci		return;
7808c2ecf20Sopenharmony_ci	    }
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci	else {
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	    /*
7858c2ecf20Sopenharmony_ci	     * The kernel should never fault on its own address space,
7868c2ecf20Sopenharmony_ci	     * unless pagefault_disable() was called before.
7878c2ecf20Sopenharmony_ci	     */
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	    if (faulthandler_disabled() || fault_space == 0)
7908c2ecf20Sopenharmony_ci	    {
7918c2ecf20Sopenharmony_ci		/* Clean up and return if in exception table. */
7928c2ecf20Sopenharmony_ci		if (fixup_exception(regs))
7938c2ecf20Sopenharmony_ci			return;
7948c2ecf20Sopenharmony_ci		pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC);
7958c2ecf20Sopenharmony_ci		parisc_terminate("Kernel Fault", regs, code, fault_address);
7968c2ecf20Sopenharmony_ci	    }
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	do_page_fault(regs, code, fault_address);
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_civoid __init initialize_ivt(const void *iva)
8048c2ecf20Sopenharmony_ci{
8058c2ecf20Sopenharmony_ci	extern u32 os_hpmc_size;
8068c2ecf20Sopenharmony_ci	extern const u32 os_hpmc[];
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	int i;
8098c2ecf20Sopenharmony_ci	u32 check = 0;
8108c2ecf20Sopenharmony_ci	u32 *ivap;
8118c2ecf20Sopenharmony_ci	u32 *hpmcp;
8128c2ecf20Sopenharmony_ci	u32 length, instr;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	if (strcmp((const char *)iva, "cows can fly"))
8158c2ecf20Sopenharmony_ci		panic("IVT invalid");
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	ivap = (u32 *)iva;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
8208c2ecf20Sopenharmony_ci	    *ivap++ = 0;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	/*
8238c2ecf20Sopenharmony_ci	 * Use PDC_INSTR firmware function to get instruction that invokes
8248c2ecf20Sopenharmony_ci	 * PDCE_CHECK in HPMC handler.  See programming note at page 1-31 of
8258c2ecf20Sopenharmony_ci	 * the PA 1.1 Firmware Architecture document.
8268c2ecf20Sopenharmony_ci	 */
8278c2ecf20Sopenharmony_ci	if (pdc_instr(&instr) == PDC_OK)
8288c2ecf20Sopenharmony_ci		ivap[0] = instr;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/*
8318c2ecf20Sopenharmony_ci	 * Rules for the checksum of the HPMC handler:
8328c2ecf20Sopenharmony_ci	 * 1. The IVA does not point to PDC/PDH space (ie: the OS has installed
8338c2ecf20Sopenharmony_ci	 *    its own IVA).
8348c2ecf20Sopenharmony_ci	 * 2. The word at IVA + 32 is nonzero.
8358c2ecf20Sopenharmony_ci	 * 3. If Length (IVA + 60) is not zero, then Length (IVA + 60) and
8368c2ecf20Sopenharmony_ci	 *    Address (IVA + 56) are word-aligned.
8378c2ecf20Sopenharmony_ci	 * 4. The checksum of the 8 words starting at IVA + 32 plus the sum of
8388c2ecf20Sopenharmony_ci	 *    the Length/4 words starting at Address is zero.
8398c2ecf20Sopenharmony_ci	 */
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	/* Setup IVA and compute checksum for HPMC handler */
8428c2ecf20Sopenharmony_ci	ivap[6] = (u32)__pa(os_hpmc);
8438c2ecf20Sopenharmony_ci	length = os_hpmc_size;
8448c2ecf20Sopenharmony_ci	ivap[7] = length;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	hpmcp = (u32 *)os_hpmc;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	for (i=0; i<length/4; i++)
8498c2ecf20Sopenharmony_ci	    check += *hpmcp++;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	for (i=0; i<8; i++)
8528c2ecf20Sopenharmony_ci	    check += ivap[i];
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	ivap[5] = -check;
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci/* early_trap_init() is called before we set up kernel mappings and
8598c2ecf20Sopenharmony_ci * write-protect the kernel */
8608c2ecf20Sopenharmony_civoid  __init early_trap_init(void)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	extern const void fault_vector_20;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci#ifndef CONFIG_64BIT
8658c2ecf20Sopenharmony_ci	extern const void fault_vector_11;
8668c2ecf20Sopenharmony_ci	initialize_ivt(&fault_vector_11);
8678c2ecf20Sopenharmony_ci#endif
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	initialize_ivt(&fault_vector_20);
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_civoid __init trap_init(void)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci}
875