traps.c 18.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 *  arch/s390/kernel/traps.c
 *
 *  S390 version
 *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
 *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
 *
 *  Derived from "arch/i386/kernel/traps.c"
 *    Copyright (C) 1991, 1992 Linus Torvalds
 */

/*
 * 'Traps.c' handles hardware traps and faults after we have saved some
 * state in 'asm.s'.
 */
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
21
#include <linux/ptrace.h>
L
Linus Torvalds 已提交
22 23 24 25 26
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/interrupt.h>
27
#include <linux/seq_file.h>
L
Linus Torvalds 已提交
28 29
#include <linux/delay.h>
#include <linux/module.h>
30
#include <linux/kdebug.h>
L
Linus Torvalds 已提交
31
#include <linux/kallsyms.h>
32
#include <linux/reboot.h>
M
Michael Grundy 已提交
33
#include <linux/kprobes.h>
H
Heiko Carstens 已提交
34
#include <linux/bug.h>
35
#include <linux/utsname.h>
L
Linus Torvalds 已提交
36 37 38
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
A
Arun Sharma 已提交
39
#include <linux/atomic.h>
L
Linus Torvalds 已提交
40 41 42 43
#include <asm/mathemu.h>
#include <asm/cpcmd.h>
#include <asm/lowcore.h>
#include <asm/debug.h>
44
#include "entry.h"
L
Linus Torvalds 已提交
45

M
Martin Schwidefsky 已提交
46
void (*pgm_check_table[128])(struct pt_regs *regs);
L
Linus Torvalds 已提交
47

48
int show_unhandled_signals = 1;
L
Linus Torvalds 已提交
49 50 51

#define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; })

52
#ifndef CONFIG_64BIT
53
#define LONG "%08lx "
L
Linus Torvalds 已提交
54 55
#define FOURLONG "%08lx %08lx %08lx %08lx\n"
static int kstack_depth_to_print = 12;
56
#else /* CONFIG_64BIT */
57
#define LONG "%016lx "
L
Linus Torvalds 已提交
58 59
#define FOURLONG "%016lx %016lx %016lx %016lx\n"
static int kstack_depth_to_print = 20;
60
#endif /* CONFIG_64BIT */
L
Linus Torvalds 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106

/*
 * For show_trace we have tree different stack to consider:
 *   - the panic stack which is used if the kernel stack has overflown
 *   - the asynchronous interrupt stack (cpu related)
 *   - the synchronous kernel stack (process related)
 * The stack trace can start at any of the three stack and can potentially
 * touch all of them. The order is: panic stack, async stack, sync stack.
 */
static unsigned long
__show_trace(unsigned long sp, unsigned long low, unsigned long high)
{
	struct stack_frame *sf;
	struct pt_regs *regs;

	while (1) {
		sp = sp & PSW_ADDR_INSN;
		if (sp < low || sp > high - sizeof(*sf))
			return sp;
		sf = (struct stack_frame *) sp;
		printk("([<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN);
		print_symbol("%s)\n", sf->gprs[8] & PSW_ADDR_INSN);
		/* Follow the backchain. */
		while (1) {
			low = sp;
			sp = sf->back_chain & PSW_ADDR_INSN;
			if (!sp)
				break;
			if (sp <= low || sp > high - sizeof(*sf))
				return sp;
			sf = (struct stack_frame *) sp;
			printk(" [<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN);
			print_symbol("%s\n", sf->gprs[8] & PSW_ADDR_INSN);
		}
		/* Zero backchain detected, check for interrupt frame. */
		sp = (unsigned long) (sf + 1);
		if (sp <= low || sp > high - sizeof(*regs))
			return sp;
		regs = (struct pt_regs *) sp;
		printk(" [<%016lx>] ", regs->psw.addr & PSW_ADDR_INSN);
		print_symbol("%s\n", regs->psw.addr & PSW_ADDR_INSN);
		low = sp;
		sp = regs->gprs[15];
	}
}

107
static void show_trace(struct task_struct *task, unsigned long *stack)
L
Linus Torvalds 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
{
	register unsigned long __r15 asm ("15");
	unsigned long sp;

	sp = (unsigned long) stack;
	if (!sp)
		sp = task ? task->thread.ksp : __r15;
	printk("Call Trace:\n");
#ifdef CONFIG_CHECK_STACK
	sp = __show_trace(sp, S390_lowcore.panic_stack - 4096,
			  S390_lowcore.panic_stack);
#endif
	sp = __show_trace(sp, S390_lowcore.async_stack - ASYNC_SIZE,
			  S390_lowcore.async_stack);
	if (task)
A
Al Viro 已提交
123 124
		__show_trace(sp, (unsigned long) task_stack_page(task),
			     (unsigned long) task_stack_page(task) + THREAD_SIZE);
L
Linus Torvalds 已提交
125 126 127
	else
		__show_trace(sp, S390_lowcore.thread_info,
			     S390_lowcore.thread_info + THREAD_SIZE);
128 129 130
	if (!task)
		task = current;
	debug_show_held_locks(task);
L
Linus Torvalds 已提交
131 132 133 134 135 136 137 138 139
}

void show_stack(struct task_struct *task, unsigned long *sp)
{
	register unsigned long * __r15 asm ("15");
	unsigned long *stack;
	int i;

	if (!sp)
H
Heiko Carstens 已提交
140 141 142
		stack = task ? (unsigned long *) task->thread.ksp : __r15;
	else
		stack = sp;
L
Linus Torvalds 已提交
143 144 145 146 147 148

	for (i = 0; i < kstack_depth_to_print; i++) {
		if (((addr_t) stack & (THREAD_SIZE-1)) == 0)
			break;
		if (i && ((i * sizeof (long) % 32) == 0))
			printk("\n       ");
149
		printk(LONG, *stack++);
L
Linus Torvalds 已提交
150 151 152 153 154
	}
	printk("\n");
	show_trace(task, sp);
}

155
static void show_last_breaking_event(struct pt_regs *regs)
156
{
157
#ifdef CONFIG_64BIT
158 159 160 161
	printk("Last Breaking-Event-Address:\n");
	printk(" [<%016lx>] ", regs->args[0] & PSW_ADDR_INSN);
	print_symbol("%s\n", regs->args[0] & PSW_ADDR_INSN);
#endif
162
}
163

L
Linus Torvalds 已提交
164 165 166 167 168
/*
 * The architecture-independent dump_stack generator
 */
void dump_stack(void)
{
169 170 171 172 173 174 175 176
	printk("CPU: %d %s %s %.*s\n",
	       task_thread_info(current)->cpu, print_tainted(),
	       init_utsname()->release,
	       (int)strcspn(init_utsname()->version, " "),
	       init_utsname()->version);
	printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
	       current->comm, current->pid, current,
	       (void *) current->thread.ksp);
H
Heiko Carstens 已提交
177
	show_stack(NULL, NULL);
L
Linus Torvalds 已提交
178 179 180
}
EXPORT_SYMBOL(dump_stack);

181 182 183 184 185
static inline int mask_bits(struct pt_regs *regs, unsigned long bits)
{
	return (regs->psw.mask & bits) / ((~bits + 1) & bits);
}

L
Linus Torvalds 已提交
186 187 188 189 190 191 192 193 194
void show_registers(struct pt_regs *regs)
{
	char *mode;

	mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl";
	printk("%s PSW : %p %p",
	       mode, (void *) regs->psw.mask,
	       (void *) regs->psw.addr);
	print_symbol(" (%s)\n", regs->psw.addr & PSW_ADDR_INSN);
195 196 197 198 199 200 201 202
	printk("           R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x "
	       "P:%x AS:%x CC:%x PM:%x", mask_bits(regs, PSW_MASK_PER),
	       mask_bits(regs, PSW_MASK_DAT), mask_bits(regs, PSW_MASK_IO),
	       mask_bits(regs, PSW_MASK_EXT), mask_bits(regs, PSW_MASK_KEY),
	       mask_bits(regs, PSW_MASK_MCHECK), mask_bits(regs, PSW_MASK_WAIT),
	       mask_bits(regs, PSW_MASK_PSTATE), mask_bits(regs, PSW_MASK_ASC),
	       mask_bits(regs, PSW_MASK_CC), mask_bits(regs, PSW_MASK_PM));
#ifdef CONFIG_64BIT
203
	printk(" EA:%x", mask_bits(regs, PSW_MASK_EA | PSW_MASK_BA));
204 205
#endif
	printk("\n%s GPRS: " FOURLONG, mode,
L
Linus Torvalds 已提交
206 207 208 209 210 211 212 213
	       regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]);
	printk("           " FOURLONG,
	       regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]);
	printk("           " FOURLONG,
	       regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]);
	printk("           " FOURLONG,
	       regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]);

214
	show_code(regs);
L
Linus Torvalds 已提交
215 216
}	

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
void show_regs(struct pt_regs *regs)
{
	print_modules();
	printk("CPU: %d %s %s %.*s\n",
	       task_thread_info(current)->cpu, print_tainted(),
	       init_utsname()->release,
	       (int)strcspn(init_utsname()->version, " "),
	       init_utsname()->version);
	printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
	       current->comm, current->pid, current,
	       (void *) current->thread.ksp);
	show_registers(regs);
	/* Show stack backtrace if pt_regs is from kernel mode */
	if (!(regs->psw.mask & PSW_MASK_PSTATE))
		show_trace(NULL, (unsigned long *) regs->gprs[15]);
	show_last_breaking_event(regs);
}

235
static DEFINE_SPINLOCK(die_lock);
L
Linus Torvalds 已提交
236

M
Martin Schwidefsky 已提交
237
void die(struct pt_regs *regs, const char *str)
L
Linus Torvalds 已提交
238 239 240
{
	static int die_counter;

241
	oops_enter();
L
Linus Torvalds 已提交
242 243 244 245
	debug_stop_all();
	console_verbose();
	spin_lock_irq(&die_lock);
	bust_spinlocks(1);
M
Martin Schwidefsky 已提交
246
	printk("%s: %04x [#%d] ", str, regs->int_code & 0xffff, ++die_counter);
247 248 249 250
#ifdef CONFIG_PREEMPT
	printk("PREEMPT ");
#endif
#ifdef CONFIG_SMP
251 252 253 254
	printk("SMP ");
#endif
#ifdef CONFIG_DEBUG_PAGEALLOC
	printk("DEBUG_PAGEALLOC");
255 256
#endif
	printk("\n");
M
Martin Schwidefsky 已提交
257
	notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV);
258
	show_regs(regs);
L
Linus Torvalds 已提交
259
	bust_spinlocks(0);
260
	add_taint(TAINT_DIE);
261
	spin_unlock_irq(&die_lock);
L
Linus Torvalds 已提交
262 263 264 265
	if (in_interrupt())
		panic("Fatal exception in interrupt");
	if (panic_on_oops)
		panic("Fatal exception: panic_on_oops");
266 267
	oops_exit();
	do_exit(SIGSEGV);
L
Linus Torvalds 已提交
268 269
}

M
Martin Schwidefsky 已提交
270
static inline void report_user_fault(struct pt_regs *regs, int signr)
L
Linus Torvalds 已提交
271
{
272
	if ((task_pid_nr(current) > 1) && !show_unhandled_signals)
L
Linus Torvalds 已提交
273
		return;
274 275 276 277
	if (!unhandled_signal(current, signr))
		return;
	if (!printk_ratelimit())
		return;
M
Martin Schwidefsky 已提交
278
	printk("User process fault: interruption code 0x%X ", regs->int_code);
279 280
	print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN);
	printk("\n");
L
Linus Torvalds 已提交
281 282 283
	show_regs(regs);
}

H
Heiko Carstens 已提交
284 285 286 287 288
int is_valid_bugaddr(unsigned long addr)
{
	return 1;
}

M
Martin Schwidefsky 已提交
289
static inline void __user *get_psw_address(struct pt_regs *regs)
L
Linus Torvalds 已提交
290
{
M
Martin Schwidefsky 已提交
291 292 293 294 295 296 297 298 299 300 301
	return (void __user *)
		((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN);
}

static void __kprobes do_trap(struct pt_regs *regs,
			      int si_signo, int si_code, char *str)
{
	siginfo_t info;

	if (notify_die(DIE_TRAP, str, regs, 0,
		       regs->int_code, si_signo) == NOTIFY_STOP)
M
Michael Grundy 已提交
302 303
		return;

L
Linus Torvalds 已提交
304
        if (regs->psw.mask & PSW_MASK_PSTATE) {
M
Martin Schwidefsky 已提交
305 306 307 308 309 310
		info.si_signo = si_signo;
		info.si_errno = 0;
		info.si_code = si_code;
		info.si_addr = get_psw_address(regs);
		force_sig_info(si_signo, &info, current);
		report_user_fault(regs, si_signo);
L
Linus Torvalds 已提交
311 312 313 314 315
        } else {
                const struct exception_table_entry *fixup;
                fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
                if (fixup)
                        regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
H
Heiko Carstens 已提交
316 317 318
		else {
			enum bug_trap_type btt;

319
			btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs);
H
Heiko Carstens 已提交
320 321
			if (btt == BUG_TRAP_TYPE_WARN)
				return;
M
Martin Schwidefsky 已提交
322
			die(regs, str);
H
Heiko Carstens 已提交
323
		}
L
Linus Torvalds 已提交
324 325 326
        }
}

M
Martin Schwidefsky 已提交
327
void __kprobes do_per_trap(struct pt_regs *regs)
L
Linus Torvalds 已提交
328
{
329 330
	siginfo_t info;

M
Martin Schwidefsky 已提交
331
	if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
M
Michael Grundy 已提交
332
		return;
333 334 335 336 337
	if (!current->ptrace)
		return;
	info.si_signo = SIGTRAP;
	info.si_errno = 0;
	info.si_code = TRAP_HWBKPT;
338 339
	info.si_addr =
		(void __force __user *) current->thread.per_event.address;
340
	force_sig_info(SIGTRAP, &info, current);
L
Linus Torvalds 已提交
341 342
}

M
Martin Schwidefsky 已提交
343
static void default_trap_handler(struct pt_regs *regs)
L
Linus Torvalds 已提交
344 345
{
        if (regs->psw.mask & PSW_MASK_PSTATE) {
M
Martin Schwidefsky 已提交
346
		report_user_fault(regs, SIGSEGV);
347
		do_exit(SIGSEGV);
L
Linus Torvalds 已提交
348
	} else
M
Martin Schwidefsky 已提交
349
		die(regs, "Unknown program exception");
L
Linus Torvalds 已提交
350 351
}

352
#define DO_ERROR_INFO(name, signr, sicode, str) \
M
Martin Schwidefsky 已提交
353
static void name(struct pt_regs *regs) \
L
Linus Torvalds 已提交
354
{ \
M
Martin Schwidefsky 已提交
355
	do_trap(regs, signr, sicode, str); \
L
Linus Torvalds 已提交
356 357
}

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
	      "addressing exception")
DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
	      "execute exception")
DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
	      "fixpoint divide exception")
DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
	      "fixpoint overflow exception")
DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
	      "HFP overflow exception")
DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
	      "HFP underflow exception")
DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
	      "HFP significance exception")
DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
	      "HFP divide exception")
DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
	      "HFP square root exception")
DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
	      "operand exception")
DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
	      "privileged operation")
DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
	      "special operation exception")
DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN,
	      "translation exception")

M
Martin Schwidefsky 已提交
385
static inline void do_fp_trap(struct pt_regs *regs, int fpc)
L
Linus Torvalds 已提交
386
{
M
Martin Schwidefsky 已提交
387
	int si_code = 0;
L
Linus Torvalds 已提交
388 389 390 391
	/* FPC[2] is Data Exception Code */
	if ((fpc & 0x00000300) == 0) {
		/* bits 6 and 7 of DXC are 0 iff IEEE exception */
		if (fpc & 0x8000) /* invalid fp operation */
M
Martin Schwidefsky 已提交
392
			si_code = FPE_FLTINV;
L
Linus Torvalds 已提交
393
		else if (fpc & 0x4000) /* div by 0 */
M
Martin Schwidefsky 已提交
394
			si_code = FPE_FLTDIV;
L
Linus Torvalds 已提交
395
		else if (fpc & 0x2000) /* overflow */
M
Martin Schwidefsky 已提交
396
			si_code = FPE_FLTOVF;
L
Linus Torvalds 已提交
397
		else if (fpc & 0x1000) /* underflow */
M
Martin Schwidefsky 已提交
398
			si_code = FPE_FLTUND;
L
Linus Torvalds 已提交
399
		else if (fpc & 0x0800) /* inexact */
M
Martin Schwidefsky 已提交
400
			si_code = FPE_FLTRES;
L
Linus Torvalds 已提交
401
	}
M
Martin Schwidefsky 已提交
402
	do_trap(regs, SIGFPE, si_code, "floating point exception");
L
Linus Torvalds 已提交
403 404
}

M
Martin Schwidefsky 已提交
405
static void __kprobes illegal_op(struct pt_regs *regs)
L
Linus Torvalds 已提交
406 407 408
{
	siginfo_t info;
        __u8 opcode[6];
H
Heiko Carstens 已提交
409
	__u16 __user *location;
L
Linus Torvalds 已提交
410 411
	int signal = 0;

M
Martin Schwidefsky 已提交
412
	location = get_psw_address(regs);
L
Linus Torvalds 已提交
413 414

	if (regs->psw.mask & PSW_MASK_PSTATE) {
H
Heiko Carstens 已提交
415 416
		if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
			return;
L
Linus Torvalds 已提交
417
		if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
418 419 420 421 422 423 424
			if (current->ptrace) {
				info.si_signo = SIGTRAP;
				info.si_errno = 0;
				info.si_code = TRAP_BRKPT;
				info.si_addr = location;
				force_sig_info(SIGTRAP, &info, current);
			} else
L
Linus Torvalds 已提交
425 426 427
				signal = SIGILL;
#ifdef CONFIG_MATHEMU
		} else if (opcode[0] == 0xb3) {
H
Heiko Carstens 已提交
428 429
			if (get_user(*((__u16 *) (opcode+2)), location+1))
				return;
L
Linus Torvalds 已提交
430 431
			signal = math_emu_b3(opcode, regs);
                } else if (opcode[0] == 0xed) {
H
Heiko Carstens 已提交
432 433 434
			if (get_user(*((__u32 *) (opcode+2)),
				     (__u32 __user *)(location+1)))
				return;
L
Linus Torvalds 已提交
435 436
			signal = math_emu_ed(opcode, regs);
		} else if (*((__u16 *) opcode) == 0xb299) {
H
Heiko Carstens 已提交
437 438
			if (get_user(*((__u16 *) (opcode+2)), location+1))
				return;
L
Linus Torvalds 已提交
439 440
			signal = math_emu_srnm(opcode, regs);
		} else if (*((__u16 *) opcode) == 0xb29c) {
H
Heiko Carstens 已提交
441 442
			if (get_user(*((__u16 *) (opcode+2)), location+1))
				return;
L
Linus Torvalds 已提交
443 444
			signal = math_emu_stfpc(opcode, regs);
		} else if (*((__u16 *) opcode) == 0xb29d) {
H
Heiko Carstens 已提交
445 446
			if (get_user(*((__u16 *) (opcode+2)), location+1))
				return;
L
Linus Torvalds 已提交
447 448 449 450
			signal = math_emu_lfpc(opcode, regs);
#endif
		} else
			signal = SIGILL;
451 452 453 454 455
	} else {
		/*
		 * If we get an illegal op in kernel mode, send it through the
		 * kprobes notifier. If kprobes doesn't pick it up, SIGILL
		 */
M
Martin Schwidefsky 已提交
456
		if (notify_die(DIE_BPT, "bpt", regs, 0,
457 458 459
			       3, SIGTRAP) != NOTIFY_STOP)
			signal = SIGILL;
	}
L
Linus Torvalds 已提交
460 461 462

#ifdef CONFIG_MATHEMU
        if (signal == SIGFPE)
M
Martin Schwidefsky 已提交
463 464 465 466
		do_fp_trap(regs, current->thread.fp_regs.fpc);
	else if (signal == SIGSEGV)
		do_trap(regs, signal, SEGV_MAPERR, "user address fault");
	else
L
Linus Torvalds 已提交
467
#endif
M
Martin Schwidefsky 已提交
468 469
	if (signal)
		do_trap(regs, signal, ILL_ILLOPC, "illegal operation");
L
Linus Torvalds 已提交
470 471 472 473
}


#ifdef CONFIG_MATHEMU
M
Martin Schwidefsky 已提交
474
void specification_exception(struct pt_regs *regs)
L
Linus Torvalds 已提交
475 476
{
        __u8 opcode[6];
A
Al Viro 已提交
477
	__u16 __user *location = NULL;
L
Linus Torvalds 已提交
478 479
	int signal = 0;

M
Martin Schwidefsky 已提交
480
	location = (__u16 __user *) get_psw_address(regs);
L
Linus Torvalds 已提交
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514

        if (regs->psw.mask & PSW_MASK_PSTATE) {
		get_user(*((__u16 *) opcode), location);
		switch (opcode[0]) {
		case 0x28: /* LDR Rx,Ry   */
			signal = math_emu_ldr(opcode);
			break;
		case 0x38: /* LER Rx,Ry   */
			signal = math_emu_ler(opcode);
			break;
		case 0x60: /* STD R,D(X,B) */
			get_user(*((__u16 *) (opcode+2)), location+1);
			signal = math_emu_std(opcode, regs);
			break;
		case 0x68: /* LD R,D(X,B) */
			get_user(*((__u16 *) (opcode+2)), location+1);
			signal = math_emu_ld(opcode, regs);
			break;
		case 0x70: /* STE R,D(X,B) */
			get_user(*((__u16 *) (opcode+2)), location+1);
			signal = math_emu_ste(opcode, regs);
			break;
		case 0x78: /* LE R,D(X,B) */
			get_user(*((__u16 *) (opcode+2)), location+1);
			signal = math_emu_le(opcode, regs);
			break;
		default:
			signal = SIGILL;
			break;
                }
        } else
		signal = SIGILL;

        if (signal == SIGFPE)
M
Martin Schwidefsky 已提交
515 516 517
		do_fp_trap(regs, current->thread.fp_regs.fpc);
	else if (signal)
		do_trap(regs, signal, ILL_ILLOPN, "specification exception");
L
Linus Torvalds 已提交
518 519
}
#else
520 521
DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
	      "specification exception");
L
Linus Torvalds 已提交
522 523
#endif

M
Martin Schwidefsky 已提交
524
static void data_exception(struct pt_regs *regs)
L
Linus Torvalds 已提交
525
{
H
Heiko Carstens 已提交
526
	__u16 __user *location;
L
Linus Torvalds 已提交
527 528
	int signal = 0;

M
Martin Schwidefsky 已提交
529
	location = get_psw_address(regs);
L
Linus Torvalds 已提交
530 531

	if (MACHINE_HAS_IEEE)
532
		asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
L
Linus Torvalds 已提交
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566

#ifdef CONFIG_MATHEMU
        else if (regs->psw.mask & PSW_MASK_PSTATE) {
        	__u8 opcode[6];
		get_user(*((__u16 *) opcode), location);
		switch (opcode[0]) {
		case 0x28: /* LDR Rx,Ry   */
			signal = math_emu_ldr(opcode);
			break;
		case 0x38: /* LER Rx,Ry   */
			signal = math_emu_ler(opcode);
			break;
		case 0x60: /* STD R,D(X,B) */
			get_user(*((__u16 *) (opcode+2)), location+1);
			signal = math_emu_std(opcode, regs);
			break;
		case 0x68: /* LD R,D(X,B) */
			get_user(*((__u16 *) (opcode+2)), location+1);
			signal = math_emu_ld(opcode, regs);
			break;
		case 0x70: /* STE R,D(X,B) */
			get_user(*((__u16 *) (opcode+2)), location+1);
			signal = math_emu_ste(opcode, regs);
			break;
		case 0x78: /* LE R,D(X,B) */
			get_user(*((__u16 *) (opcode+2)), location+1);
			signal = math_emu_le(opcode, regs);
			break;
		case 0xb3:
			get_user(*((__u16 *) (opcode+2)), location+1);
			signal = math_emu_b3(opcode, regs);
			break;
                case 0xed:
			get_user(*((__u32 *) (opcode+2)),
A
Al Viro 已提交
567
				 (__u32 __user *)(location+1));
L
Linus Torvalds 已提交
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
			signal = math_emu_ed(opcode, regs);
			break;
	        case 0xb2:
			if (opcode[1] == 0x99) {
				get_user(*((__u16 *) (opcode+2)), location+1);
				signal = math_emu_srnm(opcode, regs);
			} else if (opcode[1] == 0x9c) {
				get_user(*((__u16 *) (opcode+2)), location+1);
				signal = math_emu_stfpc(opcode, regs);
			} else if (opcode[1] == 0x9d) {
				get_user(*((__u16 *) (opcode+2)), location+1);
				signal = math_emu_lfpc(opcode, regs);
			} else
				signal = SIGILL;
			break;
		default:
			signal = SIGILL;
			break;
                }
        }
#endif 
	if (current->thread.fp_regs.fpc & FPC_DXC_MASK)
		signal = SIGFPE;
	else
		signal = SIGILL;
        if (signal == SIGFPE)
M
Martin Schwidefsky 已提交
594 595 596
		do_fp_trap(regs, current->thread.fp_regs.fpc);
	else if (signal)
		do_trap(regs, signal, ILL_ILLOPN, "data exception");
L
Linus Torvalds 已提交
597 598
}

M
Martin Schwidefsky 已提交
599
static void space_switch_exception(struct pt_regs *regs)
L
Linus Torvalds 已提交
600 601 602 603 604
{
	/* Set user psw back to home space mode. */
	if (regs->psw.mask & PSW_MASK_PSTATE)
		regs->psw.mask |= PSW_ASC_HOME;
	/* Send SIGILL. */
M
Martin Schwidefsky 已提交
605
	do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
L
Linus Torvalds 已提交
606 607
}

608
void __kprobes kernel_stack_overflow(struct pt_regs * regs)
L
Linus Torvalds 已提交
609
{
610 611 612 613
	bust_spinlocks(1);
	printk("Kernel stack overflow.\n");
	show_regs(regs);
	bust_spinlocks(0);
L
Linus Torvalds 已提交
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
	panic("Corrupt kernel stack, can't continue.");
}

/* init is done in lowcore.S and head.S */

void __init trap_init(void)
{
        int i;

        for (i = 0; i < 128; i++)
          pgm_check_table[i] = &default_trap_handler;
        pgm_check_table[1] = &illegal_op;
        pgm_check_table[2] = &privileged_op;
        pgm_check_table[3] = &execute_exception;
        pgm_check_table[4] = &do_protection_exception;
        pgm_check_table[5] = &addressing_exception;
        pgm_check_table[6] = &specification_exception;
        pgm_check_table[7] = &data_exception;
        pgm_check_table[8] = &overflow_exception;
        pgm_check_table[9] = &divide_exception;
        pgm_check_table[0x0A] = &overflow_exception;
        pgm_check_table[0x0B] = &divide_exception;
        pgm_check_table[0x0C] = &hfp_overflow_exception;
        pgm_check_table[0x0D] = &hfp_underflow_exception;
        pgm_check_table[0x0E] = &hfp_significance_exception;
        pgm_check_table[0x0F] = &hfp_divide_exception;
        pgm_check_table[0x10] = &do_dat_exception;
        pgm_check_table[0x11] = &do_dat_exception;
        pgm_check_table[0x12] = &translation_exception;
        pgm_check_table[0x13] = &special_op_exception;
644
#ifdef CONFIG_64BIT
M
Martin Schwidefsky 已提交
645
	pgm_check_table[0x38] = &do_asce_exception;
L
Linus Torvalds 已提交
646 647 648
	pgm_check_table[0x39] = &do_dat_exception;
	pgm_check_table[0x3A] = &do_dat_exception;
        pgm_check_table[0x3B] = &do_dat_exception;
649
#endif /* CONFIG_64BIT */
L
Linus Torvalds 已提交
650 651 652
        pgm_check_table[0x15] = &operand_exception;
        pgm_check_table[0x1C] = &space_switch_exception;
        pgm_check_table[0x1D] = &hfp_sqrt_exception;
653 654
	/* Enable machine checks early. */
	local_mcck_enable();
L
Linus Torvalds 已提交
655
}