Merge branch 'for-next/stacktrace' into for-next/core
[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/types.h>
13
14 struct stack_info {
15         unsigned long low;
16         unsigned long high;
17 };
18
19 /**
20  * struct unwind_state - state used for robust unwinding.
21  *
22  * @fp:          The fp value in the frame record (or the real fp)
23  * @pc:          The lr value in the frame record (or the real lr)
24  *
25  * @stack:       The stack currently being unwound.
26  * @stacks:      An array of stacks which can be unwound.
27  * @nr_stacks:   The number of stacks in @stacks.
28  */
29 struct unwind_state {
30         unsigned long fp;
31         unsigned long pc;
32
33         struct stack_info stack;
34         struct stack_info *stacks;
35         int nr_stacks;
36 };
37
38 static inline struct stack_info stackinfo_get_unknown(void)
39 {
40         return (struct stack_info) {
41                 .low = 0,
42                 .high = 0,
43         };
44 }
45
46 static inline bool stackinfo_on_stack(const struct stack_info *info,
47                                       unsigned long sp, unsigned long size)
48 {
49         if (!info->low)
50                 return false;
51
52         if (sp < info->low || sp + size < sp || sp + size > info->high)
53                 return false;
54
55         return true;
56 }
57
58 static inline void unwind_init_common(struct unwind_state *state)
59 {
60         state->stack = stackinfo_get_unknown();
61 }
62
63 static struct stack_info *unwind_find_next_stack(const struct unwind_state *state,
64                                                  unsigned long sp,
65                                                  unsigned long size)
66 {
67         for (int i = 0; i < state->nr_stacks; i++) {
68                 struct stack_info *info = &state->stacks[i];
69
70                 if (stackinfo_on_stack(info, sp, size))
71                         return info;
72         }
73
74         return NULL;
75 }
76
77 /**
78  * unwind_consume_stack() - Check if an object is on an accessible stack,
79  * updating stack boundaries so that future unwind steps cannot consume this
80  * object again.
81  *
82  * @state: the current unwind state.
83  * @sp:    the base address of the object.
84  * @size:  the size of the object.
85  *
86  * Return: 0 upon success, an error code otherwise.
87  */
88 static inline int unwind_consume_stack(struct unwind_state *state,
89                                        unsigned long sp,
90                                        unsigned long size)
91 {
92         struct stack_info *next;
93
94         if (stackinfo_on_stack(&state->stack, sp, size))
95                 goto found;
96
97         next = unwind_find_next_stack(state, sp, size);
98         if (!next)
99                 return -EINVAL;
100
101         /*
102          * Stack transitions are strictly one-way, and once we've
103          * transitioned from one stack to another, it's never valid to
104          * unwind back to the old stack.
105          *
106          * Remove the current stack from the list of stacks so that it cannot
107          * be found on a subsequent transition.
108          *
109          * Note that stacks can nest in several valid orders, e.g.
110          *
111          *   TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
112          *   TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
113          *   HYP -> OVERFLOW
114          *
115          * ... so we do not check the specific order of stack
116          * transitions.
117          */
118         state->stack = *next;
119         *next = stackinfo_get_unknown();
120
121 found:
122         /*
123          * Future unwind steps can only consume stack above this frame record.
124          * Update the current stack to start immediately above it.
125          */
126         state->stack.low = sp + size;
127         return 0;
128 }
129
130 /**
131  * unwind_next_frame_record() - Unwind to the next frame record.
132  *
133  * @state:        the current unwind state.
134  *
135  * Return: 0 upon success, an error code otherwise.
136  */
137 static inline int
138 unwind_next_frame_record(struct unwind_state *state)
139 {
140         unsigned long fp = state->fp;
141         int err;
142
143         if (fp & 0x7)
144                 return -EINVAL;
145
146         err = unwind_consume_stack(state, fp, 16);
147         if (err)
148                 return err;
149
150         /*
151          * Record this frame record's values.
152          */
153         state->fp = READ_ONCE(*(unsigned long *)(fp));
154         state->pc = READ_ONCE(*(unsigned long *)(fp + 8));
155
156         return 0;
157 }
158
159 #endif  /* __ASM_STACKTRACE_COMMON_H */