diff options
-rw-r--r-- | arch/s390/Kconfig | 4 | ||||
-rw-r--r-- | arch/s390/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/s390/kernel/stacktrace.c | 90 |
3 files changed, 95 insertions, 0 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 821a141889de..5b28da4ad809 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
@@ -7,6 +7,10 @@ config MMU | |||
7 | bool | 7 | bool |
8 | default y | 8 | default y |
9 | 9 | ||
10 | config STACKTRACE_SUPPORT | ||
11 | bool | ||
12 | default y | ||
13 | |||
10 | config RWSEM_GENERIC_SPINLOCK | 14 | config RWSEM_GENERIC_SPINLOCK |
11 | bool | 15 | bool |
12 | 16 | ||
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 9269b5788fac..eabf00a6f770 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
@@ -21,6 +21,7 @@ obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \ | |||
21 | obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o | 21 | obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o |
22 | 22 | ||
23 | obj-$(CONFIG_VIRT_TIMER) += vtime.o | 23 | obj-$(CONFIG_VIRT_TIMER) += vtime.o |
24 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | ||
24 | 25 | ||
25 | # Kexec part | 26 | # Kexec part |
26 | S390_KEXEC_OBJS := machine_kexec.o crash.o | 27 | S390_KEXEC_OBJS := machine_kexec.o crash.o |
diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c new file mode 100644 index 000000000000..de83f38288d0 --- /dev/null +++ b/arch/s390/kernel/stacktrace.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * arch/s390/kernel/stacktrace.c | ||
3 | * | ||
4 | * Stack trace management functions | ||
5 | * | ||
6 | * Copyright (C) IBM Corp. 2006 | ||
7 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/sched.h> | ||
11 | #include <linux/stacktrace.h> | ||
12 | #include <linux/kallsyms.h> | ||
13 | |||
14 | static inline unsigned long save_context_stack(struct stack_trace *trace, | ||
15 | unsigned int *skip, | ||
16 | unsigned long sp, | ||
17 | unsigned long low, | ||
18 | unsigned long high) | ||
19 | { | ||
20 | struct stack_frame *sf; | ||
21 | struct pt_regs *regs; | ||
22 | unsigned long addr; | ||
23 | |||
24 | while(1) { | ||
25 | sp &= PSW_ADDR_INSN; | ||
26 | if (sp < low || sp > high) | ||
27 | return sp; | ||
28 | sf = (struct stack_frame *)sp; | ||
29 | while(1) { | ||
30 | addr = sf->gprs[8] & PSW_ADDR_INSN; | ||
31 | if (!(*skip)) | ||
32 | trace->entries[trace->nr_entries++] = addr; | ||
33 | else | ||
34 | (*skip)--; | ||
35 | if (trace->nr_entries >= trace->max_entries) | ||
36 | return sp; | ||
37 | low = sp; | ||
38 | sp = sf->back_chain & PSW_ADDR_INSN; | ||
39 | if (!sp) | ||
40 | break; | ||
41 | if (sp <= low || sp > high - sizeof(*sf)) | ||
42 | return sp; | ||
43 | sf = (struct stack_frame *)sp; | ||
44 | } | ||
45 | /* Zero backchain detected, check for interrupt frame. */ | ||
46 | sp = (unsigned long)(sf + 1); | ||
47 | if (sp <= low || sp > high - sizeof(*regs)) | ||
48 | return sp; | ||
49 | regs = (struct pt_regs *)sp; | ||
50 | addr = regs->psw.addr & PSW_ADDR_INSN; | ||
51 | if (!(*skip)) | ||
52 | trace->entries[trace->nr_entries++] = addr; | ||
53 | else | ||
54 | (*skip)--; | ||
55 | if (trace->nr_entries >= trace->max_entries) | ||
56 | return sp; | ||
57 | low = sp; | ||
58 | sp = regs->gprs[15]; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | void save_stack_trace(struct stack_trace *trace, | ||
63 | struct task_struct *task, int all_contexts, | ||
64 | unsigned int skip) | ||
65 | { | ||
66 | register unsigned long sp asm ("15"); | ||
67 | unsigned long orig_sp; | ||
68 | |||
69 | sp &= PSW_ADDR_INSN; | ||
70 | orig_sp = sp; | ||
71 | |||
72 | sp = save_context_stack(trace, &skip, sp, | ||
73 | S390_lowcore.panic_stack - PAGE_SIZE, | ||
74 | S390_lowcore.panic_stack); | ||
75 | if ((sp != orig_sp) && !all_contexts) | ||
76 | return; | ||
77 | sp = save_context_stack(trace, &skip, sp, | ||
78 | S390_lowcore.async_stack - ASYNC_SIZE, | ||
79 | S390_lowcore.async_stack); | ||
80 | if ((sp != orig_sp) && !all_contexts) | ||
81 | return; | ||
82 | if (task) | ||
83 | save_context_stack(trace, &skip, sp, | ||
84 | (unsigned long) task_stack_page(task), | ||
85 | (unsigned long) task_stack_page(task) + THREAD_SIZE); | ||
86 | else | ||
87 | save_context_stack(trace, &skip, sp, S390_lowcore.thread_info, | ||
88 | S390_lowcore.thread_info + THREAD_SIZE); | ||
89 | return; | ||
90 | } | ||