xref: /openbmc/linux/arch/parisc/kernel/unwind.c (revision 384740dc)
1 /*
2  * Kernel unwinding support
3  *
4  * (c) 2002-2004 Randolph Chung <tausq@debian.org>
5  *
6  * Derived partially from the IA64 implementation. The PA-RISC
7  * Runtime Architecture Document is also a useful reference to
8  * understand what is happening here
9  */
10 
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/sched.h>
14 #include <linux/slab.h>
15 #include <linux/kallsyms.h>
16 
17 #include <asm/uaccess.h>
18 #include <asm/assembly.h>
19 #include <asm/asm-offsets.h>
20 #include <asm/ptrace.h>
21 
22 #include <asm/unwind.h>
23 
24 /* #define DEBUG 1 */
25 #ifdef DEBUG
26 #define dbg(x...) printk(x)
27 #else
28 #define dbg(x...)
29 #endif
30 
31 #define KERNEL_START (KERNEL_BINARY_TEXT_START - 0x1000)
32 
33 extern struct unwind_table_entry __start___unwind[];
34 extern struct unwind_table_entry __stop___unwind[];
35 
36 static spinlock_t unwind_lock;
37 /*
38  * the kernel unwind block is not dynamically allocated so that
39  * we can call unwind_init as early in the bootup process as
40  * possible (before the slab allocator is initialized)
41  */
42 static struct unwind_table kernel_unwind_table __read_mostly;
43 static LIST_HEAD(unwind_tables);
44 
45 static inline const struct unwind_table_entry *
46 find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
47 {
48 	const struct unwind_table_entry *e = NULL;
49 	unsigned long lo, hi, mid;
50 
51 	lo = 0;
52 	hi = table->length - 1;
53 
54 	while (lo <= hi) {
55 		mid = (hi - lo) / 2 + lo;
56 		e = &table->table[mid];
57 		if (addr < e->region_start)
58 			hi = mid - 1;
59 		else if (addr > e->region_end)
60 			lo = mid + 1;
61 		else
62 			return e;
63 	}
64 
65 	return NULL;
66 }
67 
68 static const struct unwind_table_entry *
69 find_unwind_entry(unsigned long addr)
70 {
71 	struct unwind_table *table;
72 	const struct unwind_table_entry *e = NULL;
73 
74 	if (addr >= kernel_unwind_table.start &&
75 	    addr <= kernel_unwind_table.end)
76 		e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
77 	else
78 		list_for_each_entry(table, &unwind_tables, list) {
79 			if (addr >= table->start &&
80 			    addr <= table->end)
81 				e = find_unwind_entry_in_table(table, addr);
82 			if (e)
83 				break;
84 		}
85 
86 	return e;
87 }
88 
89 static void
90 unwind_table_init(struct unwind_table *table, const char *name,
91 		  unsigned long base_addr, unsigned long gp,
92 		  void *table_start, void *table_end)
93 {
94 	struct unwind_table_entry *start = table_start;
95 	struct unwind_table_entry *end =
96 		(struct unwind_table_entry *)table_end - 1;
97 
98 	table->name = name;
99 	table->base_addr = base_addr;
100 	table->gp = gp;
101 	table->start = base_addr + start->region_start;
102 	table->end = base_addr + end->region_end;
103 	table->table = (struct unwind_table_entry *)table_start;
104 	table->length = end - start + 1;
105 	INIT_LIST_HEAD(&table->list);
106 
107 	for (; start <= end; start++) {
108 		if (start < end &&
109 		    start->region_end > (start+1)->region_start) {
110 			printk("WARNING: Out of order unwind entry! %p and %p\n", start, start+1);
111 		}
112 
113 		start->region_start += base_addr;
114 		start->region_end += base_addr;
115 	}
116 }
117 
118 static void
119 unwind_table_sort(struct unwind_table_entry *start,
120 		  struct unwind_table_entry *finish)
121 {
122 	struct unwind_table_entry el, *p, *q;
123 
124 	for (p = start + 1; p < finish; ++p) {
125 		if (p[0].region_start < p[-1].region_start) {
126 			el = *p;
127 			q = p;
128 			do {
129 				q[0] = q[-1];
130 				--q;
131 			} while (q > start &&
132 				 el.region_start < q[-1].region_start);
133 			*q = el;
134 		}
135 	}
136 }
137 
138 struct unwind_table *
139 unwind_table_add(const char *name, unsigned long base_addr,
140 		 unsigned long gp,
141                  void *start, void *end)
142 {
143 	struct unwind_table *table;
144 	unsigned long flags;
145 	struct unwind_table_entry *s = (struct unwind_table_entry *)start;
146 	struct unwind_table_entry *e = (struct unwind_table_entry *)end;
147 
148 	unwind_table_sort(s, e);
149 
150 	table = kmalloc(sizeof(struct unwind_table), GFP_USER);
151 	if (table == NULL)
152 		return NULL;
153 	unwind_table_init(table, name, base_addr, gp, start, end);
154 	spin_lock_irqsave(&unwind_lock, flags);
155 	list_add_tail(&table->list, &unwind_tables);
156 	spin_unlock_irqrestore(&unwind_lock, flags);
157 
158 	return table;
159 }
160 
161 void unwind_table_remove(struct unwind_table *table)
162 {
163 	unsigned long flags;
164 
165 	spin_lock_irqsave(&unwind_lock, flags);
166 	list_del(&table->list);
167 	spin_unlock_irqrestore(&unwind_lock, flags);
168 
169 	kfree(table);
170 }
171 
172 /* Called from setup_arch to import the kernel unwind info */
173 static int unwind_init(void)
174 {
175 	long start, stop;
176 	register unsigned long gp __asm__ ("r27");
177 
178 	start = (long)&__start___unwind[0];
179 	stop = (long)&__stop___unwind[0];
180 
181 	spin_lock_init(&unwind_lock);
182 
183 	printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n",
184 	    start, stop,
185 	    (stop - start) / sizeof(struct unwind_table_entry));
186 
187 	unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START,
188 			  gp,
189 			  &__start___unwind[0], &__stop___unwind[0]);
190 #if 0
191 	{
192 		int i;
193 		for (i = 0; i < 10; i++)
194 		{
195 			printk("region 0x%x-0x%x\n",
196 				__start___unwind[i].region_start,
197 				__start___unwind[i].region_end);
198 		}
199 	}
200 #endif
201 	return 0;
202 }
203 
204 #ifdef CONFIG_64BIT
205 #define get_func_addr(fptr) fptr[2]
206 #else
207 #define get_func_addr(fptr) fptr[0]
208 #endif
209 
210 static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size)
211 {
212 	extern void handle_interruption(int, struct pt_regs *);
213 	static unsigned long *hi = (unsigned long *)&handle_interruption;
214 
215 	if (pc == get_func_addr(hi)) {
216 		struct pt_regs *regs = (struct pt_regs *)(info->sp - frame_size - PT_SZ_ALGN);
217 		dbg("Unwinding through handle_interruption()\n");
218 		info->prev_sp = regs->gr[30];
219 		info->prev_ip = regs->iaoq[0];
220 
221 		return 1;
222 	}
223 
224 	return 0;
225 }
226 
227 static void unwind_frame_regs(struct unwind_frame_info *info)
228 {
229 	const struct unwind_table_entry *e;
230 	unsigned long npc;
231 	unsigned int insn;
232 	long frame_size = 0;
233 	int looking_for_rp, rpoffset = 0;
234 
235 	e = find_unwind_entry(info->ip);
236 	if (e == NULL) {
237 		unsigned long sp;
238 		extern char _stext[], _etext[];
239 
240 		dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
241 
242 #ifdef CONFIG_KALLSYMS
243 		/* Handle some frequent special cases.... */
244 		{
245 			char symname[KSYM_NAME_LEN];
246 			char *modname;
247 
248 			kallsyms_lookup(info->ip, NULL, NULL, &modname,
249 				symname);
250 
251 			dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname);
252 
253 			if (strcmp(symname, "_switch_to_ret") == 0) {
254 				info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
255 				info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
256 				dbg("_switch_to_ret @ %lx - setting "
257 				    "prev_sp=%lx prev_ip=%lx\n",
258 				    info->ip, info->prev_sp,
259 				    info->prev_ip);
260 				return;
261 			} else if (strcmp(symname, "ret_from_kernel_thread") == 0 ||
262 				   strcmp(symname, "syscall_exit") == 0) {
263 				info->prev_ip = info->prev_sp = 0;
264 				return;
265 			}
266 		}
267 #endif
268 
269 		/* Since we are doing the unwinding blind, we don't know if
270 		   we are adjusting the stack correctly or extracting the rp
271 		   correctly. The rp is checked to see if it belongs to the
272 		   kernel text section, if not we assume we don't have a
273 		   correct stack frame and we continue to unwind the stack.
274 		   This is not quite correct, and will fail for loadable
275 		   modules. */
276 		sp = info->sp & ~63;
277 		do {
278 			unsigned long tmp;
279 
280 			info->prev_sp = sp - 64;
281 			info->prev_ip = 0;
282 			if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET)))
283 				break;
284 			info->prev_ip = tmp;
285 			sp = info->prev_sp;
286 		} while (info->prev_ip < (unsigned long)_stext ||
287 			 info->prev_ip > (unsigned long)_etext);
288 
289 		info->rp = 0;
290 
291 		dbg("analyzing func @ %lx with no unwind info, setting "
292 		    "prev_sp=%lx prev_ip=%lx\n", info->ip,
293 		    info->prev_sp, info->prev_ip);
294 	} else {
295 		dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
296 		    "Save_RP = %d, Millicode = %d size = %u\n",
297 		    e->region_start, e->region_end, e->Save_SP, e->Save_RP,
298 		    e->Millicode, e->Total_frame_size);
299 
300 		looking_for_rp = e->Save_RP;
301 
302 		for (npc = e->region_start;
303 		     (frame_size < (e->Total_frame_size << 3) ||
304 		      looking_for_rp) &&
305 		     npc < info->ip;
306 		     npc += 4) {
307 
308 			insn = *(unsigned int *)npc;
309 
310 			if ((insn & 0xffffc000) == 0x37de0000 ||
311 			    (insn & 0xffe00000) == 0x6fc00000) {
312 				/* ldo X(sp), sp, or stwm X,D(sp) */
313 				frame_size += (insn & 0x1 ? -1 << 13 : 0) |
314 					((insn & 0x3fff) >> 1);
315 				dbg("analyzing func @ %lx, insn=%08x @ "
316 				    "%lx, frame_size = %ld\n", info->ip,
317 				    insn, npc, frame_size);
318 			} else if ((insn & 0xffe00008) == 0x73c00008) {
319 				/* std,ma X,D(sp) */
320 				frame_size += (insn & 0x1 ? -1 << 13 : 0) |
321 					(((insn >> 4) & 0x3ff) << 3);
322 				dbg("analyzing func @ %lx, insn=%08x @ "
323 				    "%lx, frame_size = %ld\n", info->ip,
324 				    insn, npc, frame_size);
325 			} else if (insn == 0x6bc23fd9) {
326 				/* stw rp,-20(sp) */
327 				rpoffset = 20;
328 				looking_for_rp = 0;
329 				dbg("analyzing func @ %lx, insn=stw rp,"
330 				    "-20(sp) @ %lx\n", info->ip, npc);
331 			} else if (insn == 0x0fc212c1) {
332 				/* std rp,-16(sr0,sp) */
333 				rpoffset = 16;
334 				looking_for_rp = 0;
335 				dbg("analyzing func @ %lx, insn=std rp,"
336 				    "-16(sp) @ %lx\n", info->ip, npc);
337 			}
338 		}
339 
340 		if (!unwind_special(info, e->region_start, frame_size)) {
341 			info->prev_sp = info->sp - frame_size;
342 			if (e->Millicode)
343 				info->rp = info->r31;
344 			else if (rpoffset)
345 				info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
346 			info->prev_ip = info->rp;
347 			info->rp = 0;
348 		}
349 
350 		dbg("analyzing func @ %lx, setting prev_sp=%lx "
351 		    "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp,
352 		    info->prev_ip, npc);
353 	}
354 }
355 
356 void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
357 		       struct pt_regs *regs)
358 {
359 	memset(info, 0, sizeof(struct unwind_frame_info));
360 	info->t = t;
361 	info->sp = regs->gr[30];
362 	info->ip = regs->iaoq[0];
363 	info->rp = regs->gr[2];
364 	info->r31 = regs->gr[31];
365 
366 	dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n",
367 	    t ? (int)t->pid : -1, info->sp, info->ip);
368 }
369 
370 void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
371 {
372 	struct pt_regs *r = &t->thread.regs;
373 	struct pt_regs *r2;
374 
375 	r2 = kmalloc(sizeof(struct pt_regs), GFP_KERNEL);
376 	if (!r2)
377 		return;
378 	*r2 = *r;
379 	r2->gr[30] = r->ksp;
380 	r2->iaoq[0] = r->kpc;
381 	unwind_frame_init(info, t, r2);
382 	kfree(r2);
383 }
384 
385 void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
386 {
387 	unwind_frame_init(info, current, regs);
388 }
389 
390 int unwind_once(struct unwind_frame_info *next_frame)
391 {
392 	unwind_frame_regs(next_frame);
393 
394 	if (next_frame->prev_sp == 0 ||
395 	    next_frame->prev_ip == 0)
396 		return -1;
397 
398 	next_frame->sp = next_frame->prev_sp;
399 	next_frame->ip = next_frame->prev_ip;
400 	next_frame->prev_sp = 0;
401 	next_frame->prev_ip = 0;
402 
403 	dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n",
404 	    next_frame->t ? (int)next_frame->t->pid : -1,
405 	    next_frame->sp, next_frame->ip);
406 
407 	return 0;
408 }
409 
410 int unwind_to_user(struct unwind_frame_info *info)
411 {
412 	int ret;
413 
414 	do {
415 		ret = unwind_once(info);
416 	} while (!ret && !(info->ip & 3));
417 
418 	return ret;
419 }
420 
421 module_init(unwind_init);
422