LoongArch: Add prologue unwinder support
[platform/kernel/linux-starfive.git] / arch / loongarch / kernel / unwind_prologue.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022 Loongson Technology Corporation Limited
4  */
5 #include <linux/kallsyms.h>
6
7 #include <asm/inst.h>
8 #include <asm/ptrace.h>
9 #include <asm/unwind.h>
10
11 unsigned long unwind_get_return_address(struct unwind_state *state)
12 {
13
14         if (unwind_done(state))
15                 return 0;
16         else if (state->type)
17                 return state->pc;
18         else if (state->first)
19                 return state->pc;
20
21         return *(unsigned long *)(state->sp);
22
23 }
24 EXPORT_SYMBOL_GPL(unwind_get_return_address);
25
26 static bool unwind_by_guess(struct unwind_state *state)
27 {
28         struct stack_info *info = &state->stack_info;
29         unsigned long addr;
30
31         for (state->sp += sizeof(unsigned long);
32              state->sp < info->end;
33              state->sp += sizeof(unsigned long)) {
34                 addr = *(unsigned long *)(state->sp);
35                 if (__kernel_text_address(addr))
36                         return true;
37         }
38
39         return false;
40 }
41
42 static bool unwind_by_prologue(struct unwind_state *state)
43 {
44         struct stack_info *info = &state->stack_info;
45         union loongarch_instruction *ip, *ip_end;
46         unsigned long frame_size = 0, frame_ra = -1;
47         unsigned long size, offset, pc = state->pc;
48
49         if (state->sp >= info->end || state->sp < info->begin)
50                 return false;
51
52         if (!kallsyms_lookup_size_offset(pc, &size, &offset))
53                 return false;
54
55         ip = (union loongarch_instruction *)(pc - offset);
56         ip_end = (union loongarch_instruction *)pc;
57
58         while (ip < ip_end) {
59                 if (is_stack_alloc_ins(ip)) {
60                         frame_size = (1 << 12) - ip->reg2i12_format.immediate;
61                         ip++;
62                         break;
63                 }
64                 ip++;
65         }
66
67         if (!frame_size) {
68                 if (state->first)
69                         goto first;
70
71                 return false;
72         }
73
74         while (ip < ip_end) {
75                 if (is_ra_save_ins(ip)) {
76                         frame_ra = ip->reg2i12_format.immediate;
77                         break;
78                 }
79                 if (is_branch_ins(ip))
80                         break;
81                 ip++;
82         }
83
84         if (frame_ra < 0) {
85                 if (state->first) {
86                         state->sp = state->sp + frame_size;
87                         goto first;
88                 }
89                 return false;
90         }
91
92         if (state->first)
93                 state->first = false;
94
95         state->pc = *(unsigned long *)(state->sp + frame_ra);
96         state->sp = state->sp + frame_size;
97         return !!__kernel_text_address(state->pc);
98
99 first:
100         state->first = false;
101         if (state->pc == state->ra)
102                 return false;
103
104         state->pc = state->ra;
105
106         return !!__kernel_text_address(state->ra);
107 }
108
109 void unwind_start(struct unwind_state *state, struct task_struct *task,
110                     struct pt_regs *regs)
111 {
112         memset(state, 0, sizeof(*state));
113
114         if (regs &&  __kernel_text_address(regs->csr_era)) {
115                 state->pc = regs->csr_era;
116                 state->sp = regs->regs[3];
117                 state->ra = regs->regs[1];
118                 state->type = UNWINDER_PROLOGUE;
119         }
120
121         state->task = task;
122         state->first = true;
123
124         get_stack_info(state->sp, state->task, &state->stack_info);
125
126         if (!unwind_done(state) && !__kernel_text_address(state->pc))
127                 unwind_next_frame(state);
128 }
129 EXPORT_SYMBOL_GPL(unwind_start);
130
131 bool unwind_next_frame(struct unwind_state *state)
132 {
133         struct stack_info *info = &state->stack_info;
134         struct pt_regs *regs;
135         unsigned long pc;
136
137         if (unwind_done(state))
138                 return false;
139
140         do {
141                 switch (state->type) {
142                 case UNWINDER_GUESS:
143                         state->first = false;
144                         if (unwind_by_guess(state))
145                                 return true;
146                         break;
147
148                 case UNWINDER_PROLOGUE:
149                         if (unwind_by_prologue(state))
150                                 return true;
151
152                         if (info->type == STACK_TYPE_IRQ &&
153                                 info->end == state->sp) {
154                                 regs = (struct pt_regs *)info->next_sp;
155                                 pc = regs->csr_era;
156
157                                 if (user_mode(regs) || !__kernel_text_address(pc))
158                                         return false;
159
160                                 state->pc = pc;
161                                 state->sp = regs->regs[3];
162                                 state->ra = regs->regs[1];
163                                 state->first = true;
164                                 get_stack_info(state->sp, state->task, info);
165
166                                 return true;
167                         }
168                 }
169
170                 state->sp = info->next_sp;
171
172         } while (!get_stack_info(state->sp, state->task, info));
173
174         return false;
175 }
176 EXPORT_SYMBOL_GPL(unwind_next_frame);