arm64: stacktrace: Factor out unwind_next_common()
Move common unwind_next logic to stacktrace/common.h. This allows reusing the code in the implementation the nVHE hypervisor stack unwinder, later in this series. Signed-off-by: Kalesh Singh <kaleshsingh@google.com> Reviewed-by: Fuad Tabba <tabba@google.com> Reviewed-by: Mark Brown <broonie@kernel.org> Tested-by: Fuad Tabba <tabba@google.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20220726073750.3219117-4-kaleshsingh@google.com
This commit is contained in:
parent
15a59f19a0
commit
be63c647fd
|
@ -65,6 +65,10 @@ struct unwind_state {
|
|||
static inline bool on_overflow_stack(unsigned long sp, unsigned long size,
|
||||
struct stack_info *info);
|
||||
|
||||
static inline bool on_accessible_stack(const struct task_struct *tsk,
|
||||
unsigned long sp, unsigned long size,
|
||||
struct stack_info *info);
|
||||
|
||||
static inline bool on_stack(unsigned long sp, unsigned long size,
|
||||
unsigned long low, unsigned long high,
|
||||
enum stack_type type, struct stack_info *info)
|
||||
|
@ -120,4 +124,50 @@ static inline void unwind_init_common(struct unwind_state *state,
|
|||
state->prev_type = STACK_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline int unwind_next_common(struct unwind_state *state,
|
||||
struct stack_info *info)
|
||||
{
|
||||
struct task_struct *tsk = state->task;
|
||||
unsigned long fp = state->fp;
|
||||
|
||||
if (fp & 0x7)
|
||||
return -EINVAL;
|
||||
|
||||
if (!on_accessible_stack(tsk, fp, 16, info))
|
||||
return -EINVAL;
|
||||
|
||||
if (test_bit(info->type, state->stacks_done))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* As stacks grow downward, any valid record on the same stack must be
|
||||
* at a strictly higher address than the prior record.
|
||||
*
|
||||
* Stacks can nest in several valid orders, e.g.
|
||||
*
|
||||
* TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
|
||||
* TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
|
||||
*
|
||||
* ... but the nesting itself is strict. Once we transition from one
|
||||
* stack to another, it's never valid to unwind back to that first
|
||||
* stack.
|
||||
*/
|
||||
if (info->type == state->prev_type) {
|
||||
if (fp <= state->prev_fp)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
__set_bit(state->prev_type, state->stacks_done);
|
||||
}
|
||||
|
||||
/*
|
||||
* Record this frame record's values and location. The prev_fp and
|
||||
* prev_type are only meaningful to the next unwind_next() invocation.
|
||||
*/
|
||||
state->fp = READ_ONCE(*(unsigned long *)(fp));
|
||||
state->pc = READ_ONCE(*(unsigned long *)(fp + 8));
|
||||
state->prev_fp = fp;
|
||||
state->prev_type = info->type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* __ASM_STACKTRACE_COMMON_H */
|
||||
|
|
|
@ -81,48 +81,15 @@ static int notrace unwind_next(struct unwind_state *state)
|
|||
struct task_struct *tsk = state->task;
|
||||
unsigned long fp = state->fp;
|
||||
struct stack_info info;
|
||||
int err;
|
||||
|
||||
/* Final frame; nothing to unwind */
|
||||
if (fp == (unsigned long)task_pt_regs(tsk)->stackframe)
|
||||
return -ENOENT;
|
||||
|
||||
if (fp & 0x7)
|
||||
return -EINVAL;
|
||||
|
||||
if (!on_accessible_stack(tsk, fp, 16, &info))
|
||||
return -EINVAL;
|
||||
|
||||
if (test_bit(info.type, state->stacks_done))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* As stacks grow downward, any valid record on the same stack must be
|
||||
* at a strictly higher address than the prior record.
|
||||
*
|
||||
* Stacks can nest in several valid orders, e.g.
|
||||
*
|
||||
* TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
|
||||
* TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
|
||||
*
|
||||
* ... but the nesting itself is strict. Once we transition from one
|
||||
* stack to another, it's never valid to unwind back to that first
|
||||
* stack.
|
||||
*/
|
||||
if (info.type == state->prev_type) {
|
||||
if (fp <= state->prev_fp)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
__set_bit(state->prev_type, state->stacks_done);
|
||||
}
|
||||
|
||||
/*
|
||||
* Record this frame record's values and location. The prev_fp and
|
||||
* prev_type are only meaningful to the next unwind_next() invocation.
|
||||
*/
|
||||
state->fp = READ_ONCE(*(unsigned long *)(fp));
|
||||
state->pc = READ_ONCE(*(unsigned long *)(fp + 8));
|
||||
state->prev_fp = fp;
|
||||
state->prev_type = info.type;
|
||||
err = unwind_next_common(state, &info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
state->pc = ptrauth_strip_insn_pac(state->pc);
|
||||
|
||||
|
|
Loading…
Reference in New Issue