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