Merge tag 'optee-for-6.2' of https://git.linaro.org/people/jens.wiklander/linux-tee...
[linux-2.6-microblaze.git] / arch / arm64 / include / asm / stacktrace / common.h
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Common arm64 stack unwinder code.
4  *
5  * See: arch/arm64/kernel/stacktrace.c for the reference implementation.
6  *
7  * Copyright (C) 2012 ARM Ltd.
8  */
9 #ifndef __ASM_STACKTRACE_COMMON_H
10 #define __ASM_STACKTRACE_COMMON_H
11
12 #include <linux/kprobes.h>
13 #include <linux/types.h>
14
15 struct stack_info {
16         unsigned long low;
17         unsigned long high;
18 };
19
20 /**
21  * struct unwind_state - state used for robust unwinding.
22  *
23  * @fp:          The fp value in the frame record (or the real fp)
24  * @pc:          The lr value in the frame record (or the real lr)
25  *
26  * @kr_cur:      When KRETPROBES is selected, holds the kretprobe instance
27  *               associated with the most recently encountered replacement lr
28  *               value.
29  *
30  * @task:        The task being unwound.
31  *
32  * @stack:       The stack currently being unwound.
33  * @stacks:      An array of stacks which can be unwound.
34  * @nr_stacks:   The number of stacks in @stacks.
35  */
36 struct unwind_state {
37         unsigned long fp;
38         unsigned long pc;
39 #ifdef CONFIG_KRETPROBES
40         struct llist_node *kr_cur;
41 #endif
42         struct task_struct *task;
43
44         struct stack_info stack;
45         struct stack_info *stacks;
46         int nr_stacks;
47 };
48
49 static inline struct stack_info stackinfo_get_unknown(void)
50 {
51         return (struct stack_info) {
52                 .low = 0,
53                 .high = 0,
54         };
55 }
56
57 static inline bool stackinfo_on_stack(const struct stack_info *info,
58                                       unsigned long sp, unsigned long size)
59 {
60         if (!info->low)
61                 return false;
62
63         if (sp < info->low || sp + size < sp || sp + size > info->high)
64                 return false;
65
66         return true;
67 }
68
69 static inline void unwind_init_common(struct unwind_state *state,
70                                       struct task_struct *task)
71 {
72         state->task = task;
73 #ifdef CONFIG_KRETPROBES
74         state->kr_cur = NULL;
75 #endif
76
77         state->stack = stackinfo_get_unknown();
78 }
79
80 static struct stack_info *unwind_find_next_stack(const struct unwind_state *state,
81                                                  unsigned long sp,
82                                                  unsigned long size)
83 {
84         for (int i = 0; i < state->nr_stacks; i++) {
85                 struct stack_info *info = &state->stacks[i];
86
87                 if (stackinfo_on_stack(info, sp, size))
88                         return info;
89         }
90
91         return NULL;
92 }
93
94 /**
95  * unwind_consume_stack() - Check if an object is on an accessible stack,
96  * updating stack boundaries so that future unwind steps cannot consume this
97  * object again.
98  *
99  * @state: the current unwind state.
100  * @sp:    the base address of the object.
101  * @size:  the size of the object.
102  *
103  * Return: 0 upon success, an error code otherwise.
104  */
105 static inline int unwind_consume_stack(struct unwind_state *state,
106                                        unsigned long sp,
107                                        unsigned long size)
108 {
109         struct stack_info *next;
110
111         if (stackinfo_on_stack(&state->stack, sp, size))
112                 goto found;
113
114         next = unwind_find_next_stack(state, sp, size);
115         if (!next)
116                 return -EINVAL;
117
118         /*
119          * Stack transitions are strictly one-way, and once we've
120          * transitioned from one stack to another, it's never valid to
121          * unwind back to the old stack.
122          *
123          * Remove the current stack from the list of stacks so that it cannot
124          * be found on a subsequent transition.
125          *
126          * Note that stacks can nest in several valid orders, e.g.
127          *
128          *   TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
129          *   TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
130          *   HYP -> OVERFLOW
131          *
132          * ... so we do not check the specific order of stack
133          * transitions.
134          */
135         state->stack = *next;
136         *next = stackinfo_get_unknown();
137
138 found:
139         /*
140          * Future unwind steps can only consume stack above this frame record.
141          * Update the current stack to start immediately above it.
142          */
143         state->stack.low = sp + size;
144         return 0;
145 }
146
147 /**
148  * unwind_next_frame_record() - Unwind to the next frame record.
149  *
150  * @state:        the current unwind state.
151  *
152  * Return: 0 upon success, an error code otherwise.
153  */
154 static inline int
155 unwind_next_frame_record(struct unwind_state *state)
156 {
157         unsigned long fp = state->fp;
158         int err;
159
160         if (fp & 0x7)
161                 return -EINVAL;
162
163         err = unwind_consume_stack(state, fp, 16);
164         if (err)
165                 return err;
166
167         /*
168          * Record this frame record's values.
169          */
170         state->fp = READ_ONCE(*(unsigned long *)(fp));
171         state->pc = READ_ONCE(*(unsigned long *)(fp + 8));
172
173         return 0;
174 }
175
176 #endif  /* __ASM_STACKTRACE_COMMON_H */