Commit | Line | Data |
---|---|---|
1bca09f7 HC |
1 | /* |
2 | * Stack dumping functions | |
3 | * | |
4 | * Copyright IBM Corp. 1999, 2013 | |
5 | */ | |
6 | ||
7 | #include <linux/kallsyms.h> | |
8 | #include <linux/hardirq.h> | |
9 | #include <linux/kprobes.h> | |
10 | #include <linux/utsname.h> | |
11 | #include <linux/export.h> | |
12 | #include <linux/kdebug.h> | |
13 | #include <linux/ptrace.h> | |
10917b83 | 14 | #include <linux/mm.h> |
1bca09f7 HC |
15 | #include <linux/module.h> |
16 | #include <linux/sched.h> | |
b17b0153 | 17 | #include <linux/sched/debug.h> |
1bca09f7 HC |
18 | #include <asm/processor.h> |
19 | #include <asm/debug.h> | |
0f20822a | 20 | #include <asm/dis.h> |
1bca09f7 HC |
21 | #include <asm/ipl.h> |
22 | ||
1bca09f7 | 23 | /* |
758d39eb | 24 | * For dump_trace we have tree different stack to consider: |
1bca09f7 HC |
25 | * - the panic stack which is used if the kernel stack has overflown |
26 | * - the asynchronous interrupt stack (cpu related) | |
27 | * - the synchronous kernel stack (process related) | |
758d39eb | 28 | * The stack trace can start at any of the three stacks and can potentially |
1bca09f7 HC |
29 | * touch all of them. The order is: panic stack, async stack, sync stack. |
30 | */ | |
31 | static unsigned long | |
758d39eb HC |
32 | __dump_trace(dump_trace_func_t func, void *data, unsigned long sp, |
33 | unsigned long low, unsigned long high) | |
1bca09f7 HC |
34 | { |
35 | struct stack_frame *sf; | |
36 | struct pt_regs *regs; | |
37 | ||
38 | while (1) { | |
1bca09f7 HC |
39 | if (sp < low || sp > high - sizeof(*sf)) |
40 | return sp; | |
41 | sf = (struct stack_frame *) sp; | |
d0208639 HC |
42 | if (func(data, sf->gprs[8], 0)) |
43 | return sp; | |
1bca09f7 HC |
44 | /* Follow the backchain. */ |
45 | while (1) { | |
46 | low = sp; | |
9cb1ccec | 47 | sp = sf->back_chain; |
1bca09f7 HC |
48 | if (!sp) |
49 | break; | |
50 | if (sp <= low || sp > high - sizeof(*sf)) | |
51 | return sp; | |
52 | sf = (struct stack_frame *) sp; | |
d0208639 HC |
53 | if (func(data, sf->gprs[8], 1)) |
54 | return sp; | |
1bca09f7 HC |
55 | } |
56 | /* Zero backchain detected, check for interrupt frame. */ | |
57 | sp = (unsigned long) (sf + 1); | |
58 | if (sp <= low || sp > high - sizeof(*regs)) | |
59 | return sp; | |
60 | regs = (struct pt_regs *) sp; | |
758d39eb | 61 | if (!user_mode(regs)) { |
d0208639 | 62 | if (func(data, regs->psw.addr, 1)) |
758d39eb HC |
63 | return sp; |
64 | } | |
1bca09f7 HC |
65 | low = sp; |
66 | sp = regs->gprs[15]; | |
67 | } | |
68 | } | |
69 | ||
758d39eb HC |
70 | void dump_trace(dump_trace_func_t func, void *data, struct task_struct *task, |
71 | unsigned long sp) | |
1bca09f7 | 72 | { |
758d39eb | 73 | unsigned long frame_size; |
1bca09f7 | 74 | |
758d39eb | 75 | frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); |
1bca09f7 | 76 | #ifdef CONFIG_CHECK_STACK |
758d39eb | 77 | sp = __dump_trace(func, data, sp, |
9cc5c206 MS |
78 | S390_lowcore.panic_stack + frame_size - 4096, |
79 | S390_lowcore.panic_stack + frame_size); | |
1bca09f7 | 80 | #endif |
758d39eb | 81 | sp = __dump_trace(func, data, sp, |
9cc5c206 MS |
82 | S390_lowcore.async_stack + frame_size - ASYNC_SIZE, |
83 | S390_lowcore.async_stack + frame_size); | |
c3bdf2e1 HC |
84 | task = task ?: current; |
85 | __dump_trace(func, data, sp, | |
86 | (unsigned long)task_stack_page(task), | |
87 | (unsigned long)task_stack_page(task) + THREAD_SIZE); | |
758d39eb HC |
88 | } |
89 | EXPORT_SYMBOL_GPL(dump_trace); | |
90 | ||
d0208639 | 91 | static int show_address(void *data, unsigned long address, int reliable) |
758d39eb | 92 | { |
d0208639 HC |
93 | if (reliable) |
94 | printk(" [<%016lx>] %pSR \n", address, (void *)address); | |
95 | else | |
96 | printk("([<%016lx>] %pSR)\n", address, (void *)address); | |
758d39eb HC |
97 | return 0; |
98 | } | |
99 | ||
100 | static void show_trace(struct task_struct *task, unsigned long sp) | |
101 | { | |
102 | if (!sp) | |
103 | sp = task ? task->thread.ksp : current_stack_pointer(); | |
104 | printk("Call Trace:\n"); | |
105 | dump_trace(show_address, NULL, task, sp); | |
1bca09f7 HC |
106 | if (!task) |
107 | task = current; | |
108 | debug_show_held_locks(task); | |
109 | } | |
110 | ||
111 | void show_stack(struct task_struct *task, unsigned long *sp) | |
112 | { | |
1bca09f7 HC |
113 | unsigned long *stack; |
114 | int i; | |
115 | ||
76737ce1 HC |
116 | stack = sp; |
117 | if (!stack) { | |
118 | if (!task) | |
119 | stack = (unsigned long *)current_stack_pointer(); | |
120 | else | |
121 | stack = (unsigned long *)task->thread.ksp; | |
122 | } | |
47ece7fe | 123 | printk(KERN_DEFAULT "Stack:\n"); |
5a79859a | 124 | for (i = 0; i < 20; i++) { |
1bca09f7 HC |
125 | if (((addr_t) stack & (THREAD_SIZE-1)) == 0) |
126 | break; | |
47ece7fe HC |
127 | if (i % 4 == 0) |
128 | printk(KERN_DEFAULT " "); | |
129 | pr_cont("%016lx%c", *stack++, i % 4 == 3 ? '\n' : ' '); | |
1bca09f7 | 130 | } |
758d39eb | 131 | show_trace(task, (unsigned long)sp); |
1bca09f7 HC |
132 | } |
133 | ||
134 | static void show_last_breaking_event(struct pt_regs *regs) | |
135 | { | |
1bca09f7 | 136 | printk("Last Breaking-Event-Address:\n"); |
8237ac3c | 137 | printk(" [<%016lx>] %pSR\n", regs->args[0], (void *)regs->args[0]); |
1bca09f7 HC |
138 | } |
139 | ||
1bca09f7 HC |
140 | void show_registers(struct pt_regs *regs) |
141 | { | |
1f021ea0 | 142 | struct psw_bits *psw = &psw_bits(regs->psw); |
1bca09f7 HC |
143 | char *mode; |
144 | ||
145 | mode = user_mode(regs) ? "User" : "Krnl"; | |
9ea80662 HC |
146 | printk("%s PSW : %p %p", mode, (void *)regs->psw.mask, (void *)regs->psw.addr); |
147 | if (!user_mode(regs)) | |
a7906345 HC |
148 | pr_cont(" (%pSR)", (void *)regs->psw.addr); |
149 | pr_cont("\n"); | |
1bca09f7 | 150 | printk(" R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x " |
1f021ea0 HC |
151 | "P:%x AS:%x CC:%x PM:%x", psw->r, psw->t, psw->i, psw->e, |
152 | psw->key, psw->m, psw->w, psw->p, psw->as, psw->cc, psw->pm); | |
a7906345 HC |
153 | pr_cont(" RI:%x EA:%x\n", psw->ri, psw->eaba); |
154 | printk("%s GPRS: %016lx %016lx %016lx %016lx\n", mode, | |
1bca09f7 | 155 | regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]); |
5a79859a | 156 | printk(" %016lx %016lx %016lx %016lx\n", |
1bca09f7 | 157 | regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]); |
5a79859a | 158 | printk(" %016lx %016lx %016lx %016lx\n", |
1bca09f7 | 159 | regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]); |
5a79859a | 160 | printk(" %016lx %016lx %016lx %016lx\n", |
1bca09f7 HC |
161 | regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]); |
162 | show_code(regs); | |
163 | } | |
164 | ||
165 | void show_regs(struct pt_regs *regs) | |
166 | { | |
a43cb95d | 167 | show_regs_print_info(KERN_DEFAULT); |
1bca09f7 HC |
168 | show_registers(regs); |
169 | /* Show stack backtrace if pt_regs is from kernel mode */ | |
170 | if (!user_mode(regs)) | |
758d39eb | 171 | show_trace(NULL, regs->gprs[15]); |
1bca09f7 HC |
172 | show_last_breaking_event(regs); |
173 | } | |
174 | ||
175 | static DEFINE_SPINLOCK(die_lock); | |
176 | ||
177 | void die(struct pt_regs *regs, const char *str) | |
178 | { | |
179 | static int die_counter; | |
180 | ||
181 | oops_enter(); | |
182 | lgr_info_log(); | |
183 | debug_stop_all(); | |
184 | console_verbose(); | |
185 | spin_lock_irq(&die_lock); | |
186 | bust_spinlocks(1); | |
413d4047 HC |
187 | printk("%s: %04x ilc:%d [#%d] ", str, regs->int_code & 0xffff, |
188 | regs->int_code >> 17, ++die_counter); | |
1bca09f7 | 189 | #ifdef CONFIG_PREEMPT |
47ece7fe | 190 | pr_cont("PREEMPT "); |
1bca09f7 HC |
191 | #endif |
192 | #ifdef CONFIG_SMP | |
47ece7fe | 193 | pr_cont("SMP "); |
1bca09f7 | 194 | #endif |
10917b83 | 195 | if (debug_pagealloc_enabled()) |
47ece7fe HC |
196 | pr_cont("DEBUG_PAGEALLOC"); |
197 | pr_cont("\n"); | |
1bca09f7 HC |
198 | notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV); |
199 | print_modules(); | |
200 | show_regs(regs); | |
201 | bust_spinlocks(0); | |
202 | add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); | |
203 | spin_unlock_irq(&die_lock); | |
204 | if (in_interrupt()) | |
205 | panic("Fatal exception in interrupt"); | |
206 | if (panic_on_oops) | |
207 | panic("Fatal exception: panic_on_oops"); | |
208 | oops_exit(); | |
209 | do_exit(SIGSEGV); | |
210 | } |