File Index
Files and directories
Sign in
FILE

unwind_prologue.c

File preview with syntax highlighting.

linux-6.19.14/arch/loongarch/kernel/unwind_prologue.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2022 Loongson Technology Corporation Limited
  4 */
  5#include <linux/cpumask.h>
  6#include <linux/export.h>
  7#include <linux/ftrace.h>
  8#include <linux/kallsyms.h>
  9
 10#include <asm/inst.h>
 11#include <asm/loongson.h>
 12#include <asm/ptrace.h>
 13#include <asm/setup.h>
 14#include <asm/unwind.h>
 15
 16extern const int unwind_hint_ade;
 17extern const int unwind_hint_ale;
 18extern const int unwind_hint_bp;
 19extern const int unwind_hint_fpe;
 20extern const int unwind_hint_fpu;
 21extern const int unwind_hint_lsx;
 22extern const int unwind_hint_lasx;
 23extern const int unwind_hint_lbt;
 24extern const int unwind_hint_ri;
 25extern const int unwind_hint_watch;
 26
 27static inline bool scan_handlers(unsigned long entry_offset)
 28{
 29	int idx, offset;
 30
 31	if (entry_offset >= EXCCODE_INT_START * VECSIZE)
 32		return false;
 33
 34	idx = entry_offset / VECSIZE;
 35	offset = entry_offset % VECSIZE;
 36	switch (idx) {
 37	case EXCCODE_ADE:
 38		return offset == unwind_hint_ade;
 39	case EXCCODE_ALE:
 40		return offset == unwind_hint_ale;
 41	case EXCCODE_BP:
 42		return offset == unwind_hint_bp;
 43	case EXCCODE_FPE:
 44		return offset == unwind_hint_fpe;
 45	case EXCCODE_FPDIS:
 46		return offset == unwind_hint_fpu;
 47	case EXCCODE_LSXDIS:
 48		return offset == unwind_hint_lsx;
 49	case EXCCODE_LASXDIS:
 50		return offset == unwind_hint_lasx;
 51	case EXCCODE_BTDIS:
 52		return offset == unwind_hint_lbt;
 53	case EXCCODE_INE:
 54		return offset == unwind_hint_ri;
 55	case EXCCODE_WATCH:
 56		return offset == unwind_hint_watch;
 57	default:
 58		return false;
 59	}
 60}
 61
 62static inline bool fix_exception(unsigned long pc)
 63{
 64#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
 65	int cpu;
 66
 67	for_each_possible_cpu(cpu) {
 68		if (!pcpu_handlers[cpu])
 69			continue;
 70		if (scan_handlers(pc - pcpu_handlers[cpu]))
 71			return true;
 72	}
 73#endif
 74	return scan_handlers(pc - eentry);
 75}
 76
 77/*
 78 * As we meet ftrace_regs_entry, reset first flag like first doing
 79 * tracing. Prologue analysis will stop soon because PC is at entry.
 80 */
 81static inline bool fix_ftrace(unsigned long pc)
 82{
 83#ifdef CONFIG_DYNAMIC_FTRACE
 84	return pc == (unsigned long)ftrace_call + LOONGARCH_INSN_SIZE;
 85#else
 86	return false;
 87#endif
 88}
 89
 90static inline bool unwind_state_fixup(struct unwind_state *state)
 91{
 92	if (!fix_exception(state->pc) && !fix_ftrace(state->pc))
 93		return false;
 94
 95	state->reset = true;
 96	return true;
 97}
 98
 99/*
100 * LoongArch function prologue is like follows,
101 *     [instructions not use stack var]
102 *     addi.d sp, sp, -imm
103 *     st.d   xx, sp, offset <- save callee saved regs and
104 *     st.d   yy, sp, offset    save ra if function is nest.
105 *     [others instructions]
106 */
107static bool unwind_by_prologue(struct unwind_state *state)
108{
109	long frame_ra = -1;
110	unsigned long frame_size = 0;
111	unsigned long size, offset, pc;
112	struct pt_regs *regs;
113	struct stack_info *info = &state->stack_info;
114	union loongarch_instruction *ip, *ip_end;
115
116	if (state->sp >= info->end || state->sp < info->begin)
117		return false;
118
119	if (state->reset) {
120		regs = (struct pt_regs *)state->sp;
121		state->first = true;
122		state->reset = false;
123		state->pc = regs->csr_era;
124		state->ra = regs->regs[1];
125		state->sp = regs->regs[3];
126		return true;
127	}
128
129	/*
130	 * When first is not set, the PC is a return address in the previous frame.
131	 * We need to adjust its value in case overflow to the next symbol.
132	 */
133	pc = state->pc - (state->first ? 0 : LOONGARCH_INSN_SIZE);
134	if (!kallsyms_lookup_size_offset(pc, &size, &offset))
135		return false;
136
137	ip = (union loongarch_instruction *)(pc - offset);
138	ip_end = (union loongarch_instruction *)pc;
139
140	while (ip < ip_end) {
141		if (is_stack_alloc_ins(ip)) {
142			frame_size = (1 << 12) - ip->reg2i12_format.immediate;
143			ip++;
144			break;
145		}
146		ip++;
147	}
148
149	/*
150	 * Can't find stack alloc action, PC may be in a leaf function. Only the
151	 * first being true is reasonable, otherwise indicate analysis is broken.
152	 */
153	if (!frame_size) {
154		if (state->first)
155			goto first;
156
157		return false;
158	}
159
160	while (ip < ip_end) {
161		if (is_ra_save_ins(ip)) {
162			frame_ra = ip->reg2i12_format.immediate;
163			break;
164		}
165		if (is_branch_ins(ip))
166			break;
167		ip++;
168	}
169
170	/* Can't find save $ra action, PC may be in a leaf function, too. */
171	if (frame_ra < 0) {
172		if (state->first) {
173			state->sp = state->sp + frame_size;
174			goto first;
175		}
176		return false;
177	}
178
179	state->pc = *(unsigned long *)(state->sp + frame_ra);
180	state->sp = state->sp + frame_size;
181	goto out;
182
183first:
184	state->pc = state->ra;
185
186out:
187	state->first = false;
188	return unwind_state_fixup(state) || __kernel_text_address(state->pc);
189}
190
191static bool next_frame(struct unwind_state *state)
192{
193	unsigned long pc;
194	struct pt_regs *regs;
195	struct stack_info *info = &state->stack_info;
196
197	if (unwind_done(state))
198		return false;
199
200	do {
201		if (unwind_by_prologue(state)) {
202			state->pc = unwind_graph_addr(state, state->pc, state->sp);
203			return true;
204		}
205
206		if (info->type == STACK_TYPE_IRQ && info->end == state->sp) {
207			regs = (struct pt_regs *)info->next_sp;
208			pc = regs->csr_era;
209
210			if (user_mode(regs) || !__kernel_text_address(pc))
211				goto out;
212
213			state->first = true;
214			state->pc = pc;
215			state->ra = regs->regs[1];
216			state->sp = regs->regs[3];
217			get_stack_info(state->sp, state->task, info);
218
219			return true;
220		}
221
222		state->sp = info->next_sp;
223
224	} while (!get_stack_info(state->sp, state->task, info));
225
226out:
227	state->stack_info.type = STACK_TYPE_UNKNOWN;
228	return false;
229}
230
231unsigned long unwind_get_return_address(struct unwind_state *state)
232{
233	return __unwind_get_return_address(state);
234}
235EXPORT_SYMBOL_GPL(unwind_get_return_address);
236
237void unwind_start(struct unwind_state *state, struct task_struct *task,
238		    struct pt_regs *regs)
239{
240	__unwind_start(state, task, regs);
241	state->type = UNWINDER_PROLOGUE;
242	state->first = true;
243
244	/*
245	 * The current PC is not kernel text address, we cannot find its
246	 * relative symbol. Thus, prologue analysis will be broken. Luckily,
247	 * we can use the default_next_frame().
248	 */
249	if (!__kernel_text_address(state->pc)) {
250		state->type = UNWINDER_GUESS;
251		if (!unwind_done(state))
252			unwind_next_frame(state);
253	}
254}
255EXPORT_SYMBOL_GPL(unwind_start);
256
257bool unwind_next_frame(struct unwind_state *state)
258{
259	return state->type == UNWINDER_PROLOGUE ?
260			next_frame(state) : default_next_frame(state);
261}
262EXPORT_SYMBOL_GPL(unwind_next_frame);
~/ Read-only file preview.