18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _ASM_X86_UNWIND_H
38c2ecf20Sopenharmony_ci#define _ASM_X86_UNWIND_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/sched.h>
68c2ecf20Sopenharmony_ci#include <linux/ftrace.h>
78c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
88c2ecf20Sopenharmony_ci#include <asm/stacktrace.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define IRET_FRAME_OFFSET (offsetof(struct pt_regs, ip))
118c2ecf20Sopenharmony_ci#define IRET_FRAME_SIZE   (sizeof(struct pt_regs) - IRET_FRAME_OFFSET)
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistruct unwind_state {
148c2ecf20Sopenharmony_ci	struct stack_info stack_info;
158c2ecf20Sopenharmony_ci	unsigned long stack_mask;
168c2ecf20Sopenharmony_ci	struct task_struct *task;
178c2ecf20Sopenharmony_ci	int graph_idx;
188c2ecf20Sopenharmony_ci	bool error;
198c2ecf20Sopenharmony_ci#if defined(CONFIG_UNWINDER_ORC)
208c2ecf20Sopenharmony_ci	bool signal, full_regs;
218c2ecf20Sopenharmony_ci	unsigned long sp, bp, ip;
228c2ecf20Sopenharmony_ci	struct pt_regs *regs, *prev_regs;
238c2ecf20Sopenharmony_ci#elif defined(CONFIG_UNWINDER_FRAME_POINTER)
248c2ecf20Sopenharmony_ci	bool got_irq;
258c2ecf20Sopenharmony_ci	unsigned long *bp, *orig_sp, ip;
268c2ecf20Sopenharmony_ci	/*
278c2ecf20Sopenharmony_ci	 * If non-NULL: The current frame is incomplete and doesn't contain a
288c2ecf20Sopenharmony_ci	 * valid BP. When looking for the next frame, use this instead of the
298c2ecf20Sopenharmony_ci	 * non-existent saved BP.
308c2ecf20Sopenharmony_ci	 */
318c2ecf20Sopenharmony_ci	unsigned long *next_bp;
328c2ecf20Sopenharmony_ci	struct pt_regs *regs;
338c2ecf20Sopenharmony_ci#else
348c2ecf20Sopenharmony_ci	unsigned long *sp;
358c2ecf20Sopenharmony_ci#endif
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_civoid __unwind_start(struct unwind_state *state, struct task_struct *task,
398c2ecf20Sopenharmony_ci		    struct pt_regs *regs, unsigned long *first_frame);
408c2ecf20Sopenharmony_cibool unwind_next_frame(struct unwind_state *state);
418c2ecf20Sopenharmony_ciunsigned long unwind_get_return_address(struct unwind_state *state);
428c2ecf20Sopenharmony_ciunsigned long *unwind_get_return_address_ptr(struct unwind_state *state);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic inline bool unwind_done(struct unwind_state *state)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	return state->stack_info.type == STACK_TYPE_UNKNOWN;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic inline bool unwind_error(struct unwind_state *state)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	return state->error;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic inline
558c2ecf20Sopenharmony_civoid unwind_start(struct unwind_state *state, struct task_struct *task,
568c2ecf20Sopenharmony_ci		  struct pt_regs *regs, unsigned long *first_frame)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	first_frame = first_frame ? : get_stack_pointer(task, regs);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	__unwind_start(state, task, regs, first_frame);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#if defined(CONFIG_UNWINDER_ORC) || defined(CONFIG_UNWINDER_FRAME_POINTER)
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * If 'partial' returns true, only the iret frame registers are valid.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cistatic inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state,
688c2ecf20Sopenharmony_ci						    bool *partial)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	if (unwind_done(state))
718c2ecf20Sopenharmony_ci		return NULL;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (partial) {
748c2ecf20Sopenharmony_ci#ifdef CONFIG_UNWINDER_ORC
758c2ecf20Sopenharmony_ci		*partial = !state->full_regs;
768c2ecf20Sopenharmony_ci#else
778c2ecf20Sopenharmony_ci		*partial = false;
788c2ecf20Sopenharmony_ci#endif
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return state->regs;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci#else
848c2ecf20Sopenharmony_cistatic inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state,
858c2ecf20Sopenharmony_ci						    bool *partial)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	return NULL;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci#endif
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#ifdef CONFIG_UNWINDER_ORC
928c2ecf20Sopenharmony_civoid unwind_init(void);
938c2ecf20Sopenharmony_civoid unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size,
948c2ecf20Sopenharmony_ci			void *orc, size_t orc_size);
958c2ecf20Sopenharmony_ci#else
968c2ecf20Sopenharmony_cistatic inline void unwind_init(void) {}
978c2ecf20Sopenharmony_cistatic inline
988c2ecf20Sopenharmony_civoid unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size,
998c2ecf20Sopenharmony_ci			void *orc, size_t orc_size) {}
1008c2ecf20Sopenharmony_ci#endif
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci * This disables KASAN checking when reading a value from another task's stack,
1048c2ecf20Sopenharmony_ci * since the other task could be running on another CPU and could have poisoned
1058c2ecf20Sopenharmony_ci * the stack in the meantime.
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_ci#define READ_ONCE_TASK_STACK(task, x)			\
1088c2ecf20Sopenharmony_ci({							\
1098c2ecf20Sopenharmony_ci	unsigned long val;				\
1108c2ecf20Sopenharmony_ci	if (task == current)				\
1118c2ecf20Sopenharmony_ci		val = READ_ONCE(x);			\
1128c2ecf20Sopenharmony_ci	else						\
1138c2ecf20Sopenharmony_ci		val = READ_ONCE_NOCHECK(x);		\
1148c2ecf20Sopenharmony_ci	val;						\
1158c2ecf20Sopenharmony_ci})
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic inline bool task_on_another_cpu(struct task_struct *task)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
1208c2ecf20Sopenharmony_ci	return task != current && task->on_cpu;
1218c2ecf20Sopenharmony_ci#else
1228c2ecf20Sopenharmony_ci	return false;
1238c2ecf20Sopenharmony_ci#endif
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci#endif /* _ASM_X86_UNWIND_H */
127