Linux Audio

Check our new training course

Loading...
v4.6
 
  1/*
  2 * Stack tracing support
  3 *
  4 * Copyright (C) 2012 ARM Ltd.
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License version 2 as
  8 * published by the Free Software Foundation.
  9 *
 10 * This program is distributed in the hope that it will be useful,
 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 * GNU General Public License for more details.
 14 *
 15 * You should have received a copy of the GNU General Public License
 16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 17 */
 18#include <linux/kernel.h>
 19#include <linux/export.h>
 20#include <linux/ftrace.h>
 
 21#include <linux/sched.h>
 
 
 22#include <linux/stacktrace.h>
 23
 24#include <asm/irq.h>
 
 
 25#include <asm/stacktrace.h>
 26
 27/*
 28 * AArch64 PCS assigns the frame pointer to x29.
 29 *
 30 * A simple function prologue looks like this:
 31 * 	sub	sp, sp, #0x10
 32 *   	stp	x29, x30, [sp]
 33 *	mov	x29, sp
 34 *
 35 * A simple function epilogue looks like this:
 36 *	mov	sp, x29
 37 *	ldp	x29, x30, [sp]
 38 *	add	sp, sp, #0x10
 39 */
 
 
 
 
 
 
 
 
 40int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
 41{
 42	unsigned long high, low;
 43	unsigned long fp = frame->fp;
 44	unsigned long irq_stack_ptr;
 45
 46	/*
 47	 * Switching between stacks is valid when tracing current and in
 48	 * non-preemptible context.
 49	 */
 50	if (tsk == current && !preemptible())
 51		irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id());
 52	else
 53		irq_stack_ptr = 0;
 54
 55	low  = frame->sp;
 56	/* irq stacks are not THREAD_SIZE aligned */
 57	if (on_irq_stack(frame->sp, raw_smp_processor_id()))
 58		high = irq_stack_ptr;
 59	else
 60		high = ALIGN(low, THREAD_SIZE) - 0x20;
 61
 62	if (fp < low || fp > high || fp & 0xf)
 63		return -EINVAL;
 64
 65	frame->sp = fp + 0x10;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 66	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
 67	frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8));
 
 
 68
 69#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 70	if (tsk && tsk->ret_stack &&
 71			(frame->pc == (unsigned long)return_to_handler)) {
 
 72		/*
 73		 * This is a case where function graph tracer has
 74		 * modified a return address (LR) in a stack frame
 75		 * to hook a function return.
 76		 * So replace it to an original value.
 77		 */
 78		frame->pc = tsk->ret_stack[frame->graph--].ret;
 
 
 
 79	}
 80#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 81
 
 
 82	/*
 83	 * Check whether we are going to walk through from interrupt stack
 84	 * to task stack.
 85	 * If we reach the end of the stack - and its an interrupt stack,
 86	 * unpack the dummy frame to find the original elr.
 87	 *
 88	 * Check the frame->fp we read from the bottom of the irq_stack,
 89	 * and the original task stack pointer are both in current->stack.
 90	 */
 91	if (frame->sp == irq_stack_ptr) {
 92		struct pt_regs *irq_args;
 93		unsigned long orig_sp = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr);
 94
 95		if (object_is_on_stack((void *)orig_sp) &&
 96		   object_is_on_stack((void *)frame->fp)) {
 97			frame->sp = orig_sp;
 98
 99			/* orig_sp is the saved pt_regs, find the elr */
100			irq_args = (struct pt_regs *)orig_sp;
101			frame->pc = irq_args->pc;
102		} else {
103			/*
104			 * This frame has a non-standard format, and we
105			 * didn't fix it, because the data looked wrong.
106			 * Refuse to output this frame.
107			 */
108			return -EINVAL;
109		}
110	}
111
112	return 0;
113}
 
114
115void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
116		     int (*fn)(struct stackframe *, void *), void *data)
117{
118	while (1) {
119		int ret;
120
121		if (fn(frame, data))
122			break;
123		ret = unwind_frame(tsk, frame);
124		if (ret < 0)
125			break;
126	}
127}
128EXPORT_SYMBOL(walk_stackframe);
129
130#ifdef CONFIG_STACKTRACE
131struct stack_trace_data {
132	struct stack_trace *trace;
133	unsigned int no_sched_functions;
134	unsigned int skip;
135};
136
137static int save_trace(struct stackframe *frame, void *d)
138{
139	struct stack_trace_data *data = d;
140	struct stack_trace *trace = data->trace;
141	unsigned long addr = frame->pc;
142
143	if (data->no_sched_functions && in_sched_functions(addr))
144		return 0;
145	if (data->skip) {
146		data->skip--;
147		return 0;
148	}
149
150	trace->entries[trace->nr_entries++] = addr;
151
152	return trace->nr_entries >= trace->max_entries;
153}
154
155void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156{
157	struct stack_trace_data data;
158	struct stackframe frame;
159
 
 
 
160	data.trace = trace;
161	data.skip = trace->skip;
 
162
163	if (tsk != current) {
164		data.no_sched_functions = 1;
165		frame.fp = thread_saved_fp(tsk);
166		frame.sp = thread_saved_sp(tsk);
167		frame.pc = thread_saved_pc(tsk);
168	} else {
169		data.no_sched_functions = 0;
170		frame.fp = (unsigned long)__builtin_frame_address(0);
171		frame.sp = current_stack_pointer;
172		frame.pc = (unsigned long)save_stack_trace_tsk;
 
173	}
174#ifdef CONFIG_FUNCTION_GRAPH_TRACER
175	frame.graph = tsk->curr_ret_stack;
176#endif
177
178	walk_stackframe(tsk, &frame, save_trace, &data);
179	if (trace->nr_entries < trace->max_entries)
180		trace->entries[trace->nr_entries++] = ULONG_MAX;
 
 
 
 
 
181}
 
182
183void save_stack_trace(struct stack_trace *trace)
184{
185	save_stack_trace_tsk(current, trace);
186}
 
187EXPORT_SYMBOL_GPL(save_stack_trace);
188#endif
v5.9
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Stack tracing support
  4 *
  5 * Copyright (C) 2012 ARM Ltd.
 
 
 
 
 
 
 
 
 
 
 
 
  6 */
  7#include <linux/kernel.h>
  8#include <linux/export.h>
  9#include <linux/ftrace.h>
 10#include <linux/kprobes.h>
 11#include <linux/sched.h>
 12#include <linux/sched/debug.h>
 13#include <linux/sched/task_stack.h>
 14#include <linux/stacktrace.h>
 15
 16#include <asm/irq.h>
 17#include <asm/pointer_auth.h>
 18#include <asm/stack_pointer.h>
 19#include <asm/stacktrace.h>
 20
 21/*
 22 * AArch64 PCS assigns the frame pointer to x29.
 23 *
 24 * A simple function prologue looks like this:
 25 * 	sub	sp, sp, #0x10
 26 *   	stp	x29, x30, [sp]
 27 *	mov	x29, sp
 28 *
 29 * A simple function epilogue looks like this:
 30 *	mov	sp, x29
 31 *	ldp	x29, x30, [sp]
 32 *	add	sp, sp, #0x10
 33 */
 34
 35/*
 36 * Unwind from one frame record (A) to the next frame record (B).
 37 *
 38 * We terminate early if the location of B indicates a malformed chain of frame
 39 * records (e.g. a cycle), determined based on the location and fp value of A
 40 * and the location (but not the fp value) of B.
 41 */
 42int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
 43{
 
 44	unsigned long fp = frame->fp;
 45	struct stack_info info;
 46
 47	if (fp & 0xf)
 48		return -EINVAL;
 49
 50	if (!tsk)
 51		tsk = current;
 52
 53	if (!on_accessible_stack(tsk, fp, &info))
 54		return -EINVAL;
 
 
 
 
 
 
 
 55
 56	if (test_bit(info.type, frame->stacks_done))
 57		return -EINVAL;
 58
 59	/*
 60	 * As stacks grow downward, any valid record on the same stack must be
 61	 * at a strictly higher address than the prior record.
 62	 *
 63	 * Stacks can nest in several valid orders, e.g.
 64	 *
 65	 * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
 66	 * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
 67	 *
 68	 * ... but the nesting itself is strict. Once we transition from one
 69	 * stack to another, it's never valid to unwind back to that first
 70	 * stack.
 71	 */
 72	if (info.type == frame->prev_type) {
 73		if (fp <= frame->prev_fp)
 74			return -EINVAL;
 75	} else {
 76		set_bit(frame->prev_type, frame->stacks_done);
 77	}
 78
 79	/*
 80	 * Record this frame record's values and location. The prev_fp and
 81	 * prev_type are only meaningful to the next unwind_frame() invocation.
 82	 */
 83	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
 84	frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8));
 85	frame->prev_fp = fp;
 86	frame->prev_type = info.type;
 87
 88#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 89	if (tsk->ret_stack &&
 90		(ptrauth_strip_insn_pac(frame->pc) == (unsigned long)return_to_handler)) {
 91		struct ftrace_ret_stack *ret_stack;
 92		/*
 93		 * This is a case where function graph tracer has
 94		 * modified a return address (LR) in a stack frame
 95		 * to hook a function return.
 96		 * So replace it to an original value.
 97		 */
 98		ret_stack = ftrace_graph_get_ret_stack(tsk, frame->graph++);
 99		if (WARN_ON_ONCE(!ret_stack))
100			return -EINVAL;
101		frame->pc = ret_stack->ret;
102	}
103#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
104
105	frame->pc = ptrauth_strip_insn_pac(frame->pc);
106
107	/*
108	 * Frames created upon entry from EL0 have NULL FP and PC values, so
109	 * don't bother reporting these. Frames created by __noreturn functions
110	 * might have a valid FP even if PC is bogus, so only terminate where
111	 * both are NULL.
 
 
 
112	 */
113	if (!frame->fp && !frame->pc)
114		return -EINVAL;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
116	return 0;
117}
118NOKPROBE_SYMBOL(unwind_frame);
119
120void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
121		     int (*fn)(struct stackframe *, void *), void *data)
122{
123	while (1) {
124		int ret;
125
126		if (fn(frame, data))
127			break;
128		ret = unwind_frame(tsk, frame);
129		if (ret < 0)
130			break;
131	}
132}
133NOKPROBE_SYMBOL(walk_stackframe);
134
135#ifdef CONFIG_STACKTRACE
136struct stack_trace_data {
137	struct stack_trace *trace;
138	unsigned int no_sched_functions;
139	unsigned int skip;
140};
141
142static int save_trace(struct stackframe *frame, void *d)
143{
144	struct stack_trace_data *data = d;
145	struct stack_trace *trace = data->trace;
146	unsigned long addr = frame->pc;
147
148	if (data->no_sched_functions && in_sched_functions(addr))
149		return 0;
150	if (data->skip) {
151		data->skip--;
152		return 0;
153	}
154
155	trace->entries[trace->nr_entries++] = addr;
156
157	return trace->nr_entries >= trace->max_entries;
158}
159
160void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
161{
162	struct stack_trace_data data;
163	struct stackframe frame;
164
165	data.trace = trace;
166	data.skip = trace->skip;
167	data.no_sched_functions = 0;
168
169	start_backtrace(&frame, regs->regs[29], regs->pc);
170	walk_stackframe(current, &frame, save_trace, &data);
171}
172EXPORT_SYMBOL_GPL(save_stack_trace_regs);
173
174static noinline void __save_stack_trace(struct task_struct *tsk,
175	struct stack_trace *trace, unsigned int nosched)
176{
177	struct stack_trace_data data;
178	struct stackframe frame;
179
180	if (!try_get_task_stack(tsk))
181		return;
182
183	data.trace = trace;
184	data.skip = trace->skip;
185	data.no_sched_functions = nosched;
186
187	if (tsk != current) {
188		start_backtrace(&frame, thread_saved_fp(tsk),
189				thread_saved_pc(tsk));
 
 
190	} else {
191		/* We don't want this function nor the caller */
192		data.skip += 2;
193		start_backtrace(&frame,
194				(unsigned long)__builtin_frame_address(0),
195				(unsigned long)__save_stack_trace);
196	}
 
 
 
197
198	walk_stackframe(tsk, &frame, save_trace, &data);
199
200	put_task_stack(tsk);
201}
202
203void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
204{
205	__save_stack_trace(tsk, trace, 1);
206}
207EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
208
209void save_stack_trace(struct stack_trace *trace)
210{
211	__save_stack_trace(current, trace, 0);
212}
213
214EXPORT_SYMBOL_GPL(save_stack_trace);
215#endif