162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Architecture-specific trap handling.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1998-2003 Hewlett-Packard Co
662306a36Sopenharmony_ci *	David Mosberger-Tang <davidm@hpl.hp.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * 05/12/00 grao <goutham.rao@intel.com> : added isr in siginfo for SIGFPE
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/sched/signal.h>
1462306a36Sopenharmony_ci#include <linux/sched/debug.h>
1562306a36Sopenharmony_ci#include <linux/tty.h>
1662306a36Sopenharmony_ci#include <linux/vt_kern.h>		/* For unblank_screen() */
1762306a36Sopenharmony_ci#include <linux/export.h>
1862306a36Sopenharmony_ci#include <linux/extable.h>
1962306a36Sopenharmony_ci#include <linux/hardirq.h>
2062306a36Sopenharmony_ci#include <linux/kprobes.h>
2162306a36Sopenharmony_ci#include <linux/delay.h>		/* for ssleep() */
2262306a36Sopenharmony_ci#include <linux/kdebug.h>
2362306a36Sopenharmony_ci#include <linux/uaccess.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <asm/fpswa.h>
2662306a36Sopenharmony_ci#include <asm/intrinsics.h>
2762306a36Sopenharmony_ci#include <asm/processor.h>
2862306a36Sopenharmony_ci#include <asm/exception.h>
2962306a36Sopenharmony_ci#include <asm/setup.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cifpswa_interface_t *fpswa_interface;
3262306a36Sopenharmony_ciEXPORT_SYMBOL(fpswa_interface);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_civoid __init
3562306a36Sopenharmony_citrap_init (void)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	if (ia64_boot_param->fpswa)
3862306a36Sopenharmony_ci		/* FPSWA fixup: make the interface pointer a kernel virtual address: */
3962306a36Sopenharmony_ci		fpswa_interface = __va(ia64_boot_param->fpswa);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciint
4362306a36Sopenharmony_cidie (const char *str, struct pt_regs *regs, long err)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	static struct {
4662306a36Sopenharmony_ci		spinlock_t lock;
4762306a36Sopenharmony_ci		u32 lock_owner;
4862306a36Sopenharmony_ci		int lock_owner_depth;
4962306a36Sopenharmony_ci	} die = {
5062306a36Sopenharmony_ci		.lock =	__SPIN_LOCK_UNLOCKED(die.lock),
5162306a36Sopenharmony_ci		.lock_owner = -1,
5262306a36Sopenharmony_ci		.lock_owner_depth = 0
5362306a36Sopenharmony_ci	};
5462306a36Sopenharmony_ci	static int die_counter;
5562306a36Sopenharmony_ci	int cpu = get_cpu();
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (die.lock_owner != cpu) {
5862306a36Sopenharmony_ci		console_verbose();
5962306a36Sopenharmony_ci		spin_lock_irq(&die.lock);
6062306a36Sopenharmony_ci		die.lock_owner = cpu;
6162306a36Sopenharmony_ci		die.lock_owner_depth = 0;
6262306a36Sopenharmony_ci		bust_spinlocks(1);
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci	put_cpu();
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (++die.lock_owner_depth < 3) {
6762306a36Sopenharmony_ci		printk("%s[%d]: %s %ld [%d]\n",
6862306a36Sopenharmony_ci		current->comm, task_pid_nr(current), str, err, ++die_counter);
6962306a36Sopenharmony_ci		if (notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV)
7062306a36Sopenharmony_ci	            != NOTIFY_STOP)
7162306a36Sopenharmony_ci			show_regs(regs);
7262306a36Sopenharmony_ci		else
7362306a36Sopenharmony_ci			regs = NULL;
7462306a36Sopenharmony_ci  	} else
7562306a36Sopenharmony_ci		printk(KERN_ERR "Recursive die() failure, output suppressed\n");
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	bust_spinlocks(0);
7862306a36Sopenharmony_ci	die.lock_owner = -1;
7962306a36Sopenharmony_ci	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
8062306a36Sopenharmony_ci	spin_unlock_irq(&die.lock);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (!regs)
8362306a36Sopenharmony_ci		return 1;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (panic_on_oops)
8662306a36Sopenharmony_ci		panic("Fatal exception");
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	make_task_dead(SIGSEGV);
8962306a36Sopenharmony_ci	return 0;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciint
9362306a36Sopenharmony_cidie_if_kernel (char *str, struct pt_regs *regs, long err)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	if (!user_mode(regs))
9662306a36Sopenharmony_ci		return die(str, regs, err);
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_civoid
10162306a36Sopenharmony_ci__kprobes ia64_bad_break (unsigned long break_num, struct pt_regs *regs)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	int sig, code;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	switch (break_num) {
10662306a36Sopenharmony_ci	      case 0: /* unknown error (used by GCC for __builtin_abort()) */
10762306a36Sopenharmony_ci		if (notify_die(DIE_BREAK, "break 0", regs, break_num, TRAP_BRKPT, SIGTRAP)
10862306a36Sopenharmony_ci			       	== NOTIFY_STOP)
10962306a36Sopenharmony_ci			return;
11062306a36Sopenharmony_ci		if (die_if_kernel("bugcheck!", regs, break_num))
11162306a36Sopenharmony_ci			return;
11262306a36Sopenharmony_ci		sig = SIGILL; code = ILL_ILLOPC;
11362306a36Sopenharmony_ci		break;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	      case 1: /* integer divide by zero */
11662306a36Sopenharmony_ci		sig = SIGFPE; code = FPE_INTDIV;
11762306a36Sopenharmony_ci		break;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	      case 2: /* integer overflow */
12062306a36Sopenharmony_ci		sig = SIGFPE; code = FPE_INTOVF;
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	      case 3: /* range check/bounds check */
12462306a36Sopenharmony_ci		sig = SIGFPE; code = FPE_FLTSUB;
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	      case 4: /* null pointer dereference */
12862306a36Sopenharmony_ci		sig = SIGSEGV; code = SEGV_MAPERR;
12962306a36Sopenharmony_ci		break;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	      case 5: /* misaligned data */
13262306a36Sopenharmony_ci		sig = SIGSEGV; code = BUS_ADRALN;
13362306a36Sopenharmony_ci		break;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	      case 6: /* decimal overflow */
13662306a36Sopenharmony_ci		sig = SIGFPE; code = __FPE_DECOVF;
13762306a36Sopenharmony_ci		break;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	      case 7: /* decimal divide by zero */
14062306a36Sopenharmony_ci		sig = SIGFPE; code = __FPE_DECDIV;
14162306a36Sopenharmony_ci		break;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	      case 8: /* packed decimal error */
14462306a36Sopenharmony_ci		sig = SIGFPE; code = __FPE_DECERR;
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	      case 9: /* invalid ASCII digit */
14862306a36Sopenharmony_ci		sig = SIGFPE; code = __FPE_INVASC;
14962306a36Sopenharmony_ci		break;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	      case 10: /* invalid decimal digit */
15262306a36Sopenharmony_ci		sig = SIGFPE; code = __FPE_INVDEC;
15362306a36Sopenharmony_ci		break;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	      case 11: /* paragraph stack overflow */
15662306a36Sopenharmony_ci		sig = SIGSEGV; code = __SEGV_PSTKOVF;
15762306a36Sopenharmony_ci		break;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	      case 0x3f000 ... 0x3ffff:	/* bundle-update in progress */
16062306a36Sopenharmony_ci		sig = SIGILL; code = __ILL_BNDMOD;
16162306a36Sopenharmony_ci		break;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	      default:
16462306a36Sopenharmony_ci		if ((break_num < 0x40000 || break_num > 0x100000)
16562306a36Sopenharmony_ci		    && die_if_kernel("Bad break", regs, break_num))
16662306a36Sopenharmony_ci			return;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		if (break_num < 0x80000) {
16962306a36Sopenharmony_ci			sig = SIGILL; code = __ILL_BREAK;
17062306a36Sopenharmony_ci		} else {
17162306a36Sopenharmony_ci			if (notify_die(DIE_BREAK, "bad break", regs, break_num, TRAP_BRKPT, SIGTRAP)
17262306a36Sopenharmony_ci					== NOTIFY_STOP)
17362306a36Sopenharmony_ci				return;
17462306a36Sopenharmony_ci			sig = SIGTRAP; code = TRAP_BRKPT;
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci	force_sig_fault(sig, code,
17862306a36Sopenharmony_ci			(void __user *) (regs->cr_iip + ia64_psr(regs)->ri),
17962306a36Sopenharmony_ci			break_num, 0 /* clear __ISR_VALID */, 0);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/*
18362306a36Sopenharmony_ci * disabled_fph_fault() is called when a user-level process attempts to access f32..f127
18462306a36Sopenharmony_ci * and it doesn't own the fp-high register partition.  When this happens, we save the
18562306a36Sopenharmony_ci * current fph partition in the task_struct of the fpu-owner (if necessary) and then load
18662306a36Sopenharmony_ci * the fp-high partition of the current task (if necessary).  Note that the kernel has
18762306a36Sopenharmony_ci * access to fph by the time we get here, as the IVT's "Disabled FP-Register" handler takes
18862306a36Sopenharmony_ci * care of clearing psr.dfh.
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_cistatic inline void
19162306a36Sopenharmony_cidisabled_fph_fault (struct pt_regs *regs)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct ia64_psr *psr = ia64_psr(regs);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* first, grant user-level access to fph partition: */
19662306a36Sopenharmony_ci	psr->dfh = 0;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/*
19962306a36Sopenharmony_ci	 * Make sure that no other task gets in on this processor
20062306a36Sopenharmony_ci	 * while we're claiming the FPU
20162306a36Sopenharmony_ci	 */
20262306a36Sopenharmony_ci	preempt_disable();
20362306a36Sopenharmony_ci#ifndef CONFIG_SMP
20462306a36Sopenharmony_ci	{
20562306a36Sopenharmony_ci		struct task_struct *fpu_owner
20662306a36Sopenharmony_ci			= (struct task_struct *)ia64_get_kr(IA64_KR_FPU_OWNER);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		if (ia64_is_local_fpu_owner(current)) {
20962306a36Sopenharmony_ci			preempt_enable_no_resched();
21062306a36Sopenharmony_ci			return;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		if (fpu_owner)
21462306a36Sopenharmony_ci			ia64_flush_fph(fpu_owner);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci#endif /* !CONFIG_SMP */
21762306a36Sopenharmony_ci	ia64_set_local_fpu_owner(current);
21862306a36Sopenharmony_ci	if ((current->thread.flags & IA64_THREAD_FPH_VALID) != 0) {
21962306a36Sopenharmony_ci		__ia64_load_fpu(current->thread.fph);
22062306a36Sopenharmony_ci		psr->mfh = 0;
22162306a36Sopenharmony_ci	} else {
22262306a36Sopenharmony_ci		__ia64_init_fpu();
22362306a36Sopenharmony_ci		/*
22462306a36Sopenharmony_ci		 * Set mfh because the state in thread.fph does not match the state in
22562306a36Sopenharmony_ci		 * the fph partition.
22662306a36Sopenharmony_ci		 */
22762306a36Sopenharmony_ci		psr->mfh = 1;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci	preempt_enable_no_resched();
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic inline int
23362306a36Sopenharmony_cifp_emulate (int fp_fault, void *bundle, long *ipsr, long *fpsr, long *isr, long *pr, long *ifs,
23462306a36Sopenharmony_ci	    struct pt_regs *regs)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	fp_state_t fp_state;
23762306a36Sopenharmony_ci	fpswa_ret_t ret;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (!fpswa_interface)
24062306a36Sopenharmony_ci		return -1;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	memset(&fp_state, 0, sizeof(fp_state_t));
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/*
24562306a36Sopenharmony_ci	 * compute fp_state.  only FP registers f6 - f11 are used by the
24662306a36Sopenharmony_ci	 * kernel, so set those bits in the mask and set the low volatile
24762306a36Sopenharmony_ci	 * pointer to point to these registers.
24862306a36Sopenharmony_ci	 */
24962306a36Sopenharmony_ci	fp_state.bitmask_low64 = 0xfc0;  /* bit6..bit11 */
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	fp_state.fp_state_low_volatile = (fp_state_low_volatile_t *) &regs->f6;
25262306a36Sopenharmony_ci	/*
25362306a36Sopenharmony_ci	 * unsigned long (*EFI_FPSWA) (
25462306a36Sopenharmony_ci	 *      unsigned long    trap_type,
25562306a36Sopenharmony_ci	 *	void             *Bundle,
25662306a36Sopenharmony_ci	 *	unsigned long    *pipsr,
25762306a36Sopenharmony_ci	 *	unsigned long    *pfsr,
25862306a36Sopenharmony_ci	 *	unsigned long    *pisr,
25962306a36Sopenharmony_ci	 *	unsigned long    *ppreds,
26062306a36Sopenharmony_ci	 *	unsigned long    *pifs,
26162306a36Sopenharmony_ci	 *	void             *fp_state);
26262306a36Sopenharmony_ci	 */
26362306a36Sopenharmony_ci	ret = (*fpswa_interface->fpswa)((unsigned long) fp_fault, bundle,
26462306a36Sopenharmony_ci					(unsigned long *) ipsr, (unsigned long *) fpsr,
26562306a36Sopenharmony_ci					(unsigned long *) isr, (unsigned long *) pr,
26662306a36Sopenharmony_ci					(unsigned long *) ifs, &fp_state);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return ret.status;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistruct fpu_swa_msg {
27262306a36Sopenharmony_ci	unsigned long count;
27362306a36Sopenharmony_ci	unsigned long time;
27462306a36Sopenharmony_ci};
27562306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct fpu_swa_msg, cpulast);
27662306a36Sopenharmony_ciDECLARE_PER_CPU(struct fpu_swa_msg, cpulast);
27762306a36Sopenharmony_cistatic struct fpu_swa_msg last __cacheline_aligned;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/*
28162306a36Sopenharmony_ci * Handle floating-point assist faults and traps.
28262306a36Sopenharmony_ci */
28362306a36Sopenharmony_cistatic int
28462306a36Sopenharmony_cihandle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	long exception, bundle[2];
28762306a36Sopenharmony_ci	unsigned long fault_ip;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	fault_ip = regs->cr_iip;
29062306a36Sopenharmony_ci	if (!fp_fault && (ia64_psr(regs)->ri == 0))
29162306a36Sopenharmony_ci		fault_ip -= 16;
29262306a36Sopenharmony_ci	if (copy_from_user(bundle, (void __user *) fault_ip, sizeof(bundle)))
29362306a36Sopenharmony_ci		return -1;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!(current->thread.flags & IA64_THREAD_FPEMU_NOPRINT))  {
29662306a36Sopenharmony_ci		unsigned long count, current_jiffies = jiffies;
29762306a36Sopenharmony_ci		struct fpu_swa_msg *cp = this_cpu_ptr(&cpulast);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		if (unlikely(current_jiffies > cp->time))
30062306a36Sopenharmony_ci			cp->count = 0;
30162306a36Sopenharmony_ci		if (unlikely(cp->count < 5)) {
30262306a36Sopenharmony_ci			cp->count++;
30362306a36Sopenharmony_ci			cp->time = current_jiffies + 5 * HZ;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci			/* minimize races by grabbing a copy of count BEFORE checking last.time. */
30662306a36Sopenharmony_ci			count = last.count;
30762306a36Sopenharmony_ci			barrier();
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci			/*
31062306a36Sopenharmony_ci			 * Lower 4 bits are used as a count. Upper bits are a sequence
31162306a36Sopenharmony_ci			 * number that is updated when count is reset. The cmpxchg will
31262306a36Sopenharmony_ci			 * fail is seqno has changed. This minimizes multiple cpus
31362306a36Sopenharmony_ci			 * resetting the count.
31462306a36Sopenharmony_ci			 */
31562306a36Sopenharmony_ci			if (current_jiffies > last.time)
31662306a36Sopenharmony_ci				(void) cmpxchg_acq(&last.count, count, 16 + (count & ~15));
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci			/* used fetchadd to atomically update the count */
31962306a36Sopenharmony_ci			if ((last.count & 15) < 5 && (ia64_fetchadd(1, &last.count, acq) & 15) < 5) {
32062306a36Sopenharmony_ci				last.time = current_jiffies + 5 * HZ;
32162306a36Sopenharmony_ci				printk(KERN_WARNING
32262306a36Sopenharmony_ci		       			"%s(%d): floating-point assist fault at ip %016lx, isr %016lx\n",
32362306a36Sopenharmony_ci		       			current->comm, task_pid_nr(current), regs->cr_iip + ia64_psr(regs)->ri, isr);
32462306a36Sopenharmony_ci			}
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	exception = fp_emulate(fp_fault, bundle, &regs->cr_ipsr, &regs->ar_fpsr, &isr, &regs->pr,
32962306a36Sopenharmony_ci			       &regs->cr_ifs, regs);
33062306a36Sopenharmony_ci	if (fp_fault) {
33162306a36Sopenharmony_ci		if (exception == 0) {
33262306a36Sopenharmony_ci			/* emulation was successful */
33362306a36Sopenharmony_ci			ia64_increment_ip(regs);
33462306a36Sopenharmony_ci		} else if (exception == -1) {
33562306a36Sopenharmony_ci			printk(KERN_ERR "handle_fpu_swa: fp_emulate() returned -1\n");
33662306a36Sopenharmony_ci			return -1;
33762306a36Sopenharmony_ci		} else {
33862306a36Sopenharmony_ci			/* is next instruction a trap? */
33962306a36Sopenharmony_ci			int si_code;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci			if (exception & 2) {
34262306a36Sopenharmony_ci				ia64_increment_ip(regs);
34362306a36Sopenharmony_ci			}
34462306a36Sopenharmony_ci			si_code = FPE_FLTUNK;	/* default code */
34562306a36Sopenharmony_ci			if (isr & 0x11) {
34662306a36Sopenharmony_ci				si_code = FPE_FLTINV;
34762306a36Sopenharmony_ci			} else if (isr & 0x22) {
34862306a36Sopenharmony_ci				/* denormal operand gets the same si_code as underflow
34962306a36Sopenharmony_ci				* see arch/i386/kernel/traps.c:math_error()  */
35062306a36Sopenharmony_ci				si_code = FPE_FLTUND;
35162306a36Sopenharmony_ci			} else if (isr & 0x44) {
35262306a36Sopenharmony_ci				si_code = FPE_FLTDIV;
35362306a36Sopenharmony_ci			}
35462306a36Sopenharmony_ci			force_sig_fault(SIGFPE, si_code,
35562306a36Sopenharmony_ci					(void __user *) (regs->cr_iip + ia64_psr(regs)->ri),
35662306a36Sopenharmony_ci					0, __ISR_VALID, isr);
35762306a36Sopenharmony_ci		}
35862306a36Sopenharmony_ci	} else {
35962306a36Sopenharmony_ci		if (exception == -1) {
36062306a36Sopenharmony_ci			printk(KERN_ERR "handle_fpu_swa: fp_emulate() returned -1\n");
36162306a36Sopenharmony_ci			return -1;
36262306a36Sopenharmony_ci		} else if (exception != 0) {
36362306a36Sopenharmony_ci			/* raise exception */
36462306a36Sopenharmony_ci			int si_code;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci			si_code = FPE_FLTUNK;	/* default code */
36762306a36Sopenharmony_ci			if (isr & 0x880) {
36862306a36Sopenharmony_ci				si_code = FPE_FLTOVF;
36962306a36Sopenharmony_ci			} else if (isr & 0x1100) {
37062306a36Sopenharmony_ci				si_code = FPE_FLTUND;
37162306a36Sopenharmony_ci			} else if (isr & 0x2200) {
37262306a36Sopenharmony_ci				si_code = FPE_FLTRES;
37362306a36Sopenharmony_ci			}
37462306a36Sopenharmony_ci			force_sig_fault(SIGFPE, si_code,
37562306a36Sopenharmony_ci					(void __user *) (regs->cr_iip + ia64_psr(regs)->ri),
37662306a36Sopenharmony_ci					0, __ISR_VALID, isr);
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistruct illegal_op_return {
38362306a36Sopenharmony_ci	unsigned long fkt, arg1, arg2, arg3;
38462306a36Sopenharmony_ci};
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistruct illegal_op_return
38762306a36Sopenharmony_ciia64_illegal_op_fault (unsigned long ec, long arg1, long arg2, long arg3,
38862306a36Sopenharmony_ci		       long arg4, long arg5, long arg6, long arg7,
38962306a36Sopenharmony_ci		       struct pt_regs regs)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct illegal_op_return rv;
39262306a36Sopenharmony_ci	char buf[128];
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci#ifdef CONFIG_IA64_BRL_EMU
39562306a36Sopenharmony_ci	{
39662306a36Sopenharmony_ci		extern struct illegal_op_return ia64_emulate_brl (struct pt_regs *, unsigned long);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		rv = ia64_emulate_brl(&regs, ec);
39962306a36Sopenharmony_ci		if (rv.fkt != (unsigned long) -1)
40062306a36Sopenharmony_ci			return rv;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci#endif
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	sprintf(buf, "IA-64 Illegal operation fault");
40562306a36Sopenharmony_ci	rv.fkt = 0;
40662306a36Sopenharmony_ci	if (die_if_kernel(buf, &regs, 0))
40762306a36Sopenharmony_ci		return rv;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	force_sig_fault(SIGILL, ILL_ILLOPC,
41062306a36Sopenharmony_ci			(void __user *) (regs.cr_iip + ia64_psr(&regs)->ri),
41162306a36Sopenharmony_ci			0, 0, 0);
41262306a36Sopenharmony_ci	return rv;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_civoid __kprobes
41662306a36Sopenharmony_ciia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
41762306a36Sopenharmony_ci	    unsigned long iim, unsigned long itir, long arg5, long arg6,
41862306a36Sopenharmony_ci	    long arg7, struct pt_regs regs)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	unsigned long code, error = isr, iip;
42162306a36Sopenharmony_ci	char buf[128];
42262306a36Sopenharmony_ci	int result, sig, si_code;
42362306a36Sopenharmony_ci	static const char *reason[] = {
42462306a36Sopenharmony_ci		"IA-64 Illegal Operation fault",
42562306a36Sopenharmony_ci		"IA-64 Privileged Operation fault",
42662306a36Sopenharmony_ci		"IA-64 Privileged Register fault",
42762306a36Sopenharmony_ci		"IA-64 Reserved Register/Field fault",
42862306a36Sopenharmony_ci		"Disabled Instruction Set Transition fault",
42962306a36Sopenharmony_ci		"Unknown fault 5", "Unknown fault 6", "Unknown fault 7", "Illegal Hazard fault",
43062306a36Sopenharmony_ci		"Unknown fault 9", "Unknown fault 10", "Unknown fault 11", "Unknown fault 12",
43162306a36Sopenharmony_ci		"Unknown fault 13", "Unknown fault 14", "Unknown fault 15"
43262306a36Sopenharmony_ci	};
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if ((isr & IA64_ISR_NA) && ((isr & IA64_ISR_CODE_MASK) == IA64_ISR_CODE_LFETCH)) {
43562306a36Sopenharmony_ci		/*
43662306a36Sopenharmony_ci		 * This fault was due to lfetch.fault, set "ed" bit in the psr to cancel
43762306a36Sopenharmony_ci		 * the lfetch.
43862306a36Sopenharmony_ci		 */
43962306a36Sopenharmony_ci		ia64_psr(&regs)->ed = 1;
44062306a36Sopenharmony_ci		return;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	iip = regs.cr_iip + ia64_psr(&regs)->ri;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	switch (vector) {
44662306a36Sopenharmony_ci	      case 24: /* General Exception */
44762306a36Sopenharmony_ci		code = (isr >> 4) & 0xf;
44862306a36Sopenharmony_ci		sprintf(buf, "General Exception: %s%s", reason[code],
44962306a36Sopenharmony_ci			(code == 3) ? ((isr & (1UL << 37))
45062306a36Sopenharmony_ci				       ? " (RSE access)" : " (data access)") : "");
45162306a36Sopenharmony_ci		if (code == 8) {
45262306a36Sopenharmony_ci# ifdef CONFIG_IA64_PRINT_HAZARDS
45362306a36Sopenharmony_ci			printk("%s[%d]: possible hazard @ ip=%016lx (pr = %016lx)\n",
45462306a36Sopenharmony_ci			       current->comm, task_pid_nr(current),
45562306a36Sopenharmony_ci			       regs.cr_iip + ia64_psr(&regs)->ri, regs.pr);
45662306a36Sopenharmony_ci# endif
45762306a36Sopenharmony_ci			return;
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci		break;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	      case 25: /* Disabled FP-Register */
46262306a36Sopenharmony_ci		if (isr & 2) {
46362306a36Sopenharmony_ci			disabled_fph_fault(&regs);
46462306a36Sopenharmony_ci			return;
46562306a36Sopenharmony_ci		}
46662306a36Sopenharmony_ci		sprintf(buf, "Disabled FPL fault---not supposed to happen!");
46762306a36Sopenharmony_ci		break;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	      case 26: /* NaT Consumption */
47062306a36Sopenharmony_ci		if (user_mode(&regs)) {
47162306a36Sopenharmony_ci			void __user *addr;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci			if (((isr >> 4) & 0xf) == 2) {
47462306a36Sopenharmony_ci				/* NaT page consumption */
47562306a36Sopenharmony_ci				sig = SIGSEGV;
47662306a36Sopenharmony_ci				code = SEGV_ACCERR;
47762306a36Sopenharmony_ci				addr = (void __user *) ifa;
47862306a36Sopenharmony_ci			} else {
47962306a36Sopenharmony_ci				/* register NaT consumption */
48062306a36Sopenharmony_ci				sig = SIGILL;
48162306a36Sopenharmony_ci				code = ILL_ILLOPN;
48262306a36Sopenharmony_ci				addr = (void __user *) (regs.cr_iip
48362306a36Sopenharmony_ci							+ ia64_psr(&regs)->ri);
48462306a36Sopenharmony_ci			}
48562306a36Sopenharmony_ci			force_sig_fault(sig, code, addr,
48662306a36Sopenharmony_ci					vector, __ISR_VALID, isr);
48762306a36Sopenharmony_ci			return;
48862306a36Sopenharmony_ci		} else if (ia64_done_with_exception(&regs))
48962306a36Sopenharmony_ci			return;
49062306a36Sopenharmony_ci		sprintf(buf, "NaT consumption");
49162306a36Sopenharmony_ci		break;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	      case 31: /* Unsupported Data Reference */
49462306a36Sopenharmony_ci		if (user_mode(&regs)) {
49562306a36Sopenharmony_ci			force_sig_fault(SIGILL, ILL_ILLOPN, (void __user *) iip,
49662306a36Sopenharmony_ci					vector, __ISR_VALID, isr);
49762306a36Sopenharmony_ci			return;
49862306a36Sopenharmony_ci		}
49962306a36Sopenharmony_ci		sprintf(buf, "Unsupported data reference");
50062306a36Sopenharmony_ci		break;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	      case 29: /* Debug */
50362306a36Sopenharmony_ci	      case 35: /* Taken Branch Trap */
50462306a36Sopenharmony_ci	      case 36: /* Single Step Trap */
50562306a36Sopenharmony_ci		if (fsys_mode(current, &regs)) {
50662306a36Sopenharmony_ci			extern char __kernel_syscall_via_break[];
50762306a36Sopenharmony_ci			/*
50862306a36Sopenharmony_ci			 * Got a trap in fsys-mode: Taken Branch Trap
50962306a36Sopenharmony_ci			 * and Single Step trap need special handling;
51062306a36Sopenharmony_ci			 * Debug trap is ignored (we disable it here
51162306a36Sopenharmony_ci			 * and re-enable it in the lower-privilege trap).
51262306a36Sopenharmony_ci			 */
51362306a36Sopenharmony_ci			if (unlikely(vector == 29)) {
51462306a36Sopenharmony_ci				set_thread_flag(TIF_DB_DISABLED);
51562306a36Sopenharmony_ci				ia64_psr(&regs)->db = 0;
51662306a36Sopenharmony_ci				ia64_psr(&regs)->lp = 1;
51762306a36Sopenharmony_ci				return;
51862306a36Sopenharmony_ci			}
51962306a36Sopenharmony_ci			/* re-do the system call via break 0x100000: */
52062306a36Sopenharmony_ci			regs.cr_iip = (unsigned long) __kernel_syscall_via_break;
52162306a36Sopenharmony_ci			ia64_psr(&regs)->ri = 0;
52262306a36Sopenharmony_ci			ia64_psr(&regs)->cpl = 3;
52362306a36Sopenharmony_ci			return;
52462306a36Sopenharmony_ci		}
52562306a36Sopenharmony_ci		switch (vector) {
52662306a36Sopenharmony_ci		      default:
52762306a36Sopenharmony_ci		      case 29:
52862306a36Sopenharmony_ci			si_code = TRAP_HWBKPT;
52962306a36Sopenharmony_ci#ifdef CONFIG_ITANIUM
53062306a36Sopenharmony_ci			/*
53162306a36Sopenharmony_ci			 * Erratum 10 (IFA may contain incorrect address) now has
53262306a36Sopenharmony_ci			 * "NoFix" status.  There are no plans for fixing this.
53362306a36Sopenharmony_ci			 */
53462306a36Sopenharmony_ci			if (ia64_psr(&regs)->is == 0)
53562306a36Sopenharmony_ci			  ifa = regs.cr_iip;
53662306a36Sopenharmony_ci#endif
53762306a36Sopenharmony_ci			break;
53862306a36Sopenharmony_ci		      case 35: si_code = TRAP_BRANCH; ifa = 0; break;
53962306a36Sopenharmony_ci		      case 36: si_code = TRAP_TRACE; ifa = 0; break;
54062306a36Sopenharmony_ci		}
54162306a36Sopenharmony_ci		if (notify_die(DIE_FAULT, "ia64_fault", &regs, vector, si_code, SIGTRAP)
54262306a36Sopenharmony_ci			       	== NOTIFY_STOP)
54362306a36Sopenharmony_ci			return;
54462306a36Sopenharmony_ci		force_sig_fault(SIGTRAP, si_code, (void __user *) ifa,
54562306a36Sopenharmony_ci				0, __ISR_VALID, isr);
54662306a36Sopenharmony_ci		return;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	      case 32: /* fp fault */
54962306a36Sopenharmony_ci	      case 33: /* fp trap */
55062306a36Sopenharmony_ci		result = handle_fpu_swa((vector == 32) ? 1 : 0, &regs, isr);
55162306a36Sopenharmony_ci		if ((result < 0) || (current->thread.flags & IA64_THREAD_FPEMU_SIGFPE)) {
55262306a36Sopenharmony_ci			force_sig_fault(SIGFPE, FPE_FLTINV, (void __user *) iip,
55362306a36Sopenharmony_ci					0, __ISR_VALID, isr);
55462306a36Sopenharmony_ci		}
55562306a36Sopenharmony_ci		return;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	      case 34:
55862306a36Sopenharmony_ci		if (isr & 0x2) {
55962306a36Sopenharmony_ci			/* Lower-Privilege Transfer Trap */
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci			/* If we disabled debug traps during an fsyscall,
56262306a36Sopenharmony_ci			 * re-enable them here.
56362306a36Sopenharmony_ci			 */
56462306a36Sopenharmony_ci			if (test_thread_flag(TIF_DB_DISABLED)) {
56562306a36Sopenharmony_ci				clear_thread_flag(TIF_DB_DISABLED);
56662306a36Sopenharmony_ci				ia64_psr(&regs)->db = 1;
56762306a36Sopenharmony_ci			}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci			/*
57062306a36Sopenharmony_ci			 * Just clear PSR.lp and then return immediately:
57162306a36Sopenharmony_ci			 * all the interesting work (e.g., signal delivery)
57262306a36Sopenharmony_ci			 * is done in the kernel exit path.
57362306a36Sopenharmony_ci			 */
57462306a36Sopenharmony_ci			ia64_psr(&regs)->lp = 0;
57562306a36Sopenharmony_ci			return;
57662306a36Sopenharmony_ci		} else {
57762306a36Sopenharmony_ci			/* Unimplemented Instr. Address Trap */
57862306a36Sopenharmony_ci			if (user_mode(&regs)) {
57962306a36Sopenharmony_ci				force_sig_fault(SIGILL, ILL_BADIADDR,
58062306a36Sopenharmony_ci						(void __user *) iip,
58162306a36Sopenharmony_ci						0, 0, 0);
58262306a36Sopenharmony_ci				return;
58362306a36Sopenharmony_ci			}
58462306a36Sopenharmony_ci			sprintf(buf, "Unimplemented Instruction Address fault");
58562306a36Sopenharmony_ci		}
58662306a36Sopenharmony_ci		break;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	      case 45:
58962306a36Sopenharmony_ci		printk(KERN_ERR "Unexpected IA-32 exception (Trap 45)\n");
59062306a36Sopenharmony_ci		printk(KERN_ERR "  iip - 0x%lx, ifa - 0x%lx, isr - 0x%lx\n",
59162306a36Sopenharmony_ci		       iip, ifa, isr);
59262306a36Sopenharmony_ci		force_sig(SIGSEGV);
59362306a36Sopenharmony_ci		return;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	      case 46:
59662306a36Sopenharmony_ci		printk(KERN_ERR "Unexpected IA-32 intercept trap (Trap 46)\n");
59762306a36Sopenharmony_ci		printk(KERN_ERR "  iip - 0x%lx, ifa - 0x%lx, isr - 0x%lx, iim - 0x%lx\n",
59862306a36Sopenharmony_ci		       iip, ifa, isr, iim);
59962306a36Sopenharmony_ci		force_sig(SIGSEGV);
60062306a36Sopenharmony_ci		return;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	      case 47:
60362306a36Sopenharmony_ci		sprintf(buf, "IA-32 Interruption Fault (int 0x%lx)", isr >> 16);
60462306a36Sopenharmony_ci		break;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	      default:
60762306a36Sopenharmony_ci		sprintf(buf, "Fault %lu", vector);
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci	if (!die_if_kernel(buf, &regs, error))
61162306a36Sopenharmony_ci		force_sig(SIGILL);
61262306a36Sopenharmony_ci}
613