xref: /linux/arch/openrisc/kernel/unwinder.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1eecac38bSStafford Horne /*
2eecac38bSStafford Horne  * OpenRISC unwinder.c
3eecac38bSStafford Horne  *
4eecac38bSStafford Horne  * Reusable arch specific api for unwinding stacks.
5eecac38bSStafford Horne  *
6eecac38bSStafford Horne  * Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
7eecac38bSStafford Horne  *
8eecac38bSStafford Horne  * This file is licensed under the terms of the GNU General Public License
9eecac38bSStafford Horne  * version 2.  This program is licensed "as is" without any warranty of any
10eecac38bSStafford Horne  * kind, whether express or implied.
11eecac38bSStafford Horne  */
12eecac38bSStafford Horne 
13eecac38bSStafford Horne #include <linux/sched/task_stack.h>
14eecac38bSStafford Horne #include <linux/kernel.h>
15eecac38bSStafford Horne 
16eecac38bSStafford Horne #include <asm/unwinder.h>
17eecac38bSStafford Horne 
18eecac38bSStafford Horne #ifdef CONFIG_FRAME_POINTER
19eecac38bSStafford Horne struct or1k_frameinfo {
20eecac38bSStafford Horne 	unsigned long *fp;
21eecac38bSStafford Horne 	unsigned long ra;
22eecac38bSStafford Horne 	unsigned long top;
23eecac38bSStafford Horne };
24eecac38bSStafford Horne 
25eecac38bSStafford Horne /*
26eecac38bSStafford Horne  * Verify a frameinfo structure.  The return address should be a valid text
27eecac38bSStafford Horne  * address.  The frame pointer may be null if its the last frame, otherwise
28*48bddb89SXiang wangx  * the frame pointer should point to a location in the stack after the
29eecac38bSStafford Horne  * top of the next frame up.
30eecac38bSStafford Horne  */
or1k_frameinfo_valid(struct or1k_frameinfo * frameinfo)31eecac38bSStafford Horne static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo)
32eecac38bSStafford Horne {
33eecac38bSStafford Horne 	return (frameinfo->fp == NULL ||
34eecac38bSStafford Horne 		(!kstack_end(frameinfo->fp) &&
35eecac38bSStafford Horne 		 frameinfo->fp > &frameinfo->top)) &&
36eecac38bSStafford Horne 	       __kernel_text_address(frameinfo->ra);
37eecac38bSStafford Horne }
38eecac38bSStafford Horne 
39eecac38bSStafford Horne /*
40eecac38bSStafford Horne  * Create a stack trace doing scanning which is frame pointer aware. We can
41eecac38bSStafford Horne  * get reliable stack traces by matching the previously found frame
42eecac38bSStafford Horne  * pointer with the top of the stack address every time we find a valid
43eecac38bSStafford Horne  * or1k_frameinfo.
44eecac38bSStafford Horne  *
45eecac38bSStafford Horne  * Ideally the stack parameter will be passed as FP, but it can not be
46eecac38bSStafford Horne  * guaranteed.  Therefore we scan each address looking for the first sign
47eecac38bSStafford Horne  * of a return address.
48eecac38bSStafford Horne  *
49eecac38bSStafford Horne  * The OpenRISC stack frame looks something like the following.  The
50eecac38bSStafford Horne  * location SP is held in r1 and location FP is held in r2 when frame pointers
51eecac38bSStafford Horne  * enabled.
52eecac38bSStafford Horne  *
53eecac38bSStafford Horne  * SP   -> (top of stack)
54eecac38bSStafford Horne  *      -  (callee saved registers)
55eecac38bSStafford Horne  *      -  (local variables)
56eecac38bSStafford Horne  * FP-8 -> previous FP             \
57eecac38bSStafford Horne  * FP-4 -> return address          |- or1k_frameinfo
58eecac38bSStafford Horne  * FP   -> (previous top of stack) /
59eecac38bSStafford Horne  */
unwind_stack(void * data,unsigned long * stack,void (* trace)(void * data,unsigned long addr,int reliable))60eecac38bSStafford Horne void unwind_stack(void *data, unsigned long *stack,
61eecac38bSStafford Horne 		  void (*trace)(void *data, unsigned long addr, int reliable))
62eecac38bSStafford Horne {
63eecac38bSStafford Horne 	unsigned long *next_fp = NULL;
64eecac38bSStafford Horne 	struct or1k_frameinfo *frameinfo = NULL;
65eecac38bSStafford Horne 	int reliable = 0;
66eecac38bSStafford Horne 
67eecac38bSStafford Horne 	while (!kstack_end(stack)) {
68eecac38bSStafford Horne 		frameinfo = container_of(stack,
69eecac38bSStafford Horne 					 struct or1k_frameinfo,
70eecac38bSStafford Horne 					 top);
71eecac38bSStafford Horne 
72eecac38bSStafford Horne 		if (__kernel_text_address(frameinfo->ra)) {
73eecac38bSStafford Horne 			if (or1k_frameinfo_valid(frameinfo) &&
74eecac38bSStafford Horne 			    (next_fp == NULL ||
75eecac38bSStafford Horne 			     next_fp == &frameinfo->top)) {
76eecac38bSStafford Horne 				reliable = 1;
77eecac38bSStafford Horne 				next_fp = frameinfo->fp;
78eecac38bSStafford Horne 			} else
79eecac38bSStafford Horne 				reliable = 0;
80eecac38bSStafford Horne 
81eecac38bSStafford Horne 			trace(data, frameinfo->ra, reliable);
82eecac38bSStafford Horne 		}
83eecac38bSStafford Horne 		stack++;
84eecac38bSStafford Horne 	}
85eecac38bSStafford Horne }
86eecac38bSStafford Horne 
87eecac38bSStafford Horne #else /* CONFIG_FRAME_POINTER */
88eecac38bSStafford Horne 
89eecac38bSStafford Horne /*
90eecac38bSStafford Horne  * Create a stack trace by doing a simple scan treating all text addresses
91eecac38bSStafford Horne  * as return addresses.
92eecac38bSStafford Horne  */
unwind_stack(void * data,unsigned long * stack,void (* trace)(void * data,unsigned long addr,int reliable))93eecac38bSStafford Horne void unwind_stack(void *data, unsigned long *stack,
94eecac38bSStafford Horne 		   void (*trace)(void *data, unsigned long addr, int reliable))
95eecac38bSStafford Horne {
96eecac38bSStafford Horne 	unsigned long addr;
97eecac38bSStafford Horne 
98eecac38bSStafford Horne 	while (!kstack_end(stack)) {
99eecac38bSStafford Horne 		addr = *stack++;
100eecac38bSStafford Horne 		if (__kernel_text_address(addr))
101eecac38bSStafford Horne 			trace(data, addr, 0);
102eecac38bSStafford Horne 	}
103eecac38bSStafford Horne }
104eecac38bSStafford Horne #endif /* CONFIG_FRAME_POINTER */
105eecac38bSStafford Horne 
106