Commit | Line | Data |
---|---|---|
5bdc9b44 HC |
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> | |
8de2ce86 | 13 | #include <linux/module.h> |
5bdc9b44 | 14 | |
4d284cac | 15 | static unsigned long save_context_stack(struct stack_trace *trace, |
4d284cac HC |
16 | unsigned long sp, |
17 | unsigned long low, | |
a3afe70b HC |
18 | unsigned long high, |
19 | int savesched) | |
5bdc9b44 HC |
20 | { |
21 | struct stack_frame *sf; | |
22 | struct pt_regs *regs; | |
23 | unsigned long addr; | |
24 | ||
25 | while(1) { | |
26 | sp &= PSW_ADDR_INSN; | |
27 | if (sp < low || sp > high) | |
28 | return sp; | |
29 | sf = (struct stack_frame *)sp; | |
30 | while(1) { | |
31 | addr = sf->gprs[8] & PSW_ADDR_INSN; | |
e90a2857 | 32 | if (!trace->skip) |
5bdc9b44 HC |
33 | trace->entries[trace->nr_entries++] = addr; |
34 | else | |
e90a2857 | 35 | trace->skip--; |
5bdc9b44 HC |
36 | if (trace->nr_entries >= trace->max_entries) |
37 | return sp; | |
38 | low = sp; | |
39 | sp = sf->back_chain & PSW_ADDR_INSN; | |
40 | if (!sp) | |
41 | break; | |
42 | if (sp <= low || sp > high - sizeof(*sf)) | |
43 | return sp; | |
44 | sf = (struct stack_frame *)sp; | |
45 | } | |
46 | /* Zero backchain detected, check for interrupt frame. */ | |
47 | sp = (unsigned long)(sf + 1); | |
48 | if (sp <= low || sp > high - sizeof(*regs)) | |
49 | return sp; | |
50 | regs = (struct pt_regs *)sp; | |
51 | addr = regs->psw.addr & PSW_ADDR_INSN; | |
a3afe70b HC |
52 | if (savesched || !in_sched_functions(addr)) { |
53 | if (!trace->skip) | |
54 | trace->entries[trace->nr_entries++] = addr; | |
55 | else | |
56 | trace->skip--; | |
57 | } | |
5bdc9b44 HC |
58 | if (trace->nr_entries >= trace->max_entries) |
59 | return sp; | |
60 | low = sp; | |
61 | sp = regs->gprs[15]; | |
62 | } | |
63 | } | |
64 | ||
ab1b6f03 | 65 | void save_stack_trace(struct stack_trace *trace) |
5bdc9b44 HC |
66 | { |
67 | register unsigned long sp asm ("15"); | |
75e9de18 | 68 | unsigned long orig_sp, new_sp; |
5bdc9b44 | 69 | |
75e9de18 | 70 | orig_sp = sp & PSW_ADDR_INSN; |
e90a2857 HC |
71 | new_sp = save_context_stack(trace, orig_sp, |
72 | S390_lowcore.panic_stack - PAGE_SIZE, | |
a3afe70b | 73 | S390_lowcore.panic_stack, 1); |
ab1b6f03 | 74 | if (new_sp != orig_sp) |
5bdc9b44 | 75 | return; |
e90a2857 HC |
76 | new_sp = save_context_stack(trace, new_sp, |
77 | S390_lowcore.async_stack - ASYNC_SIZE, | |
a3afe70b | 78 | S390_lowcore.async_stack, 1); |
ab1b6f03 | 79 | if (new_sp != orig_sp) |
5bdc9b44 | 80 | return; |
e90a2857 | 81 | save_context_stack(trace, new_sp, |
ab1b6f03 | 82 | S390_lowcore.thread_info, |
a3afe70b HC |
83 | S390_lowcore.thread_info + THREAD_SIZE, 1); |
84 | } | |
7b4c9505 | 85 | EXPORT_SYMBOL_GPL(save_stack_trace); |
a3afe70b HC |
86 | |
87 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |
88 | { | |
89 | unsigned long sp, low, high; | |
90 | ||
91 | sp = tsk->thread.ksp & PSW_ADDR_INSN; | |
92 | low = (unsigned long) task_stack_page(tsk); | |
93 | high = (unsigned long) task_pt_regs(tsk); | |
94 | save_context_stack(trace, sp, low, high, 0); | |
95 | if (trace->nr_entries < trace->max_entries) | |
96 | trace->entries[trace->nr_entries++] = ULONG_MAX; | |
5bdc9b44 | 97 | } |
7b4c9505 | 98 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); |