xref: /openbmc/linux/arch/x86/kernel/unwind_guess.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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