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/kprobes.h>
13#include <linux/types.h>
14
15struct stack_info {
16	unsigned long low;
17	unsigned long high;
18};
19
20/**
21 * struct unwind_state - state used for robust unwinding.
22 *
23 * @fp:          The fp value in the frame record (or the real fp)
24 * @pc:          The lr value in the frame record (or the real lr)
25 *
26 * @kr_cur:      When KRETPROBES is selected, holds the kretprobe instance
27 *               associated with the most recently encountered replacement lr
28 *               value.
29 *
30 * @task:        The task being unwound.
31 *
32 * @stack:       The stack currently being unwound.
33 * @stacks:      An array of stacks which can be unwound.
34 * @nr_stacks:   The number of stacks in @stacks.
35 */
36struct unwind_state {
37	unsigned long fp;
38	unsigned long pc;
39#ifdef CONFIG_KRETPROBES
40	struct llist_node *kr_cur;
41#endif
42	struct task_struct *task;
43
44	struct stack_info stack;
45	struct stack_info *stacks;
46	int nr_stacks;
47};
48
49static inline struct stack_info stackinfo_get_unknown(void)
50{
51	return (struct stack_info) {
52		.low = 0,
53		.high = 0,
54	};
55}
56
57static inline bool stackinfo_on_stack(const struct stack_info *info,
58				      unsigned long sp, unsigned long size)
59{
60	if (!info->low)
61		return false;
62
63	if (sp < info->low || sp + size < sp || sp + size > info->high)
64		return false;
65
66	return true;
67}
68
69static inline void unwind_init_common(struct unwind_state *state,
70				      struct task_struct *task)
71{
72	state->task = task;
73#ifdef CONFIG_KRETPROBES
74	state->kr_cur = NULL;
75#endif
76
77	state->stack = stackinfo_get_unknown();
78}
79
80static struct stack_info *unwind_find_next_stack(const struct unwind_state *state,
81						 unsigned long sp,
82						 unsigned long size)
83{
84	for (int i = 0; i < state->nr_stacks; i++) {
85		struct stack_info *info = &state->stacks[i];
86
87		if (stackinfo_on_stack(info, sp, size))
88			return info;
89	}
90
91	return NULL;
92}
93
94/**
95 * unwind_consume_stack() - Check if an object is on an accessible stack,
96 * updating stack boundaries so that future unwind steps cannot consume this
97 * object again.
98 *
99 * @state: the current unwind state.
100 * @sp:    the base address of the object.
101 * @size:  the size of the object.
102 *
103 * Return: 0 upon success, an error code otherwise.
104 */
105static inline int unwind_consume_stack(struct unwind_state *state,
106				       unsigned long sp,
107				       unsigned long size)
108{
109	struct stack_info *next;
110
111	if (stackinfo_on_stack(&state->stack, sp, size))
112		goto found;
113
114	next = unwind_find_next_stack(state, sp, size);
115	if (!next)
116		return -EINVAL;
117
118	/*
119	 * Stack transitions are strictly one-way, and once we've
120	 * transitioned from one stack to another, it's never valid to
121	 * unwind back to the old stack.
122	 *
123	 * Remove the current stack from the list of stacks so that it cannot
124	 * be found on a subsequent transition.
125	 *
126	 * Note that stacks can nest in several valid orders, e.g.
127	 *
128	 *   TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
129	 *   TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
130	 *   HYP -> OVERFLOW
131	 *
132	 * ... so we do not check the specific order of stack
133	 * transitions.
134	 */
135	state->stack = *next;
136	*next = stackinfo_get_unknown();
137
138found:
139	/*
140	 * Future unwind steps can only consume stack above this frame record.
141	 * Update the current stack to start immediately above it.
142	 */
143	state->stack.low = sp + size;
144	return 0;
145}
146
147/**
148 * unwind_next_frame_record() - Unwind to the next frame record.
149 *
150 * @state:        the current unwind state.
151 *
152 * Return: 0 upon success, an error code otherwise.
153 */
154static inline int
155unwind_next_frame_record(struct unwind_state *state)
156{
157	unsigned long fp = state->fp;
158	int err;
159
160	if (fp & 0x7)
161		return -EINVAL;
162
163	err = unwind_consume_stack(state, fp, 16);
164	if (err)
165		return err;
166
167	/*
168	 * Record this frame record's values.
169	 */
170	state->fp = READ_ONCE(*(unsigned long *)(fp));
171	state->pc = READ_ONCE(*(unsigned long *)(fp + 8));
172
173	return 0;
174}
175
176#endif	/* __ASM_STACKTRACE_COMMON_H */
177