xref: /linux/arch/loongarch/include/asm/unwind.h (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
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