Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / arch / arm64 / include / asm / stacktrace / common.h
blob821a8fdd31af470091ec6cc0691afd28f7268eb3
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3 * Common arm64 stack unwinder code.
5 * See: arch/arm64/kernel/stacktrace.c for the reference implementation.
7 * Copyright (C) 2012 ARM Ltd.
8 */
9 #ifndef __ASM_STACKTRACE_COMMON_H
10 #define __ASM_STACKTRACE_COMMON_H
12 #include <linux/types.h>
14 struct stack_info {
15 unsigned long low;
16 unsigned long high;
19 /**
20 * struct unwind_state - state used for robust unwinding.
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)
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.
29 struct unwind_state {
30 unsigned long fp;
31 unsigned long pc;
33 struct stack_info stack;
34 struct stack_info *stacks;
35 int nr_stacks;
38 static inline struct stack_info stackinfo_get_unknown(void)
40 return (struct stack_info) {
41 .low = 0,
42 .high = 0,
46 static inline bool stackinfo_on_stack(const struct stack_info *info,
47 unsigned long sp, unsigned long size)
49 if (!info->low)
50 return false;
52 if (sp < info->low || sp + size < sp || sp + size > info->high)
53 return false;
55 return true;
58 static inline void unwind_init_common(struct unwind_state *state)
60 state->stack = stackinfo_get_unknown();
63 /**
64 * unwind_find_stack() - Find the accessible stack which entirely contains an
65 * object.
67 * @state: the current unwind state.
68 * @sp: the base address of the object.
69 * @size: the size of the object.
71 * Return: a pointer to the relevant stack_info if found; NULL otherwise.
73 static struct stack_info *unwind_find_stack(struct unwind_state *state,
74 unsigned long sp,
75 unsigned long size)
77 struct stack_info *info = &state->stack;
79 if (stackinfo_on_stack(info, sp, size))
80 return info;
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;
88 return NULL;
91 /**
92 * unwind_consume_stack() - Update stack boundaries so that future unwind steps
93 * cannot consume this object again.
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.
100 * Return: 0 upon success, an error code otherwise.
102 static inline void unwind_consume_stack(struct unwind_state *state,
103 struct stack_info *info,
104 unsigned long sp,
105 unsigned long size)
107 struct stack_info tmp;
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.
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.
118 * Note that stacks can nest in several valid orders, e.g.
120 * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
121 * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
122 * HYP -> OVERFLOW
124 * ... so we do not check the specific order of stack
125 * transitions.
127 tmp = *info;
128 *info = stackinfo_get_unknown();
129 state->stack = tmp;
132 * Future unwind steps can only consume stack above this frame record.
133 * Update the current stack to start immediately above it.
135 state->stack.low = sp + size;
139 * unwind_next_frame_record() - Unwind to the next frame record.
141 * @state: the current unwind state.
143 * Return: 0 upon success, an error code otherwise.
145 static inline int
146 unwind_next_frame_record(struct unwind_state *state)
148 struct stack_info *info;
149 struct frame_record *record;
150 unsigned long fp = state->fp;
152 if (fp & 0x7)
153 return -EINVAL;
155 info = unwind_find_stack(state, fp, sizeof(*record));
156 if (!info)
157 return -EINVAL;
159 unwind_consume_stack(state, info, fp, sizeof(*record));
162 * Record this frame record's values.
164 record = (struct frame_record *)fp;
165 state->fp = READ_ONCE(record->fp);
166 state->pc = READ_ONCE(record->lr);
168 return 0;
171 #endif /* __ASM_STACKTRACE_COMMON_H */