Linux Audio

Check our new training course

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