162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Most of this ideas comes from x86. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2022 Loongson Technology Corporation Limited 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#ifndef _ASM_UNWIND_H 862306a36Sopenharmony_ci#define _ASM_UNWIND_H 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/ftrace.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <asm/ptrace.h> 1462306a36Sopenharmony_ci#include <asm/stacktrace.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cienum unwinder_type { 1762306a36Sopenharmony_ci UNWINDER_GUESS, 1862306a36Sopenharmony_ci UNWINDER_PROLOGUE, 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct unwind_state { 2262306a36Sopenharmony_ci char type; /* UNWINDER_XXX */ 2362306a36Sopenharmony_ci struct stack_info stack_info; 2462306a36Sopenharmony_ci struct task_struct *task; 2562306a36Sopenharmony_ci bool first, error, reset; 2662306a36Sopenharmony_ci int graph_idx; 2762306a36Sopenharmony_ci unsigned long sp, pc, ra; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cibool default_next_frame(struct unwind_state *state); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_civoid unwind_start(struct unwind_state *state, 3362306a36Sopenharmony_ci struct task_struct *task, struct pt_regs *regs); 3462306a36Sopenharmony_cibool unwind_next_frame(struct unwind_state *state); 3562306a36Sopenharmony_ciunsigned long unwind_get_return_address(struct unwind_state *state); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic inline bool unwind_done(struct unwind_state *state) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci return state->stack_info.type == STACK_TYPE_UNKNOWN; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline bool unwind_error(struct unwind_state *state) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci return state->error; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1])) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic inline unsigned long unwind_graph_addr(struct unwind_state *state, 5062306a36Sopenharmony_ci unsigned long pc, unsigned long cfa) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci return ftrace_graph_ret_addr(state->task, &state->graph_idx, 5362306a36Sopenharmony_ci pc, (unsigned long *)(cfa - GRAPH_FAKE_OFFSET)); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic __always_inline void __unwind_start(struct unwind_state *state, 5762306a36Sopenharmony_ci struct task_struct *task, struct pt_regs *regs) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci memset(state, 0, sizeof(*state)); 6062306a36Sopenharmony_ci if (regs) { 6162306a36Sopenharmony_ci state->sp = regs->regs[3]; 6262306a36Sopenharmony_ci state->pc = regs->csr_era; 6362306a36Sopenharmony_ci state->ra = regs->regs[1]; 6462306a36Sopenharmony_ci } else if (task && task != current) { 6562306a36Sopenharmony_ci state->sp = thread_saved_fp(task); 6662306a36Sopenharmony_ci state->pc = thread_saved_ra(task); 6762306a36Sopenharmony_ci state->ra = 0; 6862306a36Sopenharmony_ci } else { 6962306a36Sopenharmony_ci state->sp = (unsigned long)__builtin_frame_address(0); 7062306a36Sopenharmony_ci state->pc = (unsigned long)__builtin_return_address(0); 7162306a36Sopenharmony_ci state->ra = 0; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci state->task = task; 7462306a36Sopenharmony_ci get_stack_info(state->sp, state->task, &state->stack_info); 7562306a36Sopenharmony_ci state->pc = unwind_graph_addr(state, state->pc, state->sp); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci return unwind_done(state) ? 0 : state->pc; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci#endif /* _ASM_UNWIND_H */ 83