[PATCH v2 5/6] LoongArch: Fix up the prologue unwinder unwind exception frame

From: Jinyang He
Date: Tue Feb 28 2023 - 03:04:40 EST


It is a simply way to correct the unwind info when the special functions
influence the normal prologue analysis. We find out the position where
should unwind by PT_REGS, and mark it UNW_NEED_RESET. Linkers will
collect them and the prologue unwinder will compare them to pc.

Signed-off-by: Jinyang He <hejinyang@xxxxxxxxxxx>
---
arch/loongarch/include/asm/traps.h | 13 +++++++++
arch/loongarch/include/asm/unwind.h | 2 +-
arch/loongarch/kernel/genex.S | 1 +
arch/loongarch/kernel/mcount_dyn.S | 2 ++
arch/loongarch/kernel/unwind_prologue.c | 37 +++++++++----------------
arch/loongarch/kernel/vmlinux.lds.S | 9 ++++++
6 files changed, 39 insertions(+), 25 deletions(-)

diff --git a/arch/loongarch/include/asm/traps.h b/arch/loongarch/include/asm/traps.h
index 8f276253f145..0c30a024a9e5 100644
--- a/arch/loongarch/include/asm/traps.h
+++ b/arch/loongarch/include/asm/traps.h
@@ -5,6 +5,7 @@
#ifndef _ASM_TRAPS_H
#define _ASM_TRAPS_H

+#include <asm/asm.h>
#include <asm/loongarch.h> // For EXCCODES

#ifdef __ASSEMBLY__
@@ -35,11 +36,23 @@
__VA_ARGS__; \
.popsection;

+#ifdef CONFIG_UNWINDER_PROLOGUE
+#define UNW_NEED_RESET \
+ 668: \
+ .pushsection .unw_need_reset, "a"; \
+ PTR 668b; \
+ .popsection;
+#else /* CONFIG_UNWINDER_PROLOGUE */
+#define UNW_NEED_RESET
+#endif
+
#else /* __ASSEMBLY__ */

#define VECSIZE 0x200
extern void *__ex_handlers;
extern void *__tlbr_entry;
+extern void *__unw_need_reset;
+extern void *__unw_need_reset_end;

static inline void set_eentry(void *entry)
{
diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h
index b9dce87afd2e..d9a10e264bdd 100644
--- a/arch/loongarch/include/asm/unwind.h
+++ b/arch/loongarch/include/asm/unwind.h
@@ -22,7 +22,7 @@ struct unwind_state {
char type; /* UNWINDER_XXX */
struct stack_info stack_info;
struct task_struct *task;
- bool first, error, reset;
+ bool first, error, need_reset;
int graph_idx;
unsigned long sp, pc, ra;
};
diff --git a/arch/loongarch/kernel/genex.S b/arch/loongarch/kernel/genex.S
index 256e2e5b83d4..8705a7661ce9 100644
--- a/arch/loongarch/kernel/genex.S
+++ b/arch/loongarch/kernel/genex.S
@@ -78,6 +78,7 @@ SYM_FUNC_START(handle_\exception)
move a0, sp
la_abs t0, do_\handler
jirl ra, t0, 0
+ UNW_NEED_RESET
RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_\exception)
.endm
diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S
index bbabf06244c2..3550bab52ff0 100644
--- a/arch/loongarch/kernel/mcount_dyn.S
+++ b/arch/loongarch/kernel/mcount_dyn.S
@@ -7,6 +7,7 @@
#include <asm/ftrace.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
+#include <asm/traps.h>

.text
/*
@@ -81,6 +82,7 @@ SYM_CODE_START(ftrace_common)

SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
bl ftrace_stub
+ UNW_NEED_RESET
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
nop /* b ftrace_graph_caller */
diff --git a/arch/loongarch/kernel/unwind_prologue.c b/arch/loongarch/kernel/unwind_prologue.c
index de18335c6ba6..aa01c881481c 100644
--- a/arch/loongarch/kernel/unwind_prologue.c
+++ b/arch/loongarch/kernel/unwind_prologue.c
@@ -10,33 +10,22 @@
#include <asm/loongson.h>
#include <asm/ptrace.h>
#include <asm/setup.h>
+#include <asm/traps.h>
#include <asm/unwind.h>

-static inline bool fix_exception(unsigned long pc)
-{
- return false;
-}
-
-/*
- * As we meet ftrace_regs_entry, reset first flag like first doing
- * tracing. Prologue analysis will stop soon because PC is at entry.
- */
-static inline bool fix_ftrace(unsigned long pc)
-{
-#ifdef CONFIG_DYNAMIC_FTRACE
- return pc == (unsigned long)ftrace_call + LOONGARCH_INSN_SIZE;
-#else
- return false;
-#endif
-}
-
static inline bool unwind_state_fixup(struct unwind_state *state)
{
- if (!fix_exception(state->pc) && !fix_ftrace(state->pc))
- return false;
+ unsigned long *p = (unsigned long *)&__unw_need_reset;
+ unsigned long *q = (unsigned long *)&__unw_need_reset_end;

- state->reset = true;
- return true;
+ for (; p < q; p++) {
+ if (*p != state->pc)
+ continue;
+ state->need_reset = true;
+ return true;
+ }
+
+ return false;
}

/*
@@ -59,10 +48,10 @@ static bool unwind_by_prologue(struct unwind_state *state)
if (state->sp >= info->end || state->sp < info->begin)
return false;

- if (state->reset) {
+ if (state->need_reset) {
regs = (struct pt_regs *)state->sp;
state->first = true;
- state->reset = false;
+ state->need_reset = false;
state->pc = regs->csr_era;
state->ra = regs->regs[1];
state->sp = regs->regs[3];
diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
index e99b50359900..00f1f9061961 100644
--- a/arch/loongarch/kernel/vmlinux.lds.S
+++ b/arch/loongarch/kernel/vmlinux.lds.S
@@ -70,6 +70,15 @@ SECTIONS
*(.tlbrhandler)
}

+#ifdef CONFIG_UNWINDER_PROLOGUE
+ . = ALIGN(8);
+ .unw_need_reset : {
+ __unw_need_reset = .;
+ *(.unw_need_reset)
+ __unw_need_reset_end = .;
+ }
+#endif
+
/*
* struct alt_inst entries. From the header (alternative.h):
* "Alternative instructions for different CPU types or capabilities"
--
2.34.3