mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 09:50:04 +00:00
arm64: stacktrace: Add shared header for common stack unwinding code
In order to reuse the arm64 stack unwinding logic for the nVHE hypervisor stack, move the common code to a shared header (arch/arm64/include/asm/stacktrace/common.h). The nVHE hypervisor cannot safely link against kernel code, so we make use of the shared header to avoid duplicated logic later in this series. Signed-off-by: Kalesh Singh <kaleshsingh@google.com> Reviewed-by: Mark Brown <broonie@kernel.org> Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20220726073750.3219117-2-kaleshsingh@google.com
This commit is contained in:
parent
82a592c13b
commit
6bf212c89c
3 changed files with 106 additions and 91 deletions
|
@ -8,52 +8,19 @@
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/sched/task_stack.h>
|
#include <linux/sched/task_stack.h>
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/llist.h>
|
#include <linux/llist.h>
|
||||||
|
|
||||||
#include <asm/memory.h>
|
#include <asm/memory.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <asm/sdei.h>
|
#include <asm/sdei.h>
|
||||||
|
|
||||||
enum stack_type {
|
#include <asm/stacktrace/common.h>
|
||||||
STACK_TYPE_UNKNOWN,
|
|
||||||
STACK_TYPE_TASK,
|
|
||||||
STACK_TYPE_IRQ,
|
|
||||||
STACK_TYPE_OVERFLOW,
|
|
||||||
STACK_TYPE_SDEI_NORMAL,
|
|
||||||
STACK_TYPE_SDEI_CRITICAL,
|
|
||||||
__NR_STACK_TYPES
|
|
||||||
};
|
|
||||||
|
|
||||||
struct stack_info {
|
|
||||||
unsigned long low;
|
|
||||||
unsigned long high;
|
|
||||||
enum stack_type type;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
|
extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
|
||||||
const char *loglvl);
|
const char *loglvl);
|
||||||
|
|
||||||
DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);
|
DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (!low)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (sp < low || sp + size < sp || sp + size > high)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (info) {
|
|
||||||
info->low = low;
|
|
||||||
info->high = high;
|
|
||||||
info->type = type;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool on_irq_stack(unsigned long sp, unsigned long size,
|
static inline bool on_irq_stack(unsigned long sp, unsigned long size,
|
||||||
struct stack_info *info)
|
struct stack_info *info)
|
||||||
{
|
{
|
||||||
|
|
105
arch/arm64/include/asm/stacktrace/common.h
Normal file
105
arch/arm64/include/asm/stacktrace/common.h
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/*
|
||||||
|
* Common arm64 stack unwinder code.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 ARM Ltd.
|
||||||
|
*/
|
||||||
|
#ifndef __ASM_STACKTRACE_COMMON_H
|
||||||
|
#define __ASM_STACKTRACE_COMMON_H
|
||||||
|
|
||||||
|
#include <linux/bitmap.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
enum stack_type {
|
||||||
|
STACK_TYPE_UNKNOWN,
|
||||||
|
STACK_TYPE_TASK,
|
||||||
|
STACK_TYPE_IRQ,
|
||||||
|
STACK_TYPE_OVERFLOW,
|
||||||
|
STACK_TYPE_SDEI_NORMAL,
|
||||||
|
STACK_TYPE_SDEI_CRITICAL,
|
||||||
|
__NR_STACK_TYPES
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stack_info {
|
||||||
|
unsigned long low;
|
||||||
|
unsigned long high;
|
||||||
|
enum stack_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A snapshot of a frame record or fp/lr register values, along with some
|
||||||
|
* accounting information necessary 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)
|
||||||
|
*
|
||||||
|
* @stacks_done: Stacks which have been entirely unwound, for which it is no
|
||||||
|
* longer valid to unwind to.
|
||||||
|
*
|
||||||
|
* @prev_fp: The fp that pointed to this frame record, or a synthetic value
|
||||||
|
* of 0. This is used to ensure that within a stack, each
|
||||||
|
* subsequent frame record is at an increasing address.
|
||||||
|
* @prev_type: The type of stack this frame record was on, or a synthetic
|
||||||
|
* value of STACK_TYPE_UNKNOWN. This is used to detect a
|
||||||
|
* transition from one stack to another.
|
||||||
|
*
|
||||||
|
* @kr_cur: When KRETPROBES is selected, holds the kretprobe instance
|
||||||
|
* associated with the most recently encountered replacement lr
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @task: The task being unwound.
|
||||||
|
*/
|
||||||
|
struct unwind_state {
|
||||||
|
unsigned long fp;
|
||||||
|
unsigned long pc;
|
||||||
|
DECLARE_BITMAP(stacks_done, __NR_STACK_TYPES);
|
||||||
|
unsigned long prev_fp;
|
||||||
|
enum stack_type prev_type;
|
||||||
|
#ifdef CONFIG_KRETPROBES
|
||||||
|
struct llist_node *kr_cur;
|
||||||
|
#endif
|
||||||
|
struct task_struct *task;
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (!low)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (sp < low || sp + size < sp || sp + size > high)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
info->low = low;
|
||||||
|
info->high = high;
|
||||||
|
info->type = type;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void unwind_init_common(struct unwind_state *state,
|
||||||
|
struct task_struct *task)
|
||||||
|
{
|
||||||
|
state->task = task;
|
||||||
|
#ifdef CONFIG_KRETPROBES
|
||||||
|
state->kr_cur = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prime the first unwind.
|
||||||
|
*
|
||||||
|
* In unwind_next() we'll check that the FP points to a valid stack,
|
||||||
|
* which can't be STACK_TYPE_UNKNOWN, and the first unwind will be
|
||||||
|
* treated as a transition to whichever stack that happens to be. The
|
||||||
|
* prev_fp value won't be used, but we set it to 0 such that it is
|
||||||
|
* definitely not an accessible stack address.
|
||||||
|
*/
|
||||||
|
bitmap_zero(state->stacks_done, __NR_STACK_TYPES);
|
||||||
|
state->prev_fp = 0;
|
||||||
|
state->prev_type = STACK_TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __ASM_STACKTRACE_COMMON_H */
|
|
@ -18,63 +18,6 @@
|
||||||
#include <asm/stack_pointer.h>
|
#include <asm/stack_pointer.h>
|
||||||
#include <asm/stacktrace.h>
|
#include <asm/stacktrace.h>
|
||||||
|
|
||||||
/*
|
|
||||||
* A snapshot of a frame record or fp/lr register values, along with some
|
|
||||||
* accounting information necessary 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)
|
|
||||||
*
|
|
||||||
* @stacks_done: Stacks which have been entirely unwound, for which it is no
|
|
||||||
* longer valid to unwind to.
|
|
||||||
*
|
|
||||||
* @prev_fp: The fp that pointed to this frame record, or a synthetic value
|
|
||||||
* of 0. This is used to ensure that within a stack, each
|
|
||||||
* subsequent frame record is at an increasing address.
|
|
||||||
* @prev_type: The type of stack this frame record was on, or a synthetic
|
|
||||||
* value of STACK_TYPE_UNKNOWN. This is used to detect a
|
|
||||||
* transition from one stack to another.
|
|
||||||
*
|
|
||||||
* @kr_cur: When KRETPROBES is selected, holds the kretprobe instance
|
|
||||||
* associated with the most recently encountered replacement lr
|
|
||||||
* value.
|
|
||||||
*
|
|
||||||
* @task: The task being unwound.
|
|
||||||
*/
|
|
||||||
struct unwind_state {
|
|
||||||
unsigned long fp;
|
|
||||||
unsigned long pc;
|
|
||||||
DECLARE_BITMAP(stacks_done, __NR_STACK_TYPES);
|
|
||||||
unsigned long prev_fp;
|
|
||||||
enum stack_type prev_type;
|
|
||||||
#ifdef CONFIG_KRETPROBES
|
|
||||||
struct llist_node *kr_cur;
|
|
||||||
#endif
|
|
||||||
struct task_struct *task;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void unwind_init_common(struct unwind_state *state,
|
|
||||||
struct task_struct *task)
|
|
||||||
{
|
|
||||||
state->task = task;
|
|
||||||
#ifdef CONFIG_KRETPROBES
|
|
||||||
state->kr_cur = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prime the first unwind.
|
|
||||||
*
|
|
||||||
* In unwind_next() we'll check that the FP points to a valid stack,
|
|
||||||
* which can't be STACK_TYPE_UNKNOWN, and the first unwind will be
|
|
||||||
* treated as a transition to whichever stack that happens to be. The
|
|
||||||
* prev_fp value won't be used, but we set it to 0 such that it is
|
|
||||||
* definitely not an accessible stack address.
|
|
||||||
*/
|
|
||||||
bitmap_zero(state->stacks_done, __NR_STACK_TYPES);
|
|
||||||
state->prev_fp = 0;
|
|
||||||
state->prev_type = STACK_TYPE_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start an unwind from a pt_regs.
|
* Start an unwind from a pt_regs.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue