xref: /openbmc/linux/arch/s390/kernel/traps.c (revision 8ebc80a25f9d9bf7a8e368b266d5b740c485c362)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  S390 version
4a53c8fabSHeiko Carstens  *    Copyright IBM Corp. 1999, 2000
51da177e4SLinus Torvalds  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
61da177e4SLinus Torvalds  *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *  Derived from "arch/i386/kernel/traps.c"
91da177e4SLinus Torvalds  *    Copyright (C) 1991, 1992 Linus Torvalds
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds /*
131da177e4SLinus Torvalds  * 'Traps.c' handles hardware traps and faults after we have saved some
141da177e4SLinus Torvalds  * state in 'asm.s'.
151da177e4SLinus Torvalds  */
1656e62a73SSven Schnelle #include "asm/irqflags.h"
1756e62a73SSven Schnelle #include "asm/ptrace.h"
184ba069b8SMichael Grundy #include <linux/kprobes.h>
191bca09f7SHeiko Carstens #include <linux/kdebug.h>
20bae1cd36SSven Schnelle #include <linux/randomize_kstack.h>
21dcc096c5SPaul Gortmaker #include <linux/extable.h>
221bca09f7SHeiko Carstens #include <linux/ptrace.h>
231bca09f7SHeiko Carstens #include <linux/sched.h>
24b17b0153SIngo Molnar #include <linux/sched/debug.h>
251bca09f7SHeiko Carstens #include <linux/mm.h>
2680703617SMartin Schwidefsky #include <linux/slab.h>
277c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
2892acfb74SHeiko Carstens #include <linux/cpu.h>
2956e62a73SSven Schnelle #include <linux/entry-common.h>
30d09a307fSHeiko Carstens #include <asm/asm-extable.h>
31b0753902SHendrik Brueckner #include <asm/fpu/api.h>
3256e62a73SSven Schnelle #include <asm/vtime.h>
33a806170eSHeiko Carstens #include "entry.h"
341da177e4SLinus Torvalds 
get_trap_ip(struct pt_regs * regs)35d35339a4SMartin Schwidefsky static inline void __user *get_trap_ip(struct pt_regs *regs)
36d35339a4SMartin Schwidefsky {
37d35339a4SMartin Schwidefsky 	unsigned long address;
38d35339a4SMartin Schwidefsky 
39d35339a4SMartin Schwidefsky 	if (regs->int_code & 0x200)
40755112b3SSven Schnelle 		address = current->thread.trap_tdb.data[3];
41d35339a4SMartin Schwidefsky 	else
42d35339a4SMartin Schwidefsky 		address = regs->psw.addr;
439cb1ccecSHeiko Carstens 	return (void __user *) (address - (regs->int_code >> 16));
44d35339a4SMartin Schwidefsky }
45d35339a4SMartin Schwidefsky 
is_valid_bugaddr(unsigned long addr)46c0007f1aSHeiko Carstens int is_valid_bugaddr(unsigned long addr)
47c0007f1aSHeiko Carstens {
48c0007f1aSHeiko Carstens 	return 1;
49c0007f1aSHeiko Carstens }
50c0007f1aSHeiko Carstens 
do_report_trap(struct pt_regs * regs,int si_signo,int si_code,char * str)512a0a5b22SJan Willeke void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
52aa33c8cbSMartin Schwidefsky {
537d256175SHeiko Carstens 	if (user_mode(regs)) {
542e1661d2SEric W. Biederman 		force_sig_fault(si_signo, si_code, get_trap_ip(regs));
555d7eccecSHeiko Carstens 		report_user_fault(regs, si_signo, 0);
561da177e4SLinus Torvalds         } else {
5746fee16fSHeiko Carstens 		if (!fixup_exception(regs))
58aa33c8cbSMartin Schwidefsky 			die(regs, str);
591da177e4SLinus Torvalds         }
601da177e4SLinus Torvalds }
611da177e4SLinus Torvalds 
do_trap(struct pt_regs * regs,int si_signo,int si_code,char * str)627a5388deSHeiko Carstens static void do_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
632a0a5b22SJan Willeke {
642a0a5b22SJan Willeke 	if (notify_die(DIE_TRAP, str, regs, 0,
652a0a5b22SJan Willeke 		       regs->int_code, si_signo) == NOTIFY_STOP)
662a0a5b22SJan Willeke 		return;
672a0a5b22SJan Willeke 	do_report_trap(regs, si_signo, si_code, str);
682a0a5b22SJan Willeke }
697a5388deSHeiko Carstens NOKPROBE_SYMBOL(do_trap);
702a0a5b22SJan Willeke 
do_per_trap(struct pt_regs * regs)717a5388deSHeiko Carstens void do_per_trap(struct pt_regs *regs)
721da177e4SLinus Torvalds {
735e9a2692SMartin Schwidefsky 	if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
744ba069b8SMichael Grundy 		return;
7573b7d40fSMartin Schwidefsky 	if (!current->ptrace)
7673b7d40fSMartin Schwidefsky 		return;
779507a5d0SEric W. Biederman 	force_sig_fault(SIGTRAP, TRAP_HWBKPT,
782e1661d2SEric W. Biederman 		(void __force __user *) current->thread.per_event.address);
791da177e4SLinus Torvalds }
807a5388deSHeiko Carstens NOKPROBE_SYMBOL(do_per_trap);
811da177e4SLinus Torvalds 
default_trap_handler(struct pt_regs * regs)8217a363dcSHeiko Carstens static void default_trap_handler(struct pt_regs *regs)
831da177e4SLinus Torvalds {
847d256175SHeiko Carstens 	if (user_mode(regs)) {
855d7eccecSHeiko Carstens 		report_user_fault(regs, SIGSEGV, 0);
86fcb116bcSEric W. Biederman 		force_exit_sig(SIGSEGV);
871da177e4SLinus Torvalds 	} else
88aa33c8cbSMartin Schwidefsky 		die(regs, "Unknown program exception");
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
911e54622eSMartin Schwidefsky #define DO_ERROR_INFO(name, signr, sicode, str) \
926f8daa29SHeiko Carstens static void name(struct pt_regs *regs)		\
931da177e4SLinus Torvalds {						\
94aa33c8cbSMartin Schwidefsky 	do_trap(regs, signr, sicode, str);	\
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds 
971e54622eSMartin Schwidefsky DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
981e54622eSMartin Schwidefsky 	      "addressing exception")
991e54622eSMartin Schwidefsky DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
1001e54622eSMartin Schwidefsky 	      "execute exception")
1011e54622eSMartin Schwidefsky DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
1021e54622eSMartin Schwidefsky 	      "fixpoint divide exception")
1031e54622eSMartin Schwidefsky DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
1041e54622eSMartin Schwidefsky 	      "fixpoint overflow exception")
1051e54622eSMartin Schwidefsky DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
1061e54622eSMartin Schwidefsky 	      "HFP overflow exception")
1071e54622eSMartin Schwidefsky DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
1081e54622eSMartin Schwidefsky 	      "HFP underflow exception")
1091e54622eSMartin Schwidefsky DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
1101e54622eSMartin Schwidefsky 	      "HFP significance exception")
1111e54622eSMartin Schwidefsky DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
1121e54622eSMartin Schwidefsky 	      "HFP divide exception")
1131e54622eSMartin Schwidefsky DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
1141e54622eSMartin Schwidefsky 	      "HFP square root exception")
1151e54622eSMartin Schwidefsky DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
1161e54622eSMartin Schwidefsky 	      "operand exception")
1171e54622eSMartin Schwidefsky DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
1181e54622eSMartin Schwidefsky 	      "privileged operation")
1191e54622eSMartin Schwidefsky DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
1201e54622eSMartin Schwidefsky 	      "special operation exception")
121d35339a4SMartin Schwidefsky DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN,
122d35339a4SMartin Schwidefsky 	      "transaction constraint exception")
123d35339a4SMartin Schwidefsky 
do_fp_trap(struct pt_regs * regs,__u32 fpc)1249977e886SHendrik Brueckner static inline void do_fp_trap(struct pt_regs *regs, __u32 fpc)
1251da177e4SLinus Torvalds {
126aa33c8cbSMartin Schwidefsky 	int si_code = 0;
1271da177e4SLinus Torvalds 	/* FPC[2] is Data Exception Code */
1281da177e4SLinus Torvalds 	if ((fpc & 0x00000300) == 0) {
1291da177e4SLinus Torvalds 		/* bits 6 and 7 of DXC are 0 iff IEEE exception */
1301da177e4SLinus Torvalds 		if (fpc & 0x8000) /* invalid fp operation */
131aa33c8cbSMartin Schwidefsky 			si_code = FPE_FLTINV;
1321da177e4SLinus Torvalds 		else if (fpc & 0x4000) /* div by 0 */
133aa33c8cbSMartin Schwidefsky 			si_code = FPE_FLTDIV;
1341da177e4SLinus Torvalds 		else if (fpc & 0x2000) /* overflow */
135aa33c8cbSMartin Schwidefsky 			si_code = FPE_FLTOVF;
1361da177e4SLinus Torvalds 		else if (fpc & 0x1000) /* underflow */
137aa33c8cbSMartin Schwidefsky 			si_code = FPE_FLTUND;
1381da177e4SLinus Torvalds 		else if (fpc & 0x0800) /* inexact */
139aa33c8cbSMartin Schwidefsky 			si_code = FPE_FLTRES;
1401da177e4SLinus Torvalds 	}
141aa33c8cbSMartin Schwidefsky 	do_trap(regs, SIGFPE, si_code, "floating point exception");
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds 
translation_specification_exception(struct pt_regs * regs)144f09354ffSHeiko Carstens static void translation_specification_exception(struct pt_regs *regs)
145e56da345SHeiko Carstens {
146e56da345SHeiko Carstens 	/* May never happen. */
147f09354ffSHeiko Carstens 	panic("Translation-Specification Exception");
148e56da345SHeiko Carstens }
149e56da345SHeiko Carstens 
illegal_op(struct pt_regs * regs)1506f8daa29SHeiko Carstens static void illegal_op(struct pt_regs *regs)
1511da177e4SLinus Torvalds {
1521da177e4SLinus Torvalds         __u8 opcode[6];
153d2c993d8SHeiko Carstens 	__u16 __user *location;
1542a0a5b22SJan Willeke 	int is_uprobe_insn = 0;
1551da177e4SLinus Torvalds 	int signal = 0;
1561da177e4SLinus Torvalds 
157d35339a4SMartin Schwidefsky 	location = get_trap_ip(regs);
1581da177e4SLinus Torvalds 
1597d256175SHeiko Carstens 	if (user_mode(regs)) {
16012bae235SHeiko Carstens 		if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
16112bae235SHeiko Carstens 			return;
1621da177e4SLinus Torvalds 		if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
1639507a5d0SEric W. Biederman 			if (current->ptrace)
1642e1661d2SEric W. Biederman 				force_sig_fault(SIGTRAP, TRAP_BRKPT, location);
1659507a5d0SEric W. Biederman 			else
1661da177e4SLinus Torvalds 				signal = SIGILL;
1672a0a5b22SJan Willeke #ifdef CONFIG_UPROBES
1682a0a5b22SJan Willeke 		} else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) {
1692a0a5b22SJan Willeke 			is_uprobe_insn = 1;
1702a0a5b22SJan Willeke #endif
1711da177e4SLinus Torvalds 		} else
1721da177e4SLinus Torvalds 			signal = SIGILL;
1732a0a5b22SJan Willeke 	}
17435df8d53SHeiko Carstens 	/*
1752a0a5b22SJan Willeke 	 * We got either an illegal op in kernel mode, or user space trapped
1762a0a5b22SJan Willeke 	 * on a uprobes illegal instruction. See if kprobes or uprobes picks
1772a0a5b22SJan Willeke 	 * it up. If not, SIGILL.
17835df8d53SHeiko Carstens 	 */
1792a0a5b22SJan Willeke 	if (is_uprobe_insn || !user_mode(regs)) {
180aa33c8cbSMartin Schwidefsky 		if (notify_die(DIE_BPT, "bpt", regs, 0,
18135df8d53SHeiko Carstens 			       3, SIGTRAP) != NOTIFY_STOP)
1821da177e4SLinus Torvalds 			signal = SIGILL;
18335df8d53SHeiko Carstens 	}
184aa33c8cbSMartin Schwidefsky 	if (signal)
185aa33c8cbSMartin Schwidefsky 		do_trap(regs, signal, ILL_ILLOPC, "illegal operation");
1861da177e4SLinus Torvalds }
1877a5388deSHeiko Carstens NOKPROBE_SYMBOL(illegal_op);
1881da177e4SLinus Torvalds 
1891e54622eSMartin Schwidefsky DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
1901e54622eSMartin Schwidefsky 	      "specification exception");
1911da177e4SLinus Torvalds 
vector_exception(struct pt_regs * regs)1926f8daa29SHeiko Carstens static void vector_exception(struct pt_regs *regs)
19380703617SMartin Schwidefsky {
19480703617SMartin Schwidefsky 	int si_code, vic;
19580703617SMartin Schwidefsky 
19680703617SMartin Schwidefsky 	if (!MACHINE_HAS_VX) {
19780703617SMartin Schwidefsky 		do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation");
19880703617SMartin Schwidefsky 		return;
19980703617SMartin Schwidefsky 	}
20080703617SMartin Schwidefsky 
20180703617SMartin Schwidefsky 	/* get vector interrupt code from fpc */
202d0164ee2SHendrik Brueckner 	save_fpu_regs();
203904818e2SHendrik Brueckner 	vic = (current->thread.fpu.fpc & 0xf00) >> 8;
20480703617SMartin Schwidefsky 	switch (vic) {
20580703617SMartin Schwidefsky 	case 1: /* invalid vector operation */
20680703617SMartin Schwidefsky 		si_code = FPE_FLTINV;
20780703617SMartin Schwidefsky 		break;
20880703617SMartin Schwidefsky 	case 2: /* division by zero */
20980703617SMartin Schwidefsky 		si_code = FPE_FLTDIV;
21080703617SMartin Schwidefsky 		break;
21180703617SMartin Schwidefsky 	case 3: /* overflow */
21280703617SMartin Schwidefsky 		si_code = FPE_FLTOVF;
21380703617SMartin Schwidefsky 		break;
21480703617SMartin Schwidefsky 	case 4: /* underflow */
21580703617SMartin Schwidefsky 		si_code = FPE_FLTUND;
21680703617SMartin Schwidefsky 		break;
21780703617SMartin Schwidefsky 	case 5:	/* inexact */
21880703617SMartin Schwidefsky 		si_code = FPE_FLTRES;
21980703617SMartin Schwidefsky 		break;
22080703617SMartin Schwidefsky 	default: /* unknown cause */
22180703617SMartin Schwidefsky 		si_code = 0;
22280703617SMartin Schwidefsky 	}
22380703617SMartin Schwidefsky 	do_trap(regs, SIGFPE, si_code, "vector exception");
22480703617SMartin Schwidefsky }
22580703617SMartin Schwidefsky 
data_exception(struct pt_regs * regs)2266f8daa29SHeiko Carstens static void data_exception(struct pt_regs *regs)
2271da177e4SLinus Torvalds {
228d0164ee2SHendrik Brueckner 	save_fpu_regs();
229904818e2SHendrik Brueckner 	if (current->thread.fpu.fpc & FPC_DXC_MASK)
230904818e2SHendrik Brueckner 		do_fp_trap(regs, current->thread.fpu.fpc);
231b4e3133bSVasily Gorbik 	else
232b4e3133bSVasily Gorbik 		do_trap(regs, SIGILL, ILL_ILLOPN, "data exception");
2331da177e4SLinus Torvalds }
2341da177e4SLinus Torvalds 
space_switch_exception(struct pt_regs * regs)2356f8daa29SHeiko Carstens static void space_switch_exception(struct pt_regs *regs)
2361da177e4SLinus Torvalds {
2371da177e4SLinus Torvalds 	/* Set user psw back to home space mode. */
2387d256175SHeiko Carstens 	if (user_mode(regs))
2391da177e4SLinus Torvalds 		regs->psw.mask |= PSW_ASC_HOME;
2401da177e4SLinus Torvalds 	/* Send SIGILL. */
241aa33c8cbSMartin Schwidefsky 	do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds 
monitor_event_exception(struct pt_regs * regs)2446f8daa29SHeiko Carstens static void monitor_event_exception(struct pt_regs *regs)
24517248ea0SSven Schnelle {
24617248ea0SSven Schnelle 	if (user_mode(regs))
24717248ea0SSven Schnelle 		return;
24817248ea0SSven Schnelle 
24917248ea0SSven Schnelle 	switch (report_bug(regs->psw.addr - (regs->int_code >> 16), regs)) {
25017248ea0SSven Schnelle 	case BUG_TRAP_TYPE_NONE:
25146fee16fSHeiko Carstens 		fixup_exception(regs);
25217248ea0SSven Schnelle 		break;
25317248ea0SSven Schnelle 	case BUG_TRAP_TYPE_WARN:
25417248ea0SSven Schnelle 		break;
25517248ea0SSven Schnelle 	case BUG_TRAP_TYPE_BUG:
25617248ea0SSven Schnelle 		die(regs, "monitor event");
25717248ea0SSven Schnelle 		break;
25817248ea0SSven Schnelle 	}
25917248ea0SSven Schnelle }
26017248ea0SSven Schnelle 
kernel_stack_overflow(struct pt_regs * regs)2617a5388deSHeiko Carstens void kernel_stack_overflow(struct pt_regs *regs)
2621da177e4SLinus Torvalds {
26377eb65cbSHeiko Carstens 	bust_spinlocks(1);
26477eb65cbSHeiko Carstens 	printk("Kernel stack overflow.\n");
26577eb65cbSHeiko Carstens 	show_regs(regs);
26677eb65cbSHeiko Carstens 	bust_spinlocks(0);
2671da177e4SLinus Torvalds 	panic("Corrupt kernel stack, can't continue.");
2681da177e4SLinus Torvalds }
2697a5388deSHeiko Carstens NOKPROBE_SYMBOL(kernel_stack_overflow);
2701da177e4SLinus Torvalds 
test_monitor_call(void)2711d49688dSHeiko Carstens static void __init test_monitor_call(void)
27217248ea0SSven Schnelle {
27317248ea0SSven Schnelle 	int val = 1;
27417248ea0SSven Schnelle 
275b8e9cc20SIlya Leoshkevich 	if (!IS_ENABLED(CONFIG_BUG))
276b8e9cc20SIlya Leoshkevich 		return;
27717248ea0SSven Schnelle 	asm volatile(
27817248ea0SSven Schnelle 		"	mc	0,0\n"
279*272abd47SHeiko Carstens 		"0:	lhi	%[val],0\n"
28017248ea0SSven Schnelle 		"1:\n"
28117248ea0SSven Schnelle 		EX_TABLE(0b, 1b)
282*272abd47SHeiko Carstens 		: [val] "+d" (val));
28317248ea0SSven Schnelle 	if (!val)
28417248ea0SSven Schnelle 		panic("Monitor call doesn't work!\n");
28517248ea0SSven Schnelle }
28617248ea0SSven Schnelle 
trap_init(void)2871da177e4SLinus Torvalds void __init trap_init(void)
2881da177e4SLinus Torvalds {
289f3e1a273SHeiko Carstens 	local_mcck_enable();
29017248ea0SSven Schnelle 	test_monitor_call();
2911da177e4SLinus Torvalds }
29256e62a73SSven Schnelle 
2936f8daa29SHeiko Carstens static void (*pgm_check_table[128])(struct pt_regs *regs);
2946f8daa29SHeiko Carstens 
__do_pgm_check(struct pt_regs * regs)29556e62a73SSven Schnelle void noinstr __do_pgm_check(struct pt_regs *regs)
29656e62a73SSven Schnelle {
297fbf50f47SSven Schnelle 	unsigned int trapnr;
29856e62a73SSven Schnelle 	irqentry_state_t state;
29956e62a73SSven Schnelle 
30052b739e2SHeiko Carstens 	regs->int_code = S390_lowcore.pgm_int_code;
30156e62a73SSven Schnelle 	regs->int_parm_long = S390_lowcore.trans_exc_code;
30256e62a73SSven Schnelle 
30356e62a73SSven Schnelle 	state = irqentry_enter(regs);
30456e62a73SSven Schnelle 
30556e62a73SSven Schnelle 	if (user_mode(regs)) {
30656e62a73SSven Schnelle 		update_timer_sys();
3073b051e89SSven Schnelle 		if (!static_branch_likely(&cpu_has_bear)) {
3083b051e89SSven Schnelle 			if (regs->last_break < 4096)
3093b051e89SSven Schnelle 				regs->last_break = 1;
3103b051e89SSven Schnelle 		}
3113b051e89SSven Schnelle 		current->thread.last_break = regs->last_break;
31256e62a73SSven Schnelle 	}
31356e62a73SSven Schnelle 
31456e62a73SSven Schnelle 	if (S390_lowcore.pgm_code & 0x0200) {
31556e62a73SSven Schnelle 		/* transaction abort */
316755112b3SSven Schnelle 		current->thread.trap_tdb = S390_lowcore.pgm_tdb;
31756e62a73SSven Schnelle 	}
31856e62a73SSven Schnelle 
31956e62a73SSven Schnelle 	if (S390_lowcore.pgm_code & PGM_INT_CODE_PER) {
32056e62a73SSven Schnelle 		if (user_mode(regs)) {
32156e62a73SSven Schnelle 			struct per_event *ev = &current->thread.per_event;
32256e62a73SSven Schnelle 
32356e62a73SSven Schnelle 			set_thread_flag(TIF_PER_TRAP);
32456e62a73SSven Schnelle 			ev->address = S390_lowcore.per_address;
325998e7800SHeiko Carstens 			ev->cause = S390_lowcore.per_code_combined;
32656e62a73SSven Schnelle 			ev->paid = S390_lowcore.per_access_id;
32756e62a73SSven Schnelle 		} else {
32856e62a73SSven Schnelle 			/* PER event in kernel is kprobes */
32956e62a73SSven Schnelle 			__arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER);
33056e62a73SSven Schnelle 			do_per_trap(regs);
33156e62a73SSven Schnelle 			goto out;
33256e62a73SSven Schnelle 		}
33356e62a73SSven Schnelle 	}
33456e62a73SSven Schnelle 
33556e62a73SSven Schnelle 	if (!irqs_disabled_flags(regs->psw.mask))
33656e62a73SSven Schnelle 		trace_hardirqs_on();
33756e62a73SSven Schnelle 	__arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER);
33856e62a73SSven Schnelle 
33956e62a73SSven Schnelle 	trapnr = regs->int_code & PGM_INT_CODE_MASK;
34056e62a73SSven Schnelle 	if (trapnr)
34156e62a73SSven Schnelle 		pgm_check_table[trapnr](regs);
34256e62a73SSven Schnelle out:
34356e62a73SSven Schnelle 	local_irq_disable();
34456e62a73SSven Schnelle 	irqentry_exit(regs, state);
34556e62a73SSven Schnelle }
3466f8daa29SHeiko Carstens 
3476f8daa29SHeiko Carstens /*
3486f8daa29SHeiko Carstens  * The program check table contains exactly 128 (0x00-0x7f) entries. Each
3496f8daa29SHeiko Carstens  * line defines the function to be called corresponding to the program check
3506f8daa29SHeiko Carstens  * interruption code.
3516f8daa29SHeiko Carstens  */
3526f8daa29SHeiko Carstens static void (*pgm_check_table[128])(struct pt_regs *regs) = {
3536f8daa29SHeiko Carstens 	[0x00]		= default_trap_handler,
3546f8daa29SHeiko Carstens 	[0x01]		= illegal_op,
3556f8daa29SHeiko Carstens 	[0x02]		= privileged_op,
3566f8daa29SHeiko Carstens 	[0x03]		= execute_exception,
3576f8daa29SHeiko Carstens 	[0x04]		= do_protection_exception,
3586f8daa29SHeiko Carstens 	[0x05]		= addressing_exception,
3596f8daa29SHeiko Carstens 	[0x06]		= specification_exception,
3606f8daa29SHeiko Carstens 	[0x07]		= data_exception,
3616f8daa29SHeiko Carstens 	[0x08]		= overflow_exception,
3626f8daa29SHeiko Carstens 	[0x09]		= divide_exception,
3636f8daa29SHeiko Carstens 	[0x0a]		= overflow_exception,
3646f8daa29SHeiko Carstens 	[0x0b]		= divide_exception,
3656f8daa29SHeiko Carstens 	[0x0c]		= hfp_overflow_exception,
3666f8daa29SHeiko Carstens 	[0x0d]		= hfp_underflow_exception,
3676f8daa29SHeiko Carstens 	[0x0e]		= hfp_significance_exception,
3686f8daa29SHeiko Carstens 	[0x0f]		= hfp_divide_exception,
3696f8daa29SHeiko Carstens 	[0x10]		= do_dat_exception,
3706f8daa29SHeiko Carstens 	[0x11]		= do_dat_exception,
371f09354ffSHeiko Carstens 	[0x12]		= translation_specification_exception,
3726f8daa29SHeiko Carstens 	[0x13]		= special_op_exception,
3736f8daa29SHeiko Carstens 	[0x14]		= default_trap_handler,
3746f8daa29SHeiko Carstens 	[0x15]		= operand_exception,
3756f8daa29SHeiko Carstens 	[0x16]		= default_trap_handler,
3766f8daa29SHeiko Carstens 	[0x17]		= default_trap_handler,
3776f8daa29SHeiko Carstens 	[0x18]		= transaction_exception,
3786f8daa29SHeiko Carstens 	[0x19]		= default_trap_handler,
3796f8daa29SHeiko Carstens 	[0x1a]		= default_trap_handler,
3806f8daa29SHeiko Carstens 	[0x1b]		= vector_exception,
3816f8daa29SHeiko Carstens 	[0x1c]		= space_switch_exception,
3826f8daa29SHeiko Carstens 	[0x1d]		= hfp_sqrt_exception,
3836f8daa29SHeiko Carstens 	[0x1e ... 0x37] = default_trap_handler,
3846f8daa29SHeiko Carstens 	[0x38]		= do_dat_exception,
3856f8daa29SHeiko Carstens 	[0x39]		= do_dat_exception,
3866f8daa29SHeiko Carstens 	[0x3a]		= do_dat_exception,
3876f8daa29SHeiko Carstens 	[0x3b]		= do_dat_exception,
3886f8daa29SHeiko Carstens 	[0x3c]		= default_trap_handler,
3896f8daa29SHeiko Carstens 	[0x3d]		= do_secure_storage_access,
3906f8daa29SHeiko Carstens 	[0x3e]		= do_non_secure_storage_access,
3916f8daa29SHeiko Carstens 	[0x3f]		= do_secure_storage_violation,
3926f8daa29SHeiko Carstens 	[0x40]		= monitor_event_exception,
3936f8daa29SHeiko Carstens 	[0x41 ... 0x7f] = default_trap_handler,
3946f8daa29SHeiko Carstens };
39517a363dcSHeiko Carstens 
39617a363dcSHeiko Carstens #define COND_TRAP(x) asm(			\
39717a363dcSHeiko Carstens 	".weak " __stringify(x) "\n\t"		\
39817a363dcSHeiko Carstens 	".set  " __stringify(x) ","		\
39917a363dcSHeiko Carstens 	__stringify(default_trap_handler))
40017a363dcSHeiko Carstens 
40117a363dcSHeiko Carstens COND_TRAP(do_secure_storage_access);
40217a363dcSHeiko Carstens COND_TRAP(do_non_secure_storage_access);
40317a363dcSHeiko Carstens COND_TRAP(do_secure_storage_violation);
404