162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef _ASM_X86_UNWIND_H
362306a36Sopenharmony_ci#define _ASM_X86_UNWIND_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/sched.h>
662306a36Sopenharmony_ci#include <linux/ftrace.h>
762306a36Sopenharmony_ci#include <linux/rethook.h>
862306a36Sopenharmony_ci#include <asm/ptrace.h>
962306a36Sopenharmony_ci#include <asm/stacktrace.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define IRET_FRAME_OFFSET (offsetof(struct pt_regs, ip))
1262306a36Sopenharmony_ci#define IRET_FRAME_SIZE   (sizeof(struct pt_regs) - IRET_FRAME_OFFSET)
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct unwind_state {
1562306a36Sopenharmony_ci	struct stack_info stack_info;
1662306a36Sopenharmony_ci	unsigned long stack_mask;
1762306a36Sopenharmony_ci	struct task_struct *task;
1862306a36Sopenharmony_ci	int graph_idx;
1962306a36Sopenharmony_ci#if defined(CONFIG_RETHOOK)
2062306a36Sopenharmony_ci	struct llist_node *kr_cur;
2162306a36Sopenharmony_ci#endif
2262306a36Sopenharmony_ci	bool error;
2362306a36Sopenharmony_ci#if defined(CONFIG_UNWINDER_ORC)
2462306a36Sopenharmony_ci	bool signal, full_regs;
2562306a36Sopenharmony_ci	unsigned long sp, bp, ip;
2662306a36Sopenharmony_ci	struct pt_regs *regs, *prev_regs;
2762306a36Sopenharmony_ci#elif defined(CONFIG_UNWINDER_FRAME_POINTER)
2862306a36Sopenharmony_ci	bool got_irq;
2962306a36Sopenharmony_ci	unsigned long *bp, *orig_sp, ip;
3062306a36Sopenharmony_ci	/*
3162306a36Sopenharmony_ci	 * If non-NULL: The current frame is incomplete and doesn't contain a
3262306a36Sopenharmony_ci	 * valid BP. When looking for the next frame, use this instead of the
3362306a36Sopenharmony_ci	 * non-existent saved BP.
3462306a36Sopenharmony_ci	 */
3562306a36Sopenharmony_ci	unsigned long *next_bp;
3662306a36Sopenharmony_ci	struct pt_regs *regs;
3762306a36Sopenharmony_ci#else
3862306a36Sopenharmony_ci	unsigned long *sp;
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_civoid __unwind_start(struct unwind_state *state, struct task_struct *task,
4362306a36Sopenharmony_ci		    struct pt_regs *regs, unsigned long *first_frame);
4462306a36Sopenharmony_cibool unwind_next_frame(struct unwind_state *state);
4562306a36Sopenharmony_ciunsigned long unwind_get_return_address(struct unwind_state *state);
4662306a36Sopenharmony_ciunsigned long *unwind_get_return_address_ptr(struct unwind_state *state);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline bool unwind_done(struct unwind_state *state)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	return state->stack_info.type == STACK_TYPE_UNKNOWN;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic inline bool unwind_error(struct unwind_state *state)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	return state->error;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic inline
5962306a36Sopenharmony_civoid unwind_start(struct unwind_state *state, struct task_struct *task,
6062306a36Sopenharmony_ci		  struct pt_regs *regs, unsigned long *first_frame)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	first_frame = first_frame ? : get_stack_pointer(task, regs);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	__unwind_start(state, task, regs, first_frame);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#if defined(CONFIG_UNWINDER_ORC) || defined(CONFIG_UNWINDER_FRAME_POINTER)
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * If 'partial' returns true, only the iret frame registers are valid.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_cistatic inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state,
7262306a36Sopenharmony_ci						    bool *partial)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	if (unwind_done(state))
7562306a36Sopenharmony_ci		return NULL;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (partial) {
7862306a36Sopenharmony_ci#ifdef CONFIG_UNWINDER_ORC
7962306a36Sopenharmony_ci		*partial = !state->full_regs;
8062306a36Sopenharmony_ci#else
8162306a36Sopenharmony_ci		*partial = false;
8262306a36Sopenharmony_ci#endif
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return state->regs;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci#else
8862306a36Sopenharmony_cistatic inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state,
8962306a36Sopenharmony_ci						    bool *partial)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	return NULL;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci#endif
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#ifdef CONFIG_UNWINDER_ORC
9662306a36Sopenharmony_civoid unwind_init(void);
9762306a36Sopenharmony_civoid unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size,
9862306a36Sopenharmony_ci			void *orc, size_t orc_size);
9962306a36Sopenharmony_ci#else
10062306a36Sopenharmony_cistatic inline void unwind_init(void) {}
10162306a36Sopenharmony_cistatic inline
10262306a36Sopenharmony_civoid unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size,
10362306a36Sopenharmony_ci			void *orc, size_t orc_size) {}
10462306a36Sopenharmony_ci#endif
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic inline
10762306a36Sopenharmony_ciunsigned long unwind_recover_rethook(struct unwind_state *state,
10862306a36Sopenharmony_ci				     unsigned long addr, unsigned long *addr_p)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci#ifdef CONFIG_RETHOOK
11162306a36Sopenharmony_ci	if (is_rethook_trampoline(addr))
11262306a36Sopenharmony_ci		return rethook_find_ret_addr(state->task, (unsigned long)addr_p,
11362306a36Sopenharmony_ci					     &state->kr_cur);
11462306a36Sopenharmony_ci#endif
11562306a36Sopenharmony_ci	return addr;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/* Recover the return address modified by rethook and ftrace_graph. */
11962306a36Sopenharmony_cistatic inline
12062306a36Sopenharmony_ciunsigned long unwind_recover_ret_addr(struct unwind_state *state,
12162306a36Sopenharmony_ci				     unsigned long addr, unsigned long *addr_p)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	unsigned long ret;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ret = ftrace_graph_ret_addr(state->task, &state->graph_idx,
12662306a36Sopenharmony_ci				    addr, addr_p);
12762306a36Sopenharmony_ci	return unwind_recover_rethook(state, ret, addr_p);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/*
13162306a36Sopenharmony_ci * This disables KASAN checking when reading a value from another task's stack,
13262306a36Sopenharmony_ci * since the other task could be running on another CPU and could have poisoned
13362306a36Sopenharmony_ci * the stack in the meantime.
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_ci#define READ_ONCE_TASK_STACK(task, x)			\
13662306a36Sopenharmony_ci({							\
13762306a36Sopenharmony_ci	unsigned long val;				\
13862306a36Sopenharmony_ci	if (task == current)				\
13962306a36Sopenharmony_ci		val = READ_ONCE(x);			\
14062306a36Sopenharmony_ci	else						\
14162306a36Sopenharmony_ci		val = READ_ONCE_NOCHECK(x);		\
14262306a36Sopenharmony_ci	val;						\
14362306a36Sopenharmony_ci})
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic inline bool task_on_another_cpu(struct task_struct *task)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci#ifdef CONFIG_SMP
14862306a36Sopenharmony_ci	return task != current && task->on_cpu;
14962306a36Sopenharmony_ci#else
15062306a36Sopenharmony_ci	return false;
15162306a36Sopenharmony_ci#endif
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#endif /* _ASM_X86_UNWIND_H */
155