blob: 62d395151abe650db40e14df52c9061c531a2bfa [file] [log] [blame]
Thomas Gleixnercaab2772019-06-03 07:44:50 +02001// SPDX-License-Identifier: GPL-2.0-only
Catalin Marinas60ffc302012-03-05 11:49:27 +00002/*
3 * Stack tracing support
4 *
5 * Copyright (C) 2012 ARM Ltd.
Catalin Marinas60ffc302012-03-05 11:49:27 +00006 */
7#include <linux/kernel.h>
8#include <linux/export.h>
AKASHI Takahiro20380bb2015-12-15 17:33:41 +09009#include <linux/ftrace.h>
Catalin Marinas60ffc302012-03-05 11:49:27 +000010#include <linux/sched.h>
Ingo Molnarb17b0152017-02-08 18:51:35 +010011#include <linux/sched/debug.h>
Ingo Molnar68db0cf2017-02-08 18:51:37 +010012#include <linux/sched/task_stack.h>
Catalin Marinas60ffc302012-03-05 11:49:27 +000013#include <linux/stacktrace.h>
14
AKASHI Takahiro132cd882015-12-04 11:02:26 +000015#include <asm/irq.h>
Mark Rutlanda9ea0012016-11-03 20:23:05 +000016#include <asm/stack_pointer.h>
Catalin Marinas60ffc302012-03-05 11:49:27 +000017#include <asm/stacktrace.h>
18
19/*
20 * AArch64 PCS assigns the frame pointer to x29.
21 *
22 * A simple function prologue looks like this:
23 * sub sp, sp, #0x10
24 * stp x29, x30, [sp]
25 * mov x29, sp
26 *
27 * A simple function epilogue looks like this:
28 * mov sp, x29
29 * ldp x29, x30, [sp]
30 * add sp, sp, #0x10
31 */
AKASHI Takahirofe13f952015-12-15 17:33:40 +090032int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
Catalin Marinas60ffc302012-03-05 11:49:27 +000033{
Catalin Marinas60ffc302012-03-05 11:49:27 +000034 unsigned long fp = frame->fp;
Ard Biesheuvelc7365332017-07-22 12:48:34 +010035
36 if (fp & 0xf)
37 return -EINVAL;
AKASHI Takahiro132cd882015-12-04 11:02:26 +000038
Mark Rutlandb5e73072016-09-23 17:55:05 +010039 if (!tsk)
40 tsk = current;
41
Laura Abbott8a1ccfb2018-07-20 14:41:53 -070042 if (!on_accessible_stack(tsk, fp, NULL))
Catalin Marinas60ffc302012-03-05 11:49:27 +000043 return -EINVAL;
44
Yang Shibcaf6692016-02-08 09:13:09 -080045 frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
46 frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8));
Catalin Marinas60ffc302012-03-05 11:49:27 +000047
AKASHI Takahiro20380bb2015-12-15 17:33:41 +090048#ifdef CONFIG_FUNCTION_GRAPH_TRACER
Mark Rutlandb5e73072016-09-23 17:55:05 +010049 if (tsk->ret_stack &&
AKASHI Takahiro20380bb2015-12-15 17:33:41 +090050 (frame->pc == (unsigned long)return_to_handler)) {
Steven Rostedt (VMware)a4482762018-12-07 13:13:28 -050051 struct ftrace_ret_stack *ret_stack;
AKASHI Takahiro20380bb2015-12-15 17:33:41 +090052 /*
53 * This is a case where function graph tracer has
54 * modified a return address (LR) in a stack frame
55 * to hook a function return.
56 * So replace it to an original value.
57 */
Steven Rostedt (VMware)a4482762018-12-07 13:13:28 -050058 ret_stack = ftrace_graph_get_ret_stack(tsk, frame->graph++);
59 if (WARN_ON_ONCE(!ret_stack))
60 return -EINVAL;
61 frame->pc = ret_stack->ret;
AKASHI Takahiro20380bb2015-12-15 17:33:41 +090062 }
63#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
64
AKASHI Takahiro132cd882015-12-04 11:02:26 +000065 /*
Ard Biesheuvel73267492017-07-22 18:45:33 +010066 * Frames created upon entry from EL0 have NULL FP and PC values, so
67 * don't bother reporting these. Frames created by __noreturn functions
68 * might have a valid FP even if PC is bogus, so only terminate where
69 * both are NULL.
AKASHI Takahiro132cd882015-12-04 11:02:26 +000070 */
Ard Biesheuvel73267492017-07-22 18:45:33 +010071 if (!frame->fp && !frame->pc)
72 return -EINVAL;
AKASHI Takahiro132cd882015-12-04 11:02:26 +000073
Catalin Marinas60ffc302012-03-05 11:49:27 +000074 return 0;
75}
76
AKASHI Takahirofe13f952015-12-15 17:33:40 +090077void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
Catalin Marinas60ffc302012-03-05 11:49:27 +000078 int (*fn)(struct stackframe *, void *), void *data)
79{
80 while (1) {
81 int ret;
82
83 if (fn(frame, data))
84 break;
AKASHI Takahirofe13f952015-12-15 17:33:40 +090085 ret = unwind_frame(tsk, frame);
Catalin Marinas60ffc302012-03-05 11:49:27 +000086 if (ret < 0)
87 break;
88 }
89}
Catalin Marinas60ffc302012-03-05 11:49:27 +000090
91#ifdef CONFIG_STACKTRACE
92struct stack_trace_data {
93 struct stack_trace *trace;
94 unsigned int no_sched_functions;
95 unsigned int skip;
96};
97
98static int save_trace(struct stackframe *frame, void *d)
99{
100 struct stack_trace_data *data = d;
101 struct stack_trace *trace = data->trace;
102 unsigned long addr = frame->pc;
103
104 if (data->no_sched_functions && in_sched_functions(addr))
105 return 0;
106 if (data->skip) {
107 data->skip--;
108 return 0;
109 }
110
111 trace->entries[trace->nr_entries++] = addr;
112
113 return trace->nr_entries >= trace->max_entries;
114}
115
Pratyush Anand98ab10e2016-09-05 08:03:16 +0530116void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
117{
118 struct stack_trace_data data;
119 struct stackframe frame;
120
121 data.trace = trace;
122 data.skip = trace->skip;
123 data.no_sched_functions = 0;
124
125 frame.fp = regs->regs[29];
Pratyush Anand98ab10e2016-09-05 08:03:16 +0530126 frame.pc = regs->pc;
127#ifdef CONFIG_FUNCTION_GRAPH_TRACER
Steven Rostedt (VMware)a4482762018-12-07 13:13:28 -0500128 frame.graph = 0;
Pratyush Anand98ab10e2016-09-05 08:03:16 +0530129#endif
130
131 walk_stackframe(current, &frame, save_trace, &data);
Pratyush Anand98ab10e2016-09-05 08:03:16 +0530132}
William Cohenc82fd1e2019-03-01 15:00:41 -0500133EXPORT_SYMBOL_GPL(save_stack_trace_regs);
Pratyush Anand98ab10e2016-09-05 08:03:16 +0530134
Prakash Guptabb53c822017-09-13 16:28:32 -0700135static noinline void __save_stack_trace(struct task_struct *tsk,
136 struct stack_trace *trace, unsigned int nosched)
Catalin Marinas60ffc302012-03-05 11:49:27 +0000137{
138 struct stack_trace_data data;
139 struct stackframe frame;
140
Mark Rutland9bbd4c52016-11-03 20:23:08 +0000141 if (!try_get_task_stack(tsk))
142 return;
143
Catalin Marinas60ffc302012-03-05 11:49:27 +0000144 data.trace = trace;
145 data.skip = trace->skip;
Prakash Guptabb53c822017-09-13 16:28:32 -0700146 data.no_sched_functions = nosched;
Catalin Marinas60ffc302012-03-05 11:49:27 +0000147
148 if (tsk != current) {
Catalin Marinas60ffc302012-03-05 11:49:27 +0000149 frame.fp = thread_saved_fp(tsk);
Catalin Marinas60ffc302012-03-05 11:49:27 +0000150 frame.pc = thread_saved_pc(tsk);
151 } else {
Prakash Guptabb53c822017-09-13 16:28:32 -0700152 /* We don't want this function nor the caller */
153 data.skip += 2;
Catalin Marinas60ffc302012-03-05 11:49:27 +0000154 frame.fp = (unsigned long)__builtin_frame_address(0);
Prakash Guptabb53c822017-09-13 16:28:32 -0700155 frame.pc = (unsigned long)__save_stack_trace;
Catalin Marinas60ffc302012-03-05 11:49:27 +0000156 }
AKASHI Takahiro20380bb2015-12-15 17:33:41 +0900157#ifdef CONFIG_FUNCTION_GRAPH_TRACER
Steven Rostedt (VMware)a4482762018-12-07 13:13:28 -0500158 frame.graph = 0;
AKASHI Takahiro20380bb2015-12-15 17:33:41 +0900159#endif
Catalin Marinas60ffc302012-03-05 11:49:27 +0000160
AKASHI Takahirofe13f952015-12-15 17:33:40 +0900161 walk_stackframe(tsk, &frame, save_trace, &data);
Mark Rutland9bbd4c52016-11-03 20:23:08 +0000162
163 put_task_stack(tsk);
Catalin Marinas60ffc302012-03-05 11:49:27 +0000164}
Dustin Browne27c7fa2017-06-13 11:40:56 -0700165EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
Catalin Marinas60ffc302012-03-05 11:49:27 +0000166
Prakash Guptabb53c822017-09-13 16:28:32 -0700167void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
168{
169 __save_stack_trace(tsk, trace, 1);
170}
171
Catalin Marinas60ffc302012-03-05 11:49:27 +0000172void save_stack_trace(struct stack_trace *trace)
173{
Prakash Guptabb53c822017-09-13 16:28:32 -0700174 __save_stack_trace(current, trace, 0);
Catalin Marinas60ffc302012-03-05 11:49:27 +0000175}
Prakash Guptabb53c822017-09-13 16:28:32 -0700176
Catalin Marinas60ffc302012-03-05 11:49:27 +0000177EXPORT_SYMBOL_GPL(save_stack_trace);
178#endif