File Index
Files and directories
Sign in
FILE

common.h

File preview with syntax highlighting.

linux-6.19.14/arch/arm64/include/asm/stacktrace/common.h C
  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
 14struct 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 */
 29struct 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
 38static inline struct stack_info stackinfo_get_unknown(void)
 39{
 40	return (struct stack_info) {
 41		.low = 0,
 42		.high = 0,
 43	};
 44}
 45
 46static 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
 58static inline void unwind_init_common(struct unwind_state *state)
 59{
 60	state->stack = stackinfo_get_unknown();
 61}
 62
 63/**
 64 * unwind_find_stack() - Find the accessible stack which entirely contains an
 65 * object.
 66 *
 67 * @state: the current unwind state.
 68 * @sp:    the base address of the object.
 69 * @size:  the size of the object.
 70 *
 71 * Return: a pointer to the relevant stack_info if found; NULL otherwise.
 72 */
 73static struct stack_info *unwind_find_stack(struct unwind_state *state,
 74					    unsigned long sp,
 75					    unsigned long size)
 76{
 77	struct stack_info *info = &state->stack;
 78
 79	if (stackinfo_on_stack(info, sp, size))
 80		return info;
 81
 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;
 86	}
 87
 88	return NULL;
 89}
 90
 91/**
 92 * unwind_consume_stack() - Update stack boundaries so that future unwind steps
 93 * cannot consume this object again.
 94 *
 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.
 99 *
100 * Return: 0 upon success, an error code otherwise.
101 */
102static inline void unwind_consume_stack(struct unwind_state *state,
103					struct stack_info *info,
104					unsigned long sp,
105					unsigned long size)
106{
107	struct stack_info tmp;
108
109	/*
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.
113	 *
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.
117	 *
118	 * Note that stacks can nest in several valid orders, e.g.
119	 *
120	 *   TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
121	 *   TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
122	 *   HYP -> OVERFLOW
123	 *
124	 * ... so we do not check the specific order of stack
125	 * transitions.
126	 */
127	tmp = *info;
128	*info = stackinfo_get_unknown();
129	state->stack = tmp;
130
131	/*
132	 * Future unwind steps can only consume stack above this frame record.
133	 * Update the current stack to start immediately above it.
134	 */
135	state->stack.low = sp + size;
136}
137
138/**
139 * unwind_next_frame_record() - Unwind to the next frame record.
140 *
141 * @state:        the current unwind state.
142 *
143 * Return: 0 upon success, an error code otherwise.
144 */
145static inline int
146unwind_next_frame_record(struct unwind_state *state)
147{
148	struct stack_info *info;
149	struct frame_record *record;
150	unsigned long fp = state->fp;
151
152	if (fp & 0x7)
153		return -EINVAL;
154
155	info = unwind_find_stack(state, fp, sizeof(*record));
156	if (!info)
157		return -EINVAL;
158
159	unwind_consume_stack(state, info, fp, sizeof(*record));
160
161	/*
162	 * Record this frame record's values.
163	 */
164	record = (struct frame_record *)fp;
165	state->fp = READ_ONCE(record->fp);
166	state->pc = READ_ONCE(record->lr);
167
168	return 0;
169}
170
171#endif	/* __ASM_STACKTRACE_COMMON_H */
~/ Read-only file preview.