2005-04-16 16:20:36 -06:00
|
|
|
/*
|
|
|
|
* 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>
|
|
|
|
#include <linux/ptrace.h>
|
|
|
|
#include <linux/timer.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/interrupt.h>
|
2008-02-08 05:18:33 -07:00
|
|
|
#include <linux/seq_file.h>
|
2005-04-16 16:20:36 -06:00
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/module.h>
|
2007-05-08 01:27:03 -06:00
|
|
|
#include <linux/kdebug.h>
|
2005-04-16 16:20:36 -06:00
|
|
|
#include <linux/kallsyms.h>
|
2005-08-01 22:11:33 -06:00
|
|
|
#include <linux/reboot.h>
|
2006-09-20 07:58:39 -06:00
|
|
|
#include <linux/kprobes.h>
|
2007-04-27 08:01:42 -06:00
|
|
|
#include <linux/bug.h>
|
2008-01-26 06:11:01 -07:00
|
|
|
#include <linux/utsname.h>
|
2005-04-16 16:20:36 -06:00
|
|
|
#include <asm/system.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/atomic.h>
|
|
|
|
#include <asm/mathemu.h>
|
|
|
|
#include <asm/cpcmd.h>
|
|
|
|
#include <asm/s390_ext.h>
|
|
|
|
#include <asm/lowcore.h>
|
|
|
|
#include <asm/debug.h>
|
2008-04-16 23:46:26 -06:00
|
|
|
#include "entry.h"
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
pgm_check_handler_t *pgm_check_table[128];
|
|
|
|
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
|
|
#ifdef CONFIG_PROCESS_DEBUG
|
|
|
|
int sysctl_userprocess_debug = 1;
|
|
|
|
#else
|
|
|
|
int sysctl_userprocess_debug = 0;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
extern pgm_check_handler_t do_protection_exception;
|
|
|
|
extern pgm_check_handler_t do_dat_exception;
|
2008-02-09 10:24:37 -07:00
|
|
|
extern pgm_check_handler_t do_asce_exception;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
#define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; })
|
|
|
|
|
2006-01-06 01:19:28 -07:00
|
|
|
#ifndef CONFIG_64BIT
|
2005-04-16 16:20:36 -06:00
|
|
|
#define FOURLONG "%08lx %08lx %08lx %08lx\n"
|
|
|
|
static int kstack_depth_to_print = 12;
|
2006-01-06 01:19:28 -07:00
|
|
|
#else /* CONFIG_64BIT */
|
2005-04-16 16:20:36 -06:00
|
|
|
#define FOURLONG "%016lx %016lx %016lx %016lx\n"
|
|
|
|
static int kstack_depth_to_print = 20;
|
2006-01-06 01:19:28 -07:00
|
|
|
#endif /* CONFIG_64BIT */
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-30 05:38:34 -06:00
|
|
|
static void show_trace(struct task_struct *task, unsigned long *stack)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
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)
|
2006-01-12 02:05:50 -07:00
|
|
|
__show_trace(sp, (unsigned long) task_stack_page(task),
|
|
|
|
(unsigned long) task_stack_page(task) + THREAD_SIZE);
|
2005-04-16 16:20:36 -06:00
|
|
|
else
|
|
|
|
__show_trace(sp, S390_lowcore.thread_info,
|
|
|
|
S390_lowcore.thread_info + THREAD_SIZE);
|
2006-12-04 07:40:47 -07:00
|
|
|
if (!task)
|
|
|
|
task = current;
|
|
|
|
debug_show_held_locks(task);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void show_stack(struct task_struct *task, unsigned long *sp)
|
|
|
|
{
|
|
|
|
register unsigned long * __r15 asm ("15");
|
|
|
|
unsigned long *stack;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!sp)
|
2006-06-29 06:56:23 -06:00
|
|
|
stack = task ? (unsigned long *) task->thread.ksp : __r15;
|
|
|
|
else
|
|
|
|
stack = sp;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
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 ");
|
|
|
|
printk("%p ", (void *)*stack++);
|
|
|
|
}
|
|
|
|
printk("\n");
|
|
|
|
show_trace(task, sp);
|
|
|
|
}
|
|
|
|
|
2008-04-30 05:38:34 -06:00
|
|
|
static void show_last_breaking_event(struct pt_regs *regs)
|
[S390] kernel: show last breaking-event-address on oops
Newer s390 models have a breaking-event-address-recording register.
Each time an instruction causes a break in the sequential instruction
execution, the address is saved in that hardware register. On a program
interrupt the address is copied to the lowcore address 272-279, which
makes it software accessible.
This patch changes the program check handler and the stack overflow
checker to copy the value into the pt_regs argument.
The oops output is enhanced to show the last known breaking address.
It might give additional information if the stack trace is corrupted.
The feature is only available on 64 bit.
The new oops output looks like:
[---------snip----------]
Modules linked in: vmcp sunrpc qeth_l2 dm_mod qeth ccwgroup
CPU: 2 Not tainted 2.6.24zlive-host #8
Process modprobe (pid: 4788, task: 00000000bf3d8718, ksp: 00000000b2b0b8e0)
Krnl PSW : 0704200180000000 000003e000020028 (vmcp_init+0x28/0xe4 [vmcp])
R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:0 CC:2 PM:0 EA:3
Krnl GPRS: 0000000004000002 000003e000020000 0000000000000000 0000000000000001
000000000015734c ffffffffffffffff 000003e0000b3b00 0000000000000000
000003e00007ca30 00000000b5bb5d40 00000000b5bb5800 000003e0000b3b00
000003e0000a2000 00000000003ecf50 00000000b2b0bd50 00000000b2b0bcb0
Krnl Code: 000003e000020018: c0c000040ff4 larl %r12,3e0000a2000
000003e00002001e: e3e0f0000024 stg %r14,0(%r15)
000003e000020024: a7f40001 brc 15,3e000020026
>000003e000020028: e310c0100004 lg %r1,16(%r12)
000003e00002002e: c020000413dc larl %r2,3e0000a27e6
000003e000020034: c0a00004aee6 larl %r10,3e0000b5e00
000003e00002003a: a7490001 lghi %r4,1
000003e00002003e: a75900f0 lghi %r5,240
Call Trace:
([<000000000014b300>] blocking_notifier_call_chain+0x2c/0x40)
[<000000000015735c>] sys_init_module+0x19d8/0x1b08
[<0000000000110afc>] sysc_noemu+0x10/0x16
[<000002000011cda2>] 0x2000011cda2
Last Breaking-Event-Address:
[<000003e000020024>] vmcp_init+0x24/0xe4 [vmcp]
[---------snip----------]
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
2008-04-16 23:46:30 -06:00
|
|
|
{
|
2008-04-30 05:38:34 -06:00
|
|
|
#ifdef CONFIG_64BIT
|
[S390] kernel: show last breaking-event-address on oops
Newer s390 models have a breaking-event-address-recording register.
Each time an instruction causes a break in the sequential instruction
execution, the address is saved in that hardware register. On a program
interrupt the address is copied to the lowcore address 272-279, which
makes it software accessible.
This patch changes the program check handler and the stack overflow
checker to copy the value into the pt_regs argument.
The oops output is enhanced to show the last known breaking address.
It might give additional information if the stack trace is corrupted.
The feature is only available on 64 bit.
The new oops output looks like:
[---------snip----------]
Modules linked in: vmcp sunrpc qeth_l2 dm_mod qeth ccwgroup
CPU: 2 Not tainted 2.6.24zlive-host #8
Process modprobe (pid: 4788, task: 00000000bf3d8718, ksp: 00000000b2b0b8e0)
Krnl PSW : 0704200180000000 000003e000020028 (vmcp_init+0x28/0xe4 [vmcp])
R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:0 CC:2 PM:0 EA:3
Krnl GPRS: 0000000004000002 000003e000020000 0000000000000000 0000000000000001
000000000015734c ffffffffffffffff 000003e0000b3b00 0000000000000000
000003e00007ca30 00000000b5bb5d40 00000000b5bb5800 000003e0000b3b00
000003e0000a2000 00000000003ecf50 00000000b2b0bd50 00000000b2b0bcb0
Krnl Code: 000003e000020018: c0c000040ff4 larl %r12,3e0000a2000
000003e00002001e: e3e0f0000024 stg %r14,0(%r15)
000003e000020024: a7f40001 brc 15,3e000020026
>000003e000020028: e310c0100004 lg %r1,16(%r12)
000003e00002002e: c020000413dc larl %r2,3e0000a27e6
000003e000020034: c0a00004aee6 larl %r10,3e0000b5e00
000003e00002003a: a7490001 lghi %r4,1
000003e00002003e: a75900f0 lghi %r5,240
Call Trace:
([<000000000014b300>] blocking_notifier_call_chain+0x2c/0x40)
[<000000000015735c>] sys_init_module+0x19d8/0x1b08
[<0000000000110afc>] sysc_noemu+0x10/0x16
[<000002000011cda2>] 0x2000011cda2
Last Breaking-Event-Address:
[<000003e000020024>] vmcp_init+0x24/0xe4 [vmcp]
[---------snip----------]
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
2008-04-16 23:46:30 -06:00
|
|
|
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
|
2008-04-30 05:38:34 -06:00
|
|
|
}
|
[S390] kernel: show last breaking-event-address on oops
Newer s390 models have a breaking-event-address-recording register.
Each time an instruction causes a break in the sequential instruction
execution, the address is saved in that hardware register. On a program
interrupt the address is copied to the lowcore address 272-279, which
makes it software accessible.
This patch changes the program check handler and the stack overflow
checker to copy the value into the pt_regs argument.
The oops output is enhanced to show the last known breaking address.
It might give additional information if the stack trace is corrupted.
The feature is only available on 64 bit.
The new oops output looks like:
[---------snip----------]
Modules linked in: vmcp sunrpc qeth_l2 dm_mod qeth ccwgroup
CPU: 2 Not tainted 2.6.24zlive-host #8
Process modprobe (pid: 4788, task: 00000000bf3d8718, ksp: 00000000b2b0b8e0)
Krnl PSW : 0704200180000000 000003e000020028 (vmcp_init+0x28/0xe4 [vmcp])
R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:0 CC:2 PM:0 EA:3
Krnl GPRS: 0000000004000002 000003e000020000 0000000000000000 0000000000000001
000000000015734c ffffffffffffffff 000003e0000b3b00 0000000000000000
000003e00007ca30 00000000b5bb5d40 00000000b5bb5800 000003e0000b3b00
000003e0000a2000 00000000003ecf50 00000000b2b0bd50 00000000b2b0bcb0
Krnl Code: 000003e000020018: c0c000040ff4 larl %r12,3e0000a2000
000003e00002001e: e3e0f0000024 stg %r14,0(%r15)
000003e000020024: a7f40001 brc 15,3e000020026
>000003e000020028: e310c0100004 lg %r1,16(%r12)
000003e00002002e: c020000413dc larl %r2,3e0000a27e6
000003e000020034: c0a00004aee6 larl %r10,3e0000b5e00
000003e00002003a: a7490001 lghi %r4,1
000003e00002003e: a75900f0 lghi %r5,240
Call Trace:
([<000000000014b300>] blocking_notifier_call_chain+0x2c/0x40)
[<000000000015735c>] sys_init_module+0x19d8/0x1b08
[<0000000000110afc>] sysc_noemu+0x10/0x16
[<000002000011cda2>] 0x2000011cda2
Last Breaking-Event-Address:
[<000003e000020024>] vmcp_init+0x24/0xe4 [vmcp]
[---------snip----------]
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
2008-04-16 23:46:30 -06:00
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
/*
|
|
|
|
* The architecture-independent dump_stack generator
|
|
|
|
*/
|
|
|
|
void dump_stack(void)
|
|
|
|
{
|
2008-01-26 06:11:01 -07:00
|
|
|
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);
|
2006-07-12 08:41:55 -06:00
|
|
|
show_stack(NULL, NULL);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dump_stack);
|
|
|
|
|
[S390] Improved oops output.
This patch adds two improvements to the oops output. First it adds an
additional line after the PSW which decodes the different fields of it.
Second a disassembler is added that decodes the instructions surrounding
the faulting PSW. The output of a test oops now looks like this:
kernel BUG at init/main.c:419
illegal operation: 0001 [#1]
CPU: 0 Not tainted
Process swapper (pid: 0, task: 0000000000464968, ksp: 00000000004be000)
Krnl PSW : 0700000180000000 00000000000120b6 (rest_init+0x36/0x38)
R:0 T:1 IO:1 EX:1 Key:0 M:0 W:0 P:0 AS:0 CC:0 PM:0 EA:3
Krnl GPRS: 0000000000000003 00000000004ba017 0000000000000022 0000000000000001
000000000003a5f6 0000000000000000 00000000004be6a8 0000000000000000
0000000000000000 00000000004b8200 0000000000003a50 0000000000008000
0000000000516368 000000000033d008 00000000000120b2 00000000004bdee0
Krnl Code: 00000000000120a6: e3e0f0980024 stg %r14,152(%r15)
00000000000120ac: c0e500014296 brasl %r14,3a5d8
00000000000120b2: a7f40001 brc 15,120b4
>00000000000120b6: 0707 bcr 0,%r7
00000000000120b8: eb7ff0500024 stmg %r7,%r15,80(%r15)
00000000000120be: c0d000195825 larl %r13,33d108
00000000000120c4: a7f13f00 tmll %r15,16128
00000000000120c8: a7840001 brc 8,120ca
Call Trace:
([<00000000000120b2>] rest_init+0x32/0x38)
[<00000000004be614>] start_kernel+0x37c/0x410
[<0000000000012020>] _ehead+0x20/0x80
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
2007-04-27 08:01:41 -06:00
|
|
|
static inline int mask_bits(struct pt_regs *regs, unsigned long bits)
|
|
|
|
{
|
|
|
|
return (regs->psw.mask & bits) / ((~bits + 1) & bits);
|
|
|
|
}
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
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);
|
[S390] Improved oops output.
This patch adds two improvements to the oops output. First it adds an
additional line after the PSW which decodes the different fields of it.
Second a disassembler is added that decodes the instructions surrounding
the faulting PSW. The output of a test oops now looks like this:
kernel BUG at init/main.c:419
illegal operation: 0001 [#1]
CPU: 0 Not tainted
Process swapper (pid: 0, task: 0000000000464968, ksp: 00000000004be000)
Krnl PSW : 0700000180000000 00000000000120b6 (rest_init+0x36/0x38)
R:0 T:1 IO:1 EX:1 Key:0 M:0 W:0 P:0 AS:0 CC:0 PM:0 EA:3
Krnl GPRS: 0000000000000003 00000000004ba017 0000000000000022 0000000000000001
000000000003a5f6 0000000000000000 00000000004be6a8 0000000000000000
0000000000000000 00000000004b8200 0000000000003a50 0000000000008000
0000000000516368 000000000033d008 00000000000120b2 00000000004bdee0
Krnl Code: 00000000000120a6: e3e0f0980024 stg %r14,152(%r15)
00000000000120ac: c0e500014296 brasl %r14,3a5d8
00000000000120b2: a7f40001 brc 15,120b4
>00000000000120b6: 0707 bcr 0,%r7
00000000000120b8: eb7ff0500024 stmg %r7,%r15,80(%r15)
00000000000120be: c0d000195825 larl %r13,33d108
00000000000120c4: a7f13f00 tmll %r15,16128
00000000000120c8: a7840001 brc 8,120ca
Call Trace:
([<00000000000120b2>] rest_init+0x32/0x38)
[<00000000004be614>] start_kernel+0x37c/0x410
[<0000000000012020>] _ehead+0x20/0x80
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
2007-04-27 08:01:41 -06:00
|
|
|
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
|
|
|
|
printk(" EA:%x", mask_bits(regs, PSW_BASE_BITS));
|
|
|
|
#endif
|
|
|
|
printk("\n%s GPRS: " FOURLONG, mode,
|
2005-04-16 16:20:36 -06:00
|
|
|
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]);
|
|
|
|
|
[S390] Improved oops output.
This patch adds two improvements to the oops output. First it adds an
additional line after the PSW which decodes the different fields of it.
Second a disassembler is added that decodes the instructions surrounding
the faulting PSW. The output of a test oops now looks like this:
kernel BUG at init/main.c:419
illegal operation: 0001 [#1]
CPU: 0 Not tainted
Process swapper (pid: 0, task: 0000000000464968, ksp: 00000000004be000)
Krnl PSW : 0700000180000000 00000000000120b6 (rest_init+0x36/0x38)
R:0 T:1 IO:1 EX:1 Key:0 M:0 W:0 P:0 AS:0 CC:0 PM:0 EA:3
Krnl GPRS: 0000000000000003 00000000004ba017 0000000000000022 0000000000000001
000000000003a5f6 0000000000000000 00000000004be6a8 0000000000000000
0000000000000000 00000000004b8200 0000000000003a50 0000000000008000
0000000000516368 000000000033d008 00000000000120b2 00000000004bdee0
Krnl Code: 00000000000120a6: e3e0f0980024 stg %r14,152(%r15)
00000000000120ac: c0e500014296 brasl %r14,3a5d8
00000000000120b2: a7f40001 brc 15,120b4
>00000000000120b6: 0707 bcr 0,%r7
00000000000120b8: eb7ff0500024 stmg %r7,%r15,80(%r15)
00000000000120be: c0d000195825 larl %r13,33d108
00000000000120c4: a7f13f00 tmll %r15,16128
00000000000120c8: a7840001 brc 8,120ca
Call Trace:
([<00000000000120b2>] rest_init+0x32/0x38)
[<00000000004be614>] start_kernel+0x37c/0x410
[<0000000000012020>] _ehead+0x20/0x80
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
2007-04-27 08:01:41 -06:00
|
|
|
show_code(regs);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
2008-04-30 05:38:34 -06:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
/* This is called from fs/proc/array.c */
|
2008-02-08 05:18:33 -07:00
|
|
|
void task_show_regs(struct seq_file *m, struct task_struct *task)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
struct pt_regs *regs;
|
|
|
|
|
2006-01-12 02:05:49 -07:00
|
|
|
regs = task_pt_regs(task);
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, "task: %p, ksp: %p\n",
|
2005-04-16 16:20:36 -06:00
|
|
|
task, (void *)task->thread.ksp);
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, "User PSW : %p %p\n",
|
2005-04-16 16:20:36 -06:00
|
|
|
(void *) regs->psw.mask, (void *)regs->psw.addr);
|
|
|
|
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, "User GPRS: " FOURLONG,
|
2005-04-16 16:20:36 -06:00
|
|
|
regs->gprs[0], regs->gprs[1],
|
|
|
|
regs->gprs[2], regs->gprs[3]);
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, " " FOURLONG,
|
2005-04-16 16:20:36 -06:00
|
|
|
regs->gprs[4], regs->gprs[5],
|
|
|
|
regs->gprs[6], regs->gprs[7]);
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, " " FOURLONG,
|
2005-04-16 16:20:36 -06:00
|
|
|
regs->gprs[8], regs->gprs[9],
|
|
|
|
regs->gprs[10], regs->gprs[11]);
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, " " FOURLONG,
|
2005-04-16 16:20:36 -06:00
|
|
|
regs->gprs[12], regs->gprs[13],
|
|
|
|
regs->gprs[14], regs->gprs[15]);
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, "User ACRS: %08x %08x %08x %08x\n",
|
2005-04-16 16:20:36 -06:00
|
|
|
task->thread.acrs[0], task->thread.acrs[1],
|
|
|
|
task->thread.acrs[2], task->thread.acrs[3]);
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, " %08x %08x %08x %08x\n",
|
2005-04-16 16:20:36 -06:00
|
|
|
task->thread.acrs[4], task->thread.acrs[5],
|
|
|
|
task->thread.acrs[6], task->thread.acrs[7]);
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, " %08x %08x %08x %08x\n",
|
2005-04-16 16:20:36 -06:00
|
|
|
task->thread.acrs[8], task->thread.acrs[9],
|
|
|
|
task->thread.acrs[10], task->thread.acrs[11]);
|
2008-02-08 05:18:33 -07:00
|
|
|
seq_printf(m, " %08x %08x %08x %08x\n",
|
2005-04-16 16:20:36 -06:00
|
|
|
task->thread.acrs[12], task->thread.acrs[13],
|
|
|
|
task->thread.acrs[14], task->thread.acrs[15]);
|
|
|
|
}
|
|
|
|
|
2007-02-05 13:16:47 -07:00
|
|
|
static DEFINE_SPINLOCK(die_lock);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
void die(const char * str, struct pt_regs * regs, long err)
|
|
|
|
{
|
|
|
|
static int die_counter;
|
|
|
|
|
2007-06-19 05:10:05 -06:00
|
|
|
oops_enter();
|
2005-04-16 16:20:36 -06:00
|
|
|
debug_stop_all();
|
|
|
|
console_verbose();
|
|
|
|
spin_lock_irq(&die_lock);
|
|
|
|
bust_spinlocks(1);
|
2008-01-26 06:11:01 -07:00
|
|
|
printk("%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
|
|
|
|
#ifdef CONFIG_PREEMPT
|
|
|
|
printk("PREEMPT ");
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SMP
|
2008-02-05 08:50:37 -07:00
|
|
|
printk("SMP ");
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEBUG_PAGEALLOC
|
|
|
|
printk("DEBUG_PAGEALLOC");
|
2008-01-26 06:11:01 -07:00
|
|
|
#endif
|
|
|
|
printk("\n");
|
2007-11-20 03:13:40 -07:00
|
|
|
notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV);
|
2007-06-19 05:10:05 -06:00
|
|
|
show_regs(regs);
|
2005-04-16 16:20:36 -06:00
|
|
|
bust_spinlocks(0);
|
2007-07-17 05:03:42 -06:00
|
|
|
add_taint(TAINT_DIE);
|
2007-06-19 05:10:05 -06:00
|
|
|
spin_unlock_irq(&die_lock);
|
2005-04-16 16:20:36 -06:00
|
|
|
if (in_interrupt())
|
|
|
|
panic("Fatal exception in interrupt");
|
|
|
|
if (panic_on_oops)
|
|
|
|
panic("Fatal exception: panic_on_oops");
|
2007-06-19 05:10:05 -06:00
|
|
|
oops_exit();
|
|
|
|
do_exit(SIGSEGV);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void inline
|
|
|
|
report_user_fault(long interruption_code, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_SYSCTL)
|
|
|
|
if (!sysctl_userprocess_debug)
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_SYSCTL) || defined(CONFIG_PROCESS_DEBUG)
|
|
|
|
printk("User process fault: interruption code 0x%lX\n",
|
|
|
|
interruption_code);
|
|
|
|
show_regs(regs);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2007-04-27 08:01:42 -06:00
|
|
|
int is_valid_bugaddr(unsigned long addr)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-09-20 07:58:39 -06:00
|
|
|
static void __kprobes inline do_trap(long interruption_code, int signr,
|
|
|
|
char *str, struct pt_regs *regs,
|
|
|
|
siginfo_t *info)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We got all needed information from the lowcore and can
|
|
|
|
* now safely switch on interrupts.
|
|
|
|
*/
|
|
|
|
if (regs->psw.mask & PSW_MASK_PSTATE)
|
|
|
|
local_irq_enable();
|
|
|
|
|
2006-09-20 07:58:39 -06:00
|
|
|
if (notify_die(DIE_TRAP, str, regs, interruption_code,
|
|
|
|
interruption_code, signr) == NOTIFY_STOP)
|
|
|
|
return;
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
|
|
|
struct task_struct *tsk = current;
|
|
|
|
|
|
|
|
tsk->thread.trap_no = interruption_code & 0xffff;
|
|
|
|
force_sig_info(signr, info, tsk);
|
|
|
|
report_user_fault(interruption_code, regs);
|
|
|
|
} 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;
|
2007-04-27 08:01:42 -06:00
|
|
|
else {
|
|
|
|
enum bug_trap_type btt;
|
|
|
|
|
2007-07-16 00:41:39 -06:00
|
|
|
btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs);
|
2007-04-27 08:01:42 -06:00
|
|
|
if (btt == BUG_TRAP_TYPE_WARN)
|
|
|
|
return;
|
|
|
|
die(str, regs, interruption_code);
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-12 08:41:55 -06:00
|
|
|
static inline void __user *get_check_address(struct pt_regs *regs)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2006-07-12 08:41:55 -06:00
|
|
|
return (void __user *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
2006-09-20 07:58:39 -06:00
|
|
|
void __kprobes do_single_step(struct pt_regs *regs)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2006-09-20 07:58:39 -06:00
|
|
|
if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0,
|
|
|
|
SIGTRAP) == NOTIFY_STOP){
|
|
|
|
return;
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
if ((current->ptrace & PT_PTRACED) != 0)
|
|
|
|
force_sig(SIGTRAP, current);
|
|
|
|
}
|
|
|
|
|
2007-02-05 13:16:47 -07:00
|
|
|
static void default_trap_handler(struct pt_regs * regs, long interruption_code)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
|
|
|
local_irq_enable();
|
|
|
|
do_exit(SIGSEGV);
|
|
|
|
report_user_fault(interruption_code, regs);
|
|
|
|
} else
|
|
|
|
die("Unknown program exception", regs, interruption_code);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DO_ERROR_INFO(signr, str, name, sicode, siaddr) \
|
2007-02-05 13:16:47 -07:00
|
|
|
static void name(struct pt_regs * regs, long interruption_code) \
|
2005-04-16 16:20:36 -06:00
|
|
|
{ \
|
|
|
|
siginfo_t info; \
|
|
|
|
info.si_signo = signr; \
|
|
|
|
info.si_errno = 0; \
|
|
|
|
info.si_code = sicode; \
|
2006-07-12 08:41:55 -06:00
|
|
|
info.si_addr = siaddr; \
|
2005-04-16 16:20:36 -06:00
|
|
|
do_trap(interruption_code, signr, str, regs, &info); \
|
|
|
|
}
|
|
|
|
|
|
|
|
DO_ERROR_INFO(SIGILL, "addressing exception", addressing_exception,
|
|
|
|
ILL_ILLADR, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGILL, "execute exception", execute_exception,
|
|
|
|
ILL_ILLOPN, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGFPE, "fixpoint divide exception", divide_exception,
|
|
|
|
FPE_INTDIV, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGFPE, "fixpoint overflow exception", overflow_exception,
|
|
|
|
FPE_INTOVF, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGFPE, "HFP overflow exception", hfp_overflow_exception,
|
|
|
|
FPE_FLTOVF, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGFPE, "HFP underflow exception", hfp_underflow_exception,
|
|
|
|
FPE_FLTUND, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGFPE, "HFP significance exception", hfp_significance_exception,
|
|
|
|
FPE_FLTRES, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGFPE, "HFP divide exception", hfp_divide_exception,
|
|
|
|
FPE_FLTDIV, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGFPE, "HFP square root exception", hfp_sqrt_exception,
|
|
|
|
FPE_FLTINV, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGILL, "operand exception", operand_exception,
|
|
|
|
ILL_ILLOPN, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGILL, "privileged operation", privileged_op,
|
|
|
|
ILL_PRVOPC, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGILL, "special operation exception", special_op_exception,
|
|
|
|
ILL_ILLOPN, get_check_address(regs))
|
|
|
|
DO_ERROR_INFO(SIGILL, "translation exception", translation_exception,
|
|
|
|
ILL_ILLOPN, get_check_address(regs))
|
|
|
|
|
|
|
|
static inline void
|
2006-07-12 08:41:55 -06:00
|
|
|
do_fp_trap(struct pt_regs *regs, void __user *location,
|
2005-04-16 16:20:36 -06:00
|
|
|
int fpc, long interruption_code)
|
|
|
|
{
|
|
|
|
siginfo_t si;
|
|
|
|
|
|
|
|
si.si_signo = SIGFPE;
|
|
|
|
si.si_errno = 0;
|
|
|
|
si.si_addr = location;
|
|
|
|
si.si_code = 0;
|
|
|
|
/* 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 */
|
|
|
|
si.si_code = FPE_FLTINV;
|
|
|
|
else if (fpc & 0x4000) /* div by 0 */
|
|
|
|
si.si_code = FPE_FLTDIV;
|
|
|
|
else if (fpc & 0x2000) /* overflow */
|
|
|
|
si.si_code = FPE_FLTOVF;
|
|
|
|
else if (fpc & 0x1000) /* underflow */
|
|
|
|
si.si_code = FPE_FLTUND;
|
|
|
|
else if (fpc & 0x0800) /* inexact */
|
|
|
|
si.si_code = FPE_FLTRES;
|
|
|
|
}
|
|
|
|
current->thread.ieee_instruction_pointer = (addr_t) location;
|
|
|
|
do_trap(interruption_code, SIGFPE,
|
|
|
|
"floating point exception", regs, &si);
|
|
|
|
}
|
|
|
|
|
2007-02-05 13:16:47 -07:00
|
|
|
static void illegal_op(struct pt_regs * regs, long interruption_code)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
siginfo_t info;
|
|
|
|
__u8 opcode[6];
|
2006-07-12 08:41:55 -06:00
|
|
|
__u16 __user *location;
|
2005-04-16 16:20:36 -06:00
|
|
|
int signal = 0;
|
|
|
|
|
2006-07-12 08:41:55 -06:00
|
|
|
location = get_check_address(regs);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We got all needed information from the lowcore and can
|
|
|
|
* now safely switch on interrupts.
|
|
|
|
*/
|
|
|
|
if (regs->psw.mask & PSW_MASK_PSTATE)
|
|
|
|
local_irq_enable();
|
|
|
|
|
|
|
|
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
2006-10-27 04:39:22 -06:00
|
|
|
if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
|
|
|
|
return;
|
2005-04-16 16:20:36 -06:00
|
|
|
if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
|
|
|
|
if (current->ptrace & PT_PTRACED)
|
|
|
|
force_sig(SIGTRAP, current);
|
|
|
|
else
|
|
|
|
signal = SIGILL;
|
|
|
|
#ifdef CONFIG_MATHEMU
|
|
|
|
} else if (opcode[0] == 0xb3) {
|
2006-10-27 04:39:22 -06:00
|
|
|
if (get_user(*((__u16 *) (opcode+2)), location+1))
|
|
|
|
return;
|
2005-04-16 16:20:36 -06:00
|
|
|
signal = math_emu_b3(opcode, regs);
|
|
|
|
} else if (opcode[0] == 0xed) {
|
2006-10-27 04:39:22 -06:00
|
|
|
if (get_user(*((__u32 *) (opcode+2)),
|
|
|
|
(__u32 __user *)(location+1)))
|
|
|
|
return;
|
2005-04-16 16:20:36 -06:00
|
|
|
signal = math_emu_ed(opcode, regs);
|
|
|
|
} else if (*((__u16 *) opcode) == 0xb299) {
|
2006-10-27 04:39:22 -06:00
|
|
|
if (get_user(*((__u16 *) (opcode+2)), location+1))
|
|
|
|
return;
|
2005-04-16 16:20:36 -06:00
|
|
|
signal = math_emu_srnm(opcode, regs);
|
|
|
|
} else if (*((__u16 *) opcode) == 0xb29c) {
|
2006-10-27 04:39:22 -06:00
|
|
|
if (get_user(*((__u16 *) (opcode+2)), location+1))
|
|
|
|
return;
|
2005-04-16 16:20:36 -06:00
|
|
|
signal = math_emu_stfpc(opcode, regs);
|
|
|
|
} else if (*((__u16 *) opcode) == 0xb29d) {
|
2006-10-27 04:39:22 -06:00
|
|
|
if (get_user(*((__u16 *) (opcode+2)), location+1))
|
|
|
|
return;
|
2005-04-16 16:20:36 -06:00
|
|
|
signal = math_emu_lfpc(opcode, regs);
|
|
|
|
#endif
|
|
|
|
} else
|
|
|
|
signal = SIGILL;
|
2007-02-05 13:17:29 -07:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If we get an illegal op in kernel mode, send it through the
|
|
|
|
* kprobes notifier. If kprobes doesn't pick it up, SIGILL
|
|
|
|
*/
|
|
|
|
if (notify_die(DIE_BPT, "bpt", regs, interruption_code,
|
|
|
|
3, SIGTRAP) != NOTIFY_STOP)
|
|
|
|
signal = SIGILL;
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
#ifdef CONFIG_MATHEMU
|
|
|
|
if (signal == SIGFPE)
|
|
|
|
do_fp_trap(regs, location,
|
|
|
|
current->thread.fp_regs.fpc, interruption_code);
|
|
|
|
else if (signal == SIGSEGV) {
|
|
|
|
info.si_signo = signal;
|
|
|
|
info.si_errno = 0;
|
|
|
|
info.si_code = SEGV_MAPERR;
|
2006-10-09 13:28:03 -06:00
|
|
|
info.si_addr = (void __user *) location;
|
2005-04-16 16:20:36 -06:00
|
|
|
do_trap(interruption_code, signal,
|
|
|
|
"user address fault", regs, &info);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
if (signal) {
|
|
|
|
info.si_signo = signal;
|
|
|
|
info.si_errno = 0;
|
|
|
|
info.si_code = ILL_ILLOPC;
|
2006-02-01 04:55:59 -07:00
|
|
|
info.si_addr = (void __user *) location;
|
2005-04-16 16:20:36 -06:00
|
|
|
do_trap(interruption_code, signal,
|
|
|
|
"illegal operation", regs, &info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_MATHEMU
|
|
|
|
asmlinkage void
|
|
|
|
specification_exception(struct pt_regs * regs, long interruption_code)
|
|
|
|
{
|
|
|
|
__u8 opcode[6];
|
2006-10-09 13:28:03 -06:00
|
|
|
__u16 __user *location = NULL;
|
2005-04-16 16:20:36 -06:00
|
|
|
int signal = 0;
|
|
|
|
|
2006-10-09 13:28:03 -06:00
|
|
|
location = (__u16 __user *) get_check_address(regs);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We got all needed information from the lowcore and can
|
|
|
|
* now safely switch on interrupts.
|
|
|
|
*/
|
|
|
|
if (regs->psw.mask & PSW_MASK_PSTATE)
|
|
|
|
local_irq_enable();
|
|
|
|
|
|
|
|
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)
|
|
|
|
do_fp_trap(regs, location,
|
|
|
|
current->thread.fp_regs.fpc, interruption_code);
|
|
|
|
else if (signal) {
|
|
|
|
siginfo_t info;
|
|
|
|
info.si_signo = signal;
|
|
|
|
info.si_errno = 0;
|
|
|
|
info.si_code = ILL_ILLOPN;
|
|
|
|
info.si_addr = location;
|
|
|
|
do_trap(interruption_code, signal,
|
|
|
|
"specification exception", regs, &info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
DO_ERROR_INFO(SIGILL, "specification exception", specification_exception,
|
|
|
|
ILL_ILLOPN, get_check_address(regs));
|
|
|
|
#endif
|
|
|
|
|
2007-02-05 13:16:47 -07:00
|
|
|
static void data_exception(struct pt_regs * regs, long interruption_code)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2006-07-12 08:41:55 -06:00
|
|
|
__u16 __user *location;
|
2005-04-16 16:20:36 -06:00
|
|
|
int signal = 0;
|
|
|
|
|
2006-07-12 08:41:55 -06:00
|
|
|
location = get_check_address(regs);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We got all needed information from the lowcore and can
|
|
|
|
* now safely switch on interrupts.
|
|
|
|
*/
|
|
|
|
if (regs->psw.mask & PSW_MASK_PSTATE)
|
|
|
|
local_irq_enable();
|
|
|
|
|
|
|
|
if (MACHINE_HAS_IEEE)
|
2006-09-28 08:56:43 -06:00
|
|
|
asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
#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)),
|
2006-10-09 13:28:03 -06:00
|
|
|
(__u32 __user *)(location+1));
|
2005-04-16 16:20:36 -06:00
|
|
|
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)
|
|
|
|
do_fp_trap(regs, location,
|
|
|
|
current->thread.fp_regs.fpc, interruption_code);
|
|
|
|
else if (signal) {
|
|
|
|
siginfo_t info;
|
|
|
|
info.si_signo = signal;
|
|
|
|
info.si_errno = 0;
|
|
|
|
info.si_code = ILL_ILLOPN;
|
|
|
|
info.si_addr = location;
|
|
|
|
do_trap(interruption_code, signal,
|
|
|
|
"data exception", regs, &info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-05 13:16:47 -07:00
|
|
|
static void space_switch_exception(struct pt_regs * regs, long int_code)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
siginfo_t info;
|
|
|
|
|
|
|
|
/* Set user psw back to home space mode. */
|
|
|
|
if (regs->psw.mask & PSW_MASK_PSTATE)
|
|
|
|
regs->psw.mask |= PSW_ASC_HOME;
|
|
|
|
/* Send SIGILL. */
|
|
|
|
info.si_signo = SIGILL;
|
|
|
|
info.si_errno = 0;
|
|
|
|
info.si_code = ILL_PRVOPC;
|
|
|
|
info.si_addr = get_check_address(regs);
|
|
|
|
do_trap(int_code, SIGILL, "space switch event", regs, &info);
|
|
|
|
}
|
|
|
|
|
|
|
|
asmlinkage void kernel_stack_overflow(struct pt_regs * regs)
|
|
|
|
{
|
2005-06-21 18:16:28 -06:00
|
|
|
bust_spinlocks(1);
|
|
|
|
printk("Kernel stack overflow.\n");
|
|
|
|
show_regs(regs);
|
|
|
|
bust_spinlocks(0);
|
2005-04-16 16:20:36 -06:00
|
|
|
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] = ÷_exception;
|
|
|
|
pgm_check_table[0x0A] = &overflow_exception;
|
|
|
|
pgm_check_table[0x0B] = ÷_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;
|
2006-01-06 01:19:28 -07:00
|
|
|
#ifdef CONFIG_64BIT
|
2008-02-09 10:24:37 -07:00
|
|
|
pgm_check_table[0x38] = &do_asce_exception;
|
2005-04-16 16:20:36 -06:00
|
|
|
pgm_check_table[0x39] = &do_dat_exception;
|
|
|
|
pgm_check_table[0x3A] = &do_dat_exception;
|
|
|
|
pgm_check_table[0x3B] = &do_dat_exception;
|
2006-01-06 01:19:28 -07:00
|
|
|
#endif /* CONFIG_64BIT */
|
2005-04-16 16:20:36 -06:00
|
|
|
pgm_check_table[0x15] = &operand_exception;
|
|
|
|
pgm_check_table[0x1C] = &space_switch_exception;
|
|
|
|
pgm_check_table[0x1D] = &hfp_sqrt_exception;
|
2006-12-04 07:40:40 -07:00
|
|
|
pfault_irq_init();
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|