The DWARF unwinder is ready to use, so plug it into the generic
unwinding code.

Signed-off-by: Jiri Slaby <jsl...@suse.cz>
Cc: Thomas Gleixner <t...@linutronix.de>
Cc: Ingo Molnar <mi...@redhat.com>
Cc: "H. Peter Anvin" <h...@zytor.com>
Cc: x...@kernel.org
Cc: Josh Poimboeuf <jpoim...@redhat.com>
---
 arch/x86/include/asm/unwind.h  | 36 +++++++++++++++-
 arch/x86/kernel/Makefile       |  4 ++
 arch/x86/kernel/unwind_dwarf.c | 95 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+), 2 deletions(-)
 create mode 100644 arch/x86/kernel/unwind_dwarf.c

diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h
index e6676495b125..4c792ad0d9f9 100644
--- a/arch/x86/include/asm/unwind.h
+++ b/arch/x86/include/asm/unwind.h
@@ -12,7 +12,14 @@ struct unwind_state {
        struct task_struct *task;
        int graph_idx;
        bool error;
-#ifdef CONFIG_FRAME_POINTER
+#ifdef CONFIG_DWARF_UNWIND
+       union {
+               struct pt_regs regs;
+               char regs_arr[sizeof(struct pt_regs)];
+       } u;
+       unsigned long dw_sp;
+       unsigned call_frame:1;
+#elif defined(CONFIG_FRAME_POINTER)
        bool got_irq;
        unsigned long *bp, *orig_sp;
        struct pt_regs *regs;
@@ -48,7 +55,32 @@ static inline bool unwind_error(struct unwind_state *state)
        return state->error;
 }
 
-#ifdef CONFIG_FRAME_POINTER
+#ifdef CONFIG_DWARF_UNWIND
+
+#include <asm/dwarf.h>
+
+static inline
+unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
+{
+       if (unwind_done(state))
+               return NULL;
+
+       return &DW_PC(state);
+}
+
+static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
+{
+       if (unwind_done(state))
+               return NULL;
+
+       /*
+        * Should we return something? If we return the registers here, they
+        * are output for every frame.
+        */
+       return NULL;
+}
+
+#elif defined(CONFIG_FRAME_POINTER)
 
 static inline
 unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 4b994232cb57..604cbcbba9a5 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -124,11 +124,15 @@ obj-$(CONFIG_PERF_EVENTS)         += perf_regs.o
 obj-$(CONFIG_TRACING)                  += tracepoint.o
 obj-$(CONFIG_SCHED_MC_PRIO)            += itmt.o
 
+ifdef CONFIG_DWARF_UNWIND
+obj-y                                  += unwind_dwarf.o
+else
 ifdef CONFIG_FRAME_POINTER
 obj-y                                  += unwind_frame.o
 else
 obj-y                                  += unwind_guess.o
 endif
+endif
 
 ###
 # 64 bit specific files
diff --git a/arch/x86/kernel/unwind_dwarf.c b/arch/x86/kernel/unwind_dwarf.c
new file mode 100644
index 000000000000..bbcca970aca8
--- /dev/null
+++ b/arch/x86/kernel/unwind_dwarf.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016-2017 SUSE
+ *      Jiri Slaby <jirisl...@kernel.org>
+ * This code is released under the GPL v2.
+ */
+
+#include <linux/dwarf.h>
+
+unsigned long unwind_get_return_address(struct unwind_state *state)
+{
+       unsigned long *addr = unwind_get_return_address_ptr(state);
+
+       if (unwind_done(state))
+               return 0;
+
+       return ftrace_graph_ret_addr(state->task, &state->graph_idx, *addr,
+                       addr);
+}
+EXPORT_SYMBOL_GPL(unwind_get_return_address);
+
+bool unwind_next_frame(struct unwind_state *state)
+{
+       if (unwind_done(state))
+               goto bad;
+
+       if (arch_dwarf_user_mode(state))
+               goto bad;
+
+       if ((state->dw_sp & PAGE_MASK) == (DW_SP(state) & PAGE_MASK) &&
+                       state->dw_sp > DW_SP(state))
+               goto bad;
+
+       if (dwarf_unwind(state) || !DW_PC(state))
+               goto bad;
+
+       state->dw_sp = DW_SP(state);
+
+       return true;
+bad:
+       state->stack_info.type = STACK_TYPE_UNKNOWN;
+       return false;
+}
+EXPORT_SYMBOL_GPL(unwind_next_frame);
+
+void __unwind_start(struct unwind_state *state, struct task_struct *task,
+               struct pt_regs *regs, unsigned long *first_frame)
+{
+       bool do_skipping = true;
+       char type;
+
+       memset(state, 0, sizeof(*state));
+       state->task = task;
+
+       if (regs) {
+               arch_dwarf_init_frame_info(state, regs);
+               type = 'R';
+       } else if (task == current) {
+               arch_dwarf_init_running(&state->u.regs);
+               type = 'C';
+#ifdef CONFIG_SMP
+       } else if (task->on_cpu) {
+               return;
+#endif
+       } else {
+               arch_dwarf_init_blocked(state);
+               type = 'B';
+               do_skipping = false;
+       }
+
+       pr_debug("%s: %c FF=%p rip=%lx (%pS) rsp=%lx rbp=%lx\n",
+                       __func__, type, first_frame, DW_PC(state),
+                       (void *)DW_PC(state), DW_SP(state), DW_FP(state));
+
+       get_stack_info((void *)DW_SP(state), task, &state->stack_info,
+                       &state->stack_mask);
+
+       state->dw_sp = DW_SP(state);
+
+       if (arch_dwarf_user_mode(state))
+               return;
+
+       while (do_skipping) {
+               if (DW_SP(state) > (unsigned long)first_frame) {
+                       pr_debug("%s: hit first=%p sp=%lx\n", __func__,
+                                       first_frame, DW_SP(state));
+                       break;
+               }
+               if (!unwind_next_frame(state))
+                       break;
+               pr_debug("%s: skipped to %pS rsp=%lx rbp=%lx\n", __func__,
+                               (void *)DW_PC(state), DW_SP(state),
+                               DW_FP(state));
+       }
+}
+EXPORT_SYMBOL_GPL(__unwind_start);
-- 
2.12.2

Reply via email to