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