]> Git Repo - J-linux.git/blob - arch/arm64/include/asm/stacktrace/common.h
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.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 /**
64  * unwind_find_stack() - Find the accessible stack which entirely contains an
65  * object.
66  *
67  * @state: the current unwind state.
68  * @sp:    the base address of the object.
69  * @size:  the size of the object.
70  *
71  * Return: a pointer to the relevant stack_info if found; NULL otherwise.
72  */
73 static struct stack_info *unwind_find_stack(struct unwind_state *state,
74                                             unsigned long sp,
75                                             unsigned long size)
76 {
77         struct stack_info *info = &state->stack;
78
79         if (stackinfo_on_stack(info, sp, size))
80                 return info;
81
82         for (int i = 0; i < state->nr_stacks; i++) {
83                 info = &state->stacks[i];
84                 if (stackinfo_on_stack(info, sp, size))
85                         return info;
86         }
87
88         return NULL;
89 }
90
91 /**
92  * unwind_consume_stack() - Update stack boundaries so that future unwind steps
93  * cannot consume this object again.
94  *
95  * @state: the current unwind state.
96  * @info:  the stack_info of the stack containing the object.
97  * @sp:    the base address of the object.
98  * @size:  the size of the object.
99  *
100  * Return: 0 upon success, an error code otherwise.
101  */
102 static inline void unwind_consume_stack(struct unwind_state *state,
103                                         struct stack_info *info,
104                                         unsigned long sp,
105                                         unsigned long size)
106 {
107         struct stack_info tmp;
108
109         /*
110          * Stack transitions are strictly one-way, and once we've
111          * transitioned from one stack to another, it's never valid to
112          * unwind back to the old stack.
113          *
114          * Destroy the old stack info so that it cannot be found upon a
115          * subsequent transition. If the stack has not changed, we'll
116          * immediately restore the current stack info.
117          *
118          * Note that stacks can nest in several valid orders, e.g.
119          *
120          *   TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
121          *   TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
122          *   HYP -> OVERFLOW
123          *
124          * ... so we do not check the specific order of stack
125          * transitions.
126          */
127         tmp = *info;
128         *info = stackinfo_get_unknown();
129         state->stack = tmp;
130
131         /*
132          * Future unwind steps can only consume stack above this frame record.
133          * Update the current stack to start immediately above it.
134          */
135         state->stack.low = sp + size;
136 }
137
138 /**
139  * unwind_next_frame_record() - Unwind to the next frame record.
140  *
141  * @state:        the current unwind state.
142  *
143  * Return: 0 upon success, an error code otherwise.
144  */
145 static inline int
146 unwind_next_frame_record(struct unwind_state *state)
147 {
148         struct stack_info *info;
149         struct frame_record *record;
150         unsigned long fp = state->fp;
151
152         if (fp & 0x7)
153                 return -EINVAL;
154
155         info = unwind_find_stack(state, fp, sizeof(*record));
156         if (!info)
157                 return -EINVAL;
158
159         unwind_consume_stack(state, info, fp, sizeof(*record));
160
161         /*
162          * Record this frame record's values.
163          */
164         record = (struct frame_record *)fp;
165         state->fp = READ_ONCE(record->fp);
166         state->pc = READ_ONCE(record->lr);
167
168         return 0;
169 }
170
171 #endif  /* __ASM_STACKTRACE_COMMON_H */
This page took 0.037298 seconds and 4 git commands to generate.