xref: /linux/arch/arm64/include/asm/stacktrace/common.h (revision 2b0cfa6e49566c8fa6759734cf821aa6e8271a9e)
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 */
160