Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172/* SPDX-License-Identifier: GPL-2.0-only *//* * Common arm64 stack unwinder code. * * See: arch/arm64/kernel/stacktrace.c for the reference implementation. * * Copyright (C) 2012 ARM Ltd. */#ifndef __ASM_STACKTRACE_COMMON_H#define __ASM_STACKTRACE_COMMON_H
#include <linux/types.h>
struct stack_info { unsigned long low; unsigned long high;};
/** * struct unwind_state - state used for robust unwinding. * * @fp: The fp value in the frame record (or the real fp) * @pc: The lr value in the frame record (or the real lr) * * @stack: The stack currently being unwound. * @stacks: An array of stacks which can be unwound. * @nr_stacks: The number of stacks in @stacks. */struct unwind_state { unsigned long fp; unsigned long pc;
struct stack_info stack; struct stack_info *stacks; int nr_stacks;};
static inline struct stack_info stackinfo_get_unknown(void){ return (struct stack_info) { .low = 0, .high = 0, };}
static inline bool stackinfo_on_stack(const struct stack_info *info, unsigned long sp, unsigned long size){ if (!info->low) return false;
if (sp < info->low || sp + size < sp || sp + size > info->high) return false;
return true;}
static inline void unwind_init_common(struct unwind_state *state){ state->stack = stackinfo_get_unknown();}
/** * unwind_find_stack() - Find the accessible stack which entirely contains an * object. * * @state: the current unwind state. * @sp: the base address of the object. * @size: the size of the object. * * Return: a pointer to the relevant stack_info if found; NULL otherwise. */static struct stack_info *unwind_find_stack(struct unwind_state *state, unsigned long sp, unsigned long size){ struct stack_info *info = &state->stack;
if (stackinfo_on_stack(info, sp, size)) return info;
for (int i = 0; i < state->nr_stacks; i++) { info = &state->stacks[i]; if (stackinfo_on_stack(info, sp, size)) return info; }
return NULL;}
/** * unwind_consume_stack() - Update stack boundaries so that future unwind steps * cannot consume this object again. * * @state: the current unwind state. * @info: the stack_info of the stack containing the object. * @sp: the base address of the object. * @size: the size of the object. * * Return: 0 upon success, an error code otherwise. */static inline void unwind_consume_stack(struct unwind_state *state, struct stack_info *info, unsigned long sp, unsigned long size){ struct stack_info tmp;
/* * Stack transitions are strictly one-way, and once we've * transitioned from one stack to another, it's never valid to * unwind back to the old stack. * * Destroy the old stack info so that it cannot be found upon a * subsequent transition. If the stack has not changed, we'll * immediately restore the current stack info. * * Note that stacks can nest in several valid orders, e.g. * * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW * HYP -> OVERFLOW * * ... so we do not check the specific order of stack * transitions. */ tmp = *info; *info = stackinfo_get_unknown(); state->stack = tmp;
/* * Future unwind steps can only consume stack above this frame record. * Update the current stack to start immediately above it. */ state->stack.low = sp + size;}
/** * unwind_next_frame_record() - Unwind to the next frame record. * * @state: the current unwind state. * * Return: 0 upon success, an error code otherwise. */static inline intunwind_next_frame_record(struct unwind_state *state){ struct stack_info *info; struct frame_record *record; unsigned long fp = state->fp;
if (fp & 0x7) return -EINVAL;
info = unwind_find_stack(state, fp, sizeof(*record)); if (!info) return -EINVAL;
unwind_consume_stack(state, info, fp, sizeof(*record));
/* * Record this frame record's values. */ record = (struct frame_record *)fp; state->fp = READ_ONCE(record->fp); state->pc = READ_ONCE(record->lr);
return 0;}
#endif /* __ASM_STACKTRACE_COMMON_H */