Merge tag 'nfs-for-6.2-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[linux-2.6-microblaze.git] / arch / loongarch / kernel / unwind_guess.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022 Loongson Technology Corporation Limited
4  */
5 #include <linux/kernel.h>
6 #include <linux/ftrace.h>
7
8 #include <asm/unwind.h>
9
10 unsigned long unwind_get_return_address(struct unwind_state *state)
11 {
12         if (unwind_done(state))
13                 return 0;
14         else if (state->first)
15                 return state->pc;
16
17         return *(unsigned long *)(state->sp);
18 }
19 EXPORT_SYMBOL_GPL(unwind_get_return_address);
20
21 void unwind_start(struct unwind_state *state, struct task_struct *task,
22                     struct pt_regs *regs)
23 {
24         memset(state, 0, sizeof(*state));
25
26         if (regs) {
27                 state->sp = regs->regs[3];
28                 state->pc = regs->csr_era;
29         }
30
31         state->task = task;
32         state->first = true;
33
34         get_stack_info(state->sp, state->task, &state->stack_info);
35
36         if (!unwind_done(state) && !__kernel_text_address(state->pc))
37                 unwind_next_frame(state);
38 }
39 EXPORT_SYMBOL_GPL(unwind_start);
40
41 bool unwind_next_frame(struct unwind_state *state)
42 {
43         struct stack_info *info = &state->stack_info;
44         unsigned long addr;
45
46         if (unwind_done(state))
47                 return false;
48
49         if (state->first)
50                 state->first = false;
51
52         do {
53                 for (state->sp += sizeof(unsigned long);
54                      state->sp < info->end;
55                      state->sp += sizeof(unsigned long)) {
56                         addr = *(unsigned long *)(state->sp);
57                         state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
58                                         addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
59                         if (__kernel_text_address(addr))
60                                 return true;
61                 }
62
63                 state->sp = info->next_sp;
64
65         } while (!get_stack_info(state->sp, state->task, info));
66
67         return false;
68 }
69 EXPORT_SYMBOL_GPL(unwind_next_frame);