149232773SQing Zhang /* SPDX-License-Identifier: GPL-2.0 */
249232773SQing Zhang /*
349232773SQing Zhang * Most of this ideas comes from x86.
449232773SQing Zhang *
549232773SQing Zhang * Copyright (C) 2022 Loongson Technology Corporation Limited
649232773SQing Zhang */
749232773SQing Zhang #ifndef _ASM_UNWIND_H
849232773SQing Zhang #define _ASM_UNWIND_H
949232773SQing Zhang
1049232773SQing Zhang #include <linux/sched.h>
115bb8d344SJinyang He #include <linux/ftrace.h>
1249232773SQing Zhang
135bb8d344SJinyang He #include <asm/ptrace.h>
1449232773SQing Zhang #include <asm/stacktrace.h>
1549232773SQing Zhang
1649aef111SQing Zhang enum unwinder_type {
1749aef111SQing Zhang UNWINDER_GUESS,
1849aef111SQing Zhang UNWINDER_PROLOGUE,
19*cb8a2ef0STiezhu Yang UNWINDER_ORC,
2049aef111SQing Zhang };
2149aef111SQing Zhang
2249232773SQing Zhang struct unwind_state {
2349aef111SQing Zhang char type; /* UNWINDER_XXX */
2449232773SQing Zhang struct stack_info stack_info;
2549232773SQing Zhang struct task_struct *task;
26dc74a9e8SJinyang He bool first, error, reset;
27a51ac524SQing Zhang int graph_idx;
28*cb8a2ef0STiezhu Yang unsigned long sp, fp, pc, ra;
2949232773SQing Zhang };
3049232773SQing Zhang
31c5ac25e0SJinyang He bool default_next_frame(struct unwind_state *state);
32c5ac25e0SJinyang He
3349232773SQing Zhang void unwind_start(struct unwind_state *state,
3449232773SQing Zhang struct task_struct *task, struct pt_regs *regs);
3549232773SQing Zhang bool unwind_next_frame(struct unwind_state *state);
3649232773SQing Zhang unsigned long unwind_get_return_address(struct unwind_state *state);
3749232773SQing Zhang
unwind_done(struct unwind_state * state)3849232773SQing Zhang static inline bool unwind_done(struct unwind_state *state)
3949232773SQing Zhang {
4049232773SQing Zhang return state->stack_info.type == STACK_TYPE_UNKNOWN;
4149232773SQing Zhang }
4249232773SQing Zhang
unwind_error(struct unwind_state * state)4349232773SQing Zhang static inline bool unwind_error(struct unwind_state *state)
4449232773SQing Zhang {
4549232773SQing Zhang return state->error;
4649232773SQing Zhang }
4749232773SQing Zhang
485bb8d344SJinyang He #define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
495bb8d344SJinyang He
unwind_graph_addr(struct unwind_state * state,unsigned long pc,unsigned long cfa)505bb8d344SJinyang He static inline unsigned long unwind_graph_addr(struct unwind_state *state,
515bb8d344SJinyang He unsigned long pc, unsigned long cfa)
525bb8d344SJinyang He {
535bb8d344SJinyang He return ftrace_graph_ret_addr(state->task, &state->graph_idx,
545bb8d344SJinyang He pc, (unsigned long *)(cfa - GRAPH_FAKE_OFFSET));
555bb8d344SJinyang He }
56c5ac25e0SJinyang He
__unwind_start(struct unwind_state * state,struct task_struct * task,struct pt_regs * regs)57c5ac25e0SJinyang He static __always_inline void __unwind_start(struct unwind_state *state,
58c5ac25e0SJinyang He struct task_struct *task, struct pt_regs *regs)
59c5ac25e0SJinyang He {
60c5ac25e0SJinyang He memset(state, 0, sizeof(*state));
61c5ac25e0SJinyang He if (regs) {
62c5ac25e0SJinyang He state->sp = regs->regs[3];
63c5ac25e0SJinyang He state->pc = regs->csr_era;
64c5ac25e0SJinyang He state->ra = regs->regs[1];
65*cb8a2ef0STiezhu Yang state->fp = regs->regs[22];
66c5ac25e0SJinyang He } else if (task && task != current) {
67c5ac25e0SJinyang He state->sp = thread_saved_fp(task);
68c5ac25e0SJinyang He state->pc = thread_saved_ra(task);
69c5ac25e0SJinyang He state->ra = 0;
70*cb8a2ef0STiezhu Yang state->fp = 0;
71c5ac25e0SJinyang He } else {
72c5ac25e0SJinyang He state->sp = (unsigned long)__builtin_frame_address(0);
73c5ac25e0SJinyang He state->pc = (unsigned long)__builtin_return_address(0);
74c5ac25e0SJinyang He state->ra = 0;
75*cb8a2ef0STiezhu Yang state->fp = 0;
76c5ac25e0SJinyang He }
77c5ac25e0SJinyang He state->task = task;
78c5ac25e0SJinyang He get_stack_info(state->sp, state->task, &state->stack_info);
79c5ac25e0SJinyang He state->pc = unwind_graph_addr(state, state->pc, state->sp);
80c5ac25e0SJinyang He }
81c5ac25e0SJinyang He
__unwind_get_return_address(struct unwind_state * state)82c5ac25e0SJinyang He static __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state)
83c5ac25e0SJinyang He {
84*cb8a2ef0STiezhu Yang if (unwind_done(state))
85*cb8a2ef0STiezhu Yang return 0;
86*cb8a2ef0STiezhu Yang
87*cb8a2ef0STiezhu Yang return __kernel_text_address(state->pc) ? state->pc : 0;
88c5ac25e0SJinyang He }
89*cb8a2ef0STiezhu Yang
90*cb8a2ef0STiezhu Yang #ifdef CONFIG_UNWINDER_ORC
91*cb8a2ef0STiezhu Yang void unwind_init(void);
92*cb8a2ef0STiezhu Yang void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size);
93*cb8a2ef0STiezhu Yang #else
unwind_init(void)94*cb8a2ef0STiezhu Yang static inline void unwind_init(void) {}
unwind_module_init(struct module * mod,void * orc_ip,size_t orc_ip_size,void * orc,size_t orc_size)95*cb8a2ef0STiezhu Yang static inline void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size) {}
96*cb8a2ef0STiezhu Yang #endif
97*cb8a2ef0STiezhu Yang
9849232773SQing Zhang #endif /* _ASM_UNWIND_H */
99