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