]>
Commit | Line | Data |
---|---|---|
2bc5f927 AH |
1 | /* |
2 | * Copyright (C) 1991, 1992 Linus Torvalds | |
3 | * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs | |
4 | */ | |
5 | #include <linux/kallsyms.h> | |
6 | #include <linux/kprobes.h> | |
7 | #include <linux/uaccess.h> | |
2bc5f927 AH |
8 | #include <linux/hardirq.h> |
9 | #include <linux/kdebug.h> | |
186f4360 | 10 | #include <linux/export.h> |
2bc5f927 AH |
11 | #include <linux/ptrace.h> |
12 | #include <linux/kexec.h> | |
b8030906 | 13 | #include <linux/sysfs.h> |
2bc5f927 AH |
14 | #include <linux/bug.h> |
15 | #include <linux/nmi.h> | |
16 | ||
17 | #include <asm/stacktrace.h> | |
18 | ||
cb76c939 | 19 | void stack_type_str(enum stack_type type, const char **begin, const char **end) |
198d208d | 20 | { |
cb76c939 JP |
21 | switch (type) { |
22 | case STACK_TYPE_IRQ: | |
23 | case STACK_TYPE_SOFTIRQ: | |
24 | *begin = "IRQ"; | |
25 | *end = "EOI"; | |
26 | break; | |
27 | default: | |
28 | *begin = NULL; | |
29 | *end = NULL; | |
30 | } | |
198d208d SR |
31 | } |
32 | ||
cb76c939 JP |
33 | static bool in_hardirq_stack(unsigned long *stack, struct stack_info *info) |
34 | { | |
35 | unsigned long *begin = (unsigned long *)this_cpu_read(hardirq_stack); | |
36 | unsigned long *end = begin + (THREAD_SIZE / sizeof(long)); | |
37 | ||
5fe599e0 JP |
38 | /* |
39 | * This is a software stack, so 'end' can be a valid stack pointer. | |
40 | * It just means the stack is empty. | |
41 | */ | |
42 | if (stack < begin || stack > end) | |
cb76c939 JP |
43 | return false; |
44 | ||
45 | info->type = STACK_TYPE_IRQ; | |
46 | info->begin = begin; | |
47 | info->end = end; | |
48 | ||
49 | /* | |
50 | * See irq_32.c -- the next stack pointer is stored at the beginning of | |
51 | * the stack. | |
52 | */ | |
53 | info->next_sp = (unsigned long *)*begin; | |
198d208d | 54 | |
cb76c939 JP |
55 | return true; |
56 | } | |
57 | ||
58 | static bool in_softirq_stack(unsigned long *stack, struct stack_info *info) | |
198d208d | 59 | { |
cb76c939 JP |
60 | unsigned long *begin = (unsigned long *)this_cpu_read(softirq_stack); |
61 | unsigned long *end = begin + (THREAD_SIZE / sizeof(long)); | |
62 | ||
5fe599e0 JP |
63 | /* |
64 | * This is a software stack, so 'end' can be a valid stack pointer. | |
65 | * It just means the stack is empty. | |
66 | */ | |
67 | if (stack < begin || stack > end) | |
cb76c939 JP |
68 | return false; |
69 | ||
70 | info->type = STACK_TYPE_SOFTIRQ; | |
71 | info->begin = begin; | |
72 | info->end = end; | |
73 | ||
74 | /* | |
75 | * The next stack pointer is stored at the beginning of the stack. | |
76 | * See irq_32.c. | |
77 | */ | |
78 | info->next_sp = (unsigned long *)*begin; | |
198d208d | 79 | |
cb76c939 | 80 | return true; |
198d208d SR |
81 | } |
82 | ||
cb76c939 JP |
83 | int get_stack_info(unsigned long *stack, struct task_struct *task, |
84 | struct stack_info *info, unsigned long *visit_mask) | |
198d208d | 85 | { |
cb76c939 JP |
86 | if (!stack) |
87 | goto unknown; | |
198d208d | 88 | |
cb76c939 JP |
89 | task = task ? : current; |
90 | ||
91 | if (in_task_stack(stack, task, info)) | |
fcd709ef | 92 | goto recursion_check; |
cb76c939 JP |
93 | |
94 | if (task != current) | |
95 | goto unknown; | |
96 | ||
97 | if (in_hardirq_stack(stack, info)) | |
fcd709ef | 98 | goto recursion_check; |
cb76c939 JP |
99 | |
100 | if (in_softirq_stack(stack, info)) | |
fcd709ef JP |
101 | goto recursion_check; |
102 | ||
103 | goto unknown; | |
104 | ||
105 | recursion_check: | |
106 | /* | |
107 | * Make sure we don't iterate through any given stack more than once. | |
108 | * If it comes up a second time then there's something wrong going on: | |
109 | * just break out and report an unknown stack type. | |
110 | */ | |
111 | if (visit_mask) { | |
112 | if (*visit_mask & (1UL << info->type)) | |
113 | goto unknown; | |
114 | *visit_mask |= 1UL << info->type; | |
115 | } | |
116 | ||
117 | return 0; | |
cb76c939 JP |
118 | |
119 | unknown: | |
120 | info->type = STACK_TYPE_UNKNOWN; | |
121 | return -EINVAL; | |
198d208d | 122 | } |
0406ca6d | 123 | |
e8e999cf NK |
124 | void dump_trace(struct task_struct *task, struct pt_regs *regs, |
125 | unsigned long *stack, unsigned long bp, | |
2bc5f927 AH |
126 | const struct stacktrace_ops *ops, void *data) |
127 | { | |
cb76c939 | 128 | unsigned long visit_mask = 0; |
7ee991fb SR |
129 | int graph = 0; |
130 | ||
4b8afafb JP |
131 | task = task ? : current; |
132 | stack = stack ? : get_stack_pointer(task, regs); | |
133 | bp = bp ? : (unsigned long)get_frame_pointer(task, regs); | |
e8e999cf | 134 | |
2bc5f927 | 135 | for (;;) { |
cb76c939 JP |
136 | const char *begin_str, *end_str; |
137 | struct stack_info info; | |
198d208d | 138 | |
cb76c939 JP |
139 | if (get_stack_info(stack, task, &info, &visit_mask)) |
140 | break; | |
2bc5f927 | 141 | |
cb76c939 | 142 | stack_type_str(info.type, &begin_str, &end_str); |
2ac53721 | 143 | |
cb76c939 | 144 | if (begin_str && ops->stack(data, begin_str) < 0) |
0788aa6a SR |
145 | break; |
146 | ||
cb76c939 | 147 | bp = ops->walk_stack(task, stack, bp, ops, data, &info, &graph); |
0788aa6a | 148 | |
cb76c939 | 149 | if (end_str && ops->stack(data, end_str) < 0) |
2ac53721 | 150 | break; |
cb76c939 JP |
151 | |
152 | stack = info.next_sp; | |
153 | ||
2bc5f927 AH |
154 | touch_nmi_watchdog(); |
155 | } | |
156 | } | |
157 | EXPORT_SYMBOL(dump_trace); | |
158 | ||
878719e8 | 159 | void |
2bc5f927 | 160 | show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, |
e8e999cf | 161 | unsigned long *sp, unsigned long bp, char *log_lvl) |
2bc5f927 AH |
162 | { |
163 | unsigned long *stack; | |
164 | int i; | |
165 | ||
1959a601 AL |
166 | if (!try_get_task_stack(task)) |
167 | return; | |
168 | ||
4b8afafb | 169 | sp = sp ? : get_stack_pointer(task, regs); |
2bc5f927 AH |
170 | |
171 | stack = sp; | |
172 | for (i = 0; i < kstack_depth_to_print; i++) { | |
173 | if (kstack_end(stack)) | |
174 | break; | |
1fc7f61c AS |
175 | if ((i % STACKSLOTS_PER_LINE) == 0) { |
176 | if (i != 0) | |
177 | pr_cont("\n"); | |
178 | printk("%s %08lx", log_lvl, *stack++); | |
179 | } else | |
180 | pr_cont(" %08lx", *stack++); | |
ca0a8164 | 181 | touch_nmi_watchdog(); |
2bc5f927 | 182 | } |
c767a54b | 183 | pr_cont("\n"); |
e8e999cf | 184 | show_trace_log_lvl(task, regs, sp, bp, log_lvl); |
1959a601 AL |
185 | |
186 | put_task_stack(task); | |
2bc5f927 AH |
187 | } |
188 | ||
2bc5f927 | 189 | |
57da8b96 | 190 | void show_regs(struct pt_regs *regs) |
2bc5f927 AH |
191 | { |
192 | int i; | |
193 | ||
a43cb95d | 194 | show_regs_print_info(KERN_EMERG); |
f39b6f0e | 195 | __show_regs(regs, !user_mode(regs)); |
2bc5f927 | 196 | |
2bc5f927 AH |
197 | /* |
198 | * When in-kernel, we also print out the stack and code at the | |
199 | * time of the fault.. | |
200 | */ | |
f39b6f0e | 201 | if (!user_mode(regs)) { |
2bc5f927 AH |
202 | unsigned int code_prologue = code_bytes * 43 / 64; |
203 | unsigned int code_len = code_bytes; | |
204 | unsigned char c; | |
205 | u8 *ip; | |
206 | ||
c767a54b | 207 | pr_emerg("Stack:\n"); |
5a8ff54c | 208 | show_stack_log_lvl(NULL, regs, NULL, 0, KERN_EMERG); |
2bc5f927 | 209 | |
c767a54b | 210 | pr_emerg("Code:"); |
2bc5f927 AH |
211 | |
212 | ip = (u8 *)regs->ip - code_prologue; | |
213 | if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { | |
8a541665 | 214 | /* try starting at IP */ |
2bc5f927 AH |
215 | ip = (u8 *)regs->ip; |
216 | code_len = code_len - code_prologue + 1; | |
217 | } | |
218 | for (i = 0; i < code_len; i++, ip++) { | |
219 | if (ip < (u8 *)PAGE_OFFSET || | |
220 | probe_kernel_address(ip, c)) { | |
c767a54b | 221 | pr_cont(" Bad EIP value."); |
2bc5f927 AH |
222 | break; |
223 | } | |
224 | if (ip == (u8 *)regs->ip) | |
c767a54b | 225 | pr_cont(" <%02x>", c); |
2bc5f927 | 226 | else |
c767a54b | 227 | pr_cont(" %02x", c); |
2bc5f927 AH |
228 | } |
229 | } | |
c767a54b | 230 | pr_cont("\n"); |
2bc5f927 AH |
231 | } |
232 | ||
233 | int is_valid_bugaddr(unsigned long ip) | |
234 | { | |
235 | unsigned short ud2; | |
236 | ||
237 | if (ip < PAGE_OFFSET) | |
238 | return 0; | |
239 | if (probe_kernel_address((unsigned short *)ip, ud2)) | |
240 | return 0; | |
241 | ||
242 | return ud2 == 0x0b0f; | |
243 | } |