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
stackinfo_get_unknown(void)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
stackinfo_on_stack(const struct stack_info * info,unsigned long sp,unsigned long size)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
unwind_init_common(struct unwind_state * state)58 static inline void unwind_init_common(struct unwind_state *state)
59 {
60 state->stack = stackinfo_get_unknown();
61 }
62
unwind_find_next_stack(const struct unwind_state * state,unsigned long sp,unsigned long size)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 */
unwind_consume_stack(struct unwind_state * state,unsigned long sp,unsigned long size)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
unwind_next_frame_record(struct unwind_state * state)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 */
160