16bf212c8SKalesh Singh /* SPDX-License-Identifier: GPL-2.0-only */
26bf212c8SKalesh Singh /*
36bf212c8SKalesh Singh * Common arm64 stack unwinder code.
46bf212c8SKalesh Singh *
5a4c750e2SMarc Zyngier * See: arch/arm64/kernel/stacktrace.c for the reference implementation.
6051ece67SKalesh Singh *
76bf212c8SKalesh Singh * Copyright (C) 2012 ARM Ltd.
86bf212c8SKalesh Singh */
96bf212c8SKalesh Singh #ifndef __ASM_STACKTRACE_COMMON_H
106bf212c8SKalesh Singh #define __ASM_STACKTRACE_COMMON_H
116bf212c8SKalesh Singh
126bf212c8SKalesh Singh #include <linux/types.h>
136bf212c8SKalesh Singh
146bf212c8SKalesh Singh struct stack_info {
156bf212c8SKalesh Singh unsigned long low;
166bf212c8SKalesh Singh unsigned long high;
176bf212c8SKalesh Singh };
186bf212c8SKalesh Singh
1916283c54SMark Rutland /**
2016283c54SMark Rutland * struct unwind_state - state used for robust unwinding.
216bf212c8SKalesh Singh *
226bf212c8SKalesh Singh * @fp: The fp value in the frame record (or the real fp)
236bf212c8SKalesh Singh * @pc: The lr value in the frame record (or the real lr)
246bf212c8SKalesh Singh *
258df13730SMark Rutland * @stack: The stack currently being unwound.
268df13730SMark Rutland * @stacks: An array of stacks which can be unwound.
278df13730SMark Rutland * @nr_stacks: The number of stacks in @stacks.
286bf212c8SKalesh Singh */
296bf212c8SKalesh Singh struct unwind_state {
306bf212c8SKalesh Singh unsigned long fp;
316bf212c8SKalesh Singh unsigned long pc;
328df13730SMark Rutland
338df13730SMark Rutland struct stack_info stack;
348df13730SMark Rutland struct stack_info *stacks;
358df13730SMark Rutland int nr_stacks;
366bf212c8SKalesh Singh };
376bf212c8SKalesh Singh
stackinfo_get_unknown(void)38d1f684e4SMark Rutland static inline struct stack_info stackinfo_get_unknown(void)
39d1f684e4SMark Rutland {
40d1f684e4SMark Rutland return (struct stack_info) {
41d1f684e4SMark Rutland .low = 0,
42d1f684e4SMark Rutland .high = 0,
43d1f684e4SMark Rutland };
44d1f684e4SMark Rutland }
45d1f684e4SMark Rutland
stackinfo_on_stack(const struct stack_info * info,unsigned long sp,unsigned long size)4636f9a879SMark Rutland static inline bool stackinfo_on_stack(const struct stack_info *info,
4736f9a879SMark Rutland unsigned long sp, unsigned long size)
4836f9a879SMark Rutland {
4936f9a879SMark Rutland if (!info->low)
5036f9a879SMark Rutland return false;
5136f9a879SMark Rutland
5236f9a879SMark Rutland if (sp < info->low || sp + size < sp || sp + size > info->high)
5336f9a879SMark Rutland return false;
5436f9a879SMark Rutland
5536f9a879SMark Rutland return true;
5636f9a879SMark Rutland }
5736f9a879SMark Rutland
unwind_init_common(struct unwind_state * state)581beef60eSMark Rutland static inline void unwind_init_common(struct unwind_state *state)
596bf212c8SKalesh Singh {
608df13730SMark Rutland state->stack = stackinfo_get_unknown();
616bf212c8SKalesh Singh }
626bf212c8SKalesh Singh
63*f05a4a42SMark Rutland /**
64*f05a4a42SMark Rutland * unwind_find_stack() - Find the accessible stack which entirely contains an
65*f05a4a42SMark Rutland * object.
66*f05a4a42SMark Rutland *
67*f05a4a42SMark Rutland * @state: the current unwind state.
68*f05a4a42SMark Rutland * @sp: the base address of the object.
69*f05a4a42SMark Rutland * @size: the size of the object.
70*f05a4a42SMark Rutland *
71*f05a4a42SMark Rutland * Return: a pointer to the relevant stack_info if found; NULL otherwise.
72*f05a4a42SMark Rutland */
unwind_find_stack(struct unwind_state * state,unsigned long sp,unsigned long size)73*f05a4a42SMark Rutland static struct stack_info *unwind_find_stack(struct unwind_state *state,
748df13730SMark Rutland unsigned long sp,
758df13730SMark Rutland unsigned long size)
768df13730SMark Rutland {
77*f05a4a42SMark Rutland struct stack_info *info = &state->stack;
788df13730SMark Rutland
798df13730SMark Rutland if (stackinfo_on_stack(info, sp, size))
808df13730SMark Rutland return info;
81*f05a4a42SMark Rutland
82*f05a4a42SMark Rutland for (int i = 0; i < state->nr_stacks; i++) {
83*f05a4a42SMark Rutland info = &state->stacks[i];
84*f05a4a42SMark Rutland if (stackinfo_on_stack(info, sp, size))
85*f05a4a42SMark Rutland return info;
868df13730SMark Rutland }
878df13730SMark Rutland
888df13730SMark Rutland return NULL;
898df13730SMark Rutland }
908df13730SMark Rutland
9116283c54SMark Rutland /**
92*f05a4a42SMark Rutland * unwind_consume_stack() - Update stack boundaries so that future unwind steps
93*f05a4a42SMark Rutland * cannot consume this object again.
944e00532fSMarc Zyngier *
958df13730SMark Rutland * @state: the current unwind state.
96*f05a4a42SMark Rutland * @info: the stack_info of the stack containing the object.
978df13730SMark Rutland * @sp: the base address of the object.
988df13730SMark Rutland * @size: the size of the object.
9916283c54SMark Rutland *
1008df13730SMark Rutland * Return: 0 upon success, an error code otherwise.
1014e00532fSMarc Zyngier */
unwind_consume_stack(struct unwind_state * state,struct stack_info * info,unsigned long sp,unsigned long size)102*f05a4a42SMark Rutland static inline void unwind_consume_stack(struct unwind_state *state,
103*f05a4a42SMark Rutland struct stack_info *info,
1048df13730SMark Rutland unsigned long sp,
1058df13730SMark Rutland unsigned long size)
1068df13730SMark Rutland {
107*f05a4a42SMark Rutland struct stack_info tmp;
1088df13730SMark Rutland
1098df13730SMark Rutland /*
1108df13730SMark Rutland * Stack transitions are strictly one-way, and once we've
1118df13730SMark Rutland * transitioned from one stack to another, it's never valid to
1128df13730SMark Rutland * unwind back to the old stack.
1138df13730SMark Rutland *
114*f05a4a42SMark Rutland * Destroy the old stack info so that it cannot be found upon a
115*f05a4a42SMark Rutland * subsequent transition. If the stack has not changed, we'll
116*f05a4a42SMark Rutland * immediately restore the current stack info.
1178df13730SMark Rutland *
1188df13730SMark Rutland * Note that stacks can nest in several valid orders, e.g.
1198df13730SMark Rutland *
1208df13730SMark Rutland * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
1218df13730SMark Rutland * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
1228df13730SMark Rutland * HYP -> OVERFLOW
1238df13730SMark Rutland *
1248df13730SMark Rutland * ... so we do not check the specific order of stack
1258df13730SMark Rutland * transitions.
1268df13730SMark Rutland */
127*f05a4a42SMark Rutland tmp = *info;
128*f05a4a42SMark Rutland *info = stackinfo_get_unknown();
129*f05a4a42SMark Rutland state->stack = tmp;
1308df13730SMark Rutland
1318df13730SMark Rutland /*
1328df13730SMark Rutland * Future unwind steps can only consume stack above this frame record.
1338df13730SMark Rutland * Update the current stack to start immediately above it.
1348df13730SMark Rutland */
1358df13730SMark Rutland state->stack.low = sp + size;
1368df13730SMark Rutland }
1374e00532fSMarc Zyngier
138b532ab5fSMark Rutland /**
139b532ab5fSMark Rutland * unwind_next_frame_record() - Unwind to the next frame record.
140b532ab5fSMark Rutland *
141b532ab5fSMark Rutland * @state: the current unwind state.
142b532ab5fSMark Rutland *
143b532ab5fSMark Rutland * Return: 0 upon success, an error code otherwise.
144b532ab5fSMark Rutland */
145b532ab5fSMark Rutland static inline int
unwind_next_frame_record(struct unwind_state * state)1464b5e694eSMark Rutland unwind_next_frame_record(struct unwind_state *state)
147be63c647SKalesh Singh {
148*f05a4a42SMark Rutland struct stack_info *info;
149886c2b0bSMark Rutland struct frame_record *record;
1504b5e694eSMark Rutland unsigned long fp = state->fp;
151be63c647SKalesh Singh
152be63c647SKalesh Singh if (fp & 0x7)
153be63c647SKalesh Singh return -EINVAL;
154be63c647SKalesh Singh
155*f05a4a42SMark Rutland info = unwind_find_stack(state, fp, sizeof(*record));
156*f05a4a42SMark Rutland if (!info)
157*f05a4a42SMark Rutland return -EINVAL;
158*f05a4a42SMark Rutland
159*f05a4a42SMark Rutland unwind_consume_stack(state, info, fp, sizeof(*record));
160be63c647SKalesh Singh
161be63c647SKalesh Singh /*
1628df13730SMark Rutland * Record this frame record's values.
163be63c647SKalesh Singh */
164886c2b0bSMark Rutland record = (struct frame_record *)fp;
165886c2b0bSMark Rutland state->fp = READ_ONCE(record->fp);
166886c2b0bSMark Rutland state->pc = READ_ONCE(record->lr);
167be63c647SKalesh Singh
168be63c647SKalesh Singh return 0;
169be63c647SKalesh Singh }
170f51e7146SKalesh Singh
1716bf212c8SKalesh Singh #endif /* __ASM_STACKTRACE_COMMON_H */
172