1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27c7900f8SJosh Poimboeuf #include <linux/sched.h>
37c7900f8SJosh Poimboeuf #include <linux/ftrace.h>
47c7900f8SJosh Poimboeuf #include <asm/ptrace.h>
57c7900f8SJosh Poimboeuf #include <asm/bitops.h>
67c7900f8SJosh Poimboeuf #include <asm/stacktrace.h>
77c7900f8SJosh Poimboeuf #include <asm/unwind.h>
87c7900f8SJosh Poimboeuf
unwind_get_return_address(struct unwind_state * state)9cfee9eddSJosh Poimboeuf unsigned long unwind_get_return_address(struct unwind_state *state)
10cfee9eddSJosh Poimboeuf {
1155f856e6SJosh Poimboeuf unsigned long addr;
12c2d75e03SJosh Poimboeuf
13cfee9eddSJosh Poimboeuf if (unwind_done(state))
14cfee9eddSJosh Poimboeuf return 0;
15cfee9eddSJosh Poimboeuf
1655f856e6SJosh Poimboeuf addr = READ_ONCE_NOCHECK(*state->sp);
1755f856e6SJosh Poimboeuf
18*19138af1SMasami Hiramatsu return unwind_recover_ret_addr(state, addr, state->sp);
19cfee9eddSJosh Poimboeuf }
20cfee9eddSJosh Poimboeuf EXPORT_SYMBOL_GPL(unwind_get_return_address);
21cfee9eddSJosh Poimboeuf
unwind_get_return_address_ptr(struct unwind_state * state)22ee9f8fceSJosh Poimboeuf unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
23ee9f8fceSJosh Poimboeuf {
24ee9f8fceSJosh Poimboeuf return NULL;
25ee9f8fceSJosh Poimboeuf }
26ee9f8fceSJosh Poimboeuf
unwind_next_frame(struct unwind_state * state)277c7900f8SJosh Poimboeuf bool unwind_next_frame(struct unwind_state *state)
287c7900f8SJosh Poimboeuf {
297c7900f8SJosh Poimboeuf struct stack_info *info = &state->stack_info;
307c7900f8SJosh Poimboeuf
317c7900f8SJosh Poimboeuf if (unwind_done(state))
327c7900f8SJosh Poimboeuf return false;
337c7900f8SJosh Poimboeuf
347c7900f8SJosh Poimboeuf do {
3555f856e6SJosh Poimboeuf for (state->sp++; state->sp < info->end; state->sp++) {
36c2d75e03SJosh Poimboeuf unsigned long addr = READ_ONCE_NOCHECK(*state->sp);
37c2d75e03SJosh Poimboeuf
38c2d75e03SJosh Poimboeuf if (__kernel_text_address(addr))
397c7900f8SJosh Poimboeuf return true;
4055f856e6SJosh Poimboeuf }
417c7900f8SJosh Poimboeuf
42e335bb51SJosh Poimboeuf state->sp = PTR_ALIGN(info->next_sp, sizeof(long));
437c7900f8SJosh Poimboeuf
447c7900f8SJosh Poimboeuf } while (!get_stack_info(state->sp, state->task, info,
457c7900f8SJosh Poimboeuf &state->stack_mask));
467c7900f8SJosh Poimboeuf
477c7900f8SJosh Poimboeuf return false;
487c7900f8SJosh Poimboeuf }
497c7900f8SJosh Poimboeuf EXPORT_SYMBOL_GPL(unwind_next_frame);
507c7900f8SJosh Poimboeuf
__unwind_start(struct unwind_state * state,struct task_struct * task,struct pt_regs * regs,unsigned long * first_frame)517c7900f8SJosh Poimboeuf void __unwind_start(struct unwind_state *state, struct task_struct *task,
527c7900f8SJosh Poimboeuf struct pt_regs *regs, unsigned long *first_frame)
537c7900f8SJosh Poimboeuf {
547c7900f8SJosh Poimboeuf memset(state, 0, sizeof(*state));
557c7900f8SJosh Poimboeuf
567c7900f8SJosh Poimboeuf state->task = task;
57e335bb51SJosh Poimboeuf state->sp = PTR_ALIGN(first_frame, sizeof(long));
587c7900f8SJosh Poimboeuf
597c7900f8SJosh Poimboeuf get_stack_info(first_frame, state->task, &state->stack_info,
607c7900f8SJosh Poimboeuf &state->stack_mask);
617c7900f8SJosh Poimboeuf
627fbe6ac0SJosh Poimboeuf /*
637fbe6ac0SJosh Poimboeuf * The caller can provide the address of the first frame directly
647fbe6ac0SJosh Poimboeuf * (first_frame) or indirectly (regs->sp) to indicate which stack frame
657fbe6ac0SJosh Poimboeuf * to start unwinding at. Skip ahead until we reach it.
667fbe6ac0SJosh Poimboeuf */
677fbe6ac0SJosh Poimboeuf if (!unwind_done(state) &&
687fbe6ac0SJosh Poimboeuf (!on_stack(&state->stack_info, first_frame, sizeof(long)) ||
697fbe6ac0SJosh Poimboeuf !__kernel_text_address(*first_frame)))
707c7900f8SJosh Poimboeuf unwind_next_frame(state);
717c7900f8SJosh Poimboeuf }
727c7900f8SJosh Poimboeuf EXPORT_SYMBOL_GPL(__unwind_start);
73