|
@@ -0,0 +1,105 @@
|
|
|
+/*
|
|
|
+ * OpenRISC unwinder.c
|
|
|
+ *
|
|
|
+ * Reusable arch specific api for unwinding stacks.
|
|
|
+ *
|
|
|
+ * Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
|
|
|
+ *
|
|
|
+ * This file is licensed under the terms of the GNU General Public License
|
|
|
+ * version 2. This program is licensed "as is" without any warranty of any
|
|
|
+ * kind, whether express or implied.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <linux/sched/task_stack.h>
|
|
|
+#include <linux/kernel.h>
|
|
|
+
|
|
|
+#include <asm/unwinder.h>
|
|
|
+
|
|
|
+#ifdef CONFIG_FRAME_POINTER
|
|
|
+struct or1k_frameinfo {
|
|
|
+ unsigned long *fp;
|
|
|
+ unsigned long ra;
|
|
|
+ unsigned long top;
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Verify a frameinfo structure. The return address should be a valid text
|
|
|
+ * address. The frame pointer may be null if its the last frame, otherwise
|
|
|
+ * the frame pointer should point to a location in the stack after the the
|
|
|
+ * top of the next frame up.
|
|
|
+ */
|
|
|
+static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo)
|
|
|
+{
|
|
|
+ return (frameinfo->fp == NULL ||
|
|
|
+ (!kstack_end(frameinfo->fp) &&
|
|
|
+ frameinfo->fp > &frameinfo->top)) &&
|
|
|
+ __kernel_text_address(frameinfo->ra);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Create a stack trace doing scanning which is frame pointer aware. We can
|
|
|
+ * get reliable stack traces by matching the previously found frame
|
|
|
+ * pointer with the top of the stack address every time we find a valid
|
|
|
+ * or1k_frameinfo.
|
|
|
+ *
|
|
|
+ * Ideally the stack parameter will be passed as FP, but it can not be
|
|
|
+ * guaranteed. Therefore we scan each address looking for the first sign
|
|
|
+ * of a return address.
|
|
|
+ *
|
|
|
+ * The OpenRISC stack frame looks something like the following. The
|
|
|
+ * location SP is held in r1 and location FP is held in r2 when frame pointers
|
|
|
+ * enabled.
|
|
|
+ *
|
|
|
+ * SP -> (top of stack)
|
|
|
+ * - (callee saved registers)
|
|
|
+ * - (local variables)
|
|
|
+ * FP-8 -> previous FP \
|
|
|
+ * FP-4 -> return address |- or1k_frameinfo
|
|
|
+ * FP -> (previous top of stack) /
|
|
|
+ */
|
|
|
+void unwind_stack(void *data, unsigned long *stack,
|
|
|
+ void (*trace)(void *data, unsigned long addr, int reliable))
|
|
|
+{
|
|
|
+ unsigned long *next_fp = NULL;
|
|
|
+ struct or1k_frameinfo *frameinfo = NULL;
|
|
|
+ int reliable = 0;
|
|
|
+
|
|
|
+ while (!kstack_end(stack)) {
|
|
|
+ frameinfo = container_of(stack,
|
|
|
+ struct or1k_frameinfo,
|
|
|
+ top);
|
|
|
+
|
|
|
+ if (__kernel_text_address(frameinfo->ra)) {
|
|
|
+ if (or1k_frameinfo_valid(frameinfo) &&
|
|
|
+ (next_fp == NULL ||
|
|
|
+ next_fp == &frameinfo->top)) {
|
|
|
+ reliable = 1;
|
|
|
+ next_fp = frameinfo->fp;
|
|
|
+ } else
|
|
|
+ reliable = 0;
|
|
|
+
|
|
|
+ trace(data, frameinfo->ra, reliable);
|
|
|
+ }
|
|
|
+ stack++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#else /* CONFIG_FRAME_POINTER */
|
|
|
+
|
|
|
+/*
|
|
|
+ * Create a stack trace by doing a simple scan treating all text addresses
|
|
|
+ * as return addresses.
|
|
|
+ */
|
|
|
+void unwind_stack(void *data, unsigned long *stack,
|
|
|
+ void (*trace)(void *data, unsigned long addr, int reliable))
|
|
|
+{
|
|
|
+ unsigned long addr;
|
|
|
+
|
|
|
+ while (!kstack_end(stack)) {
|
|
|
+ addr = *stack++;
|
|
|
+ if (__kernel_text_address(addr))
|
|
|
+ trace(data, addr, 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+#endif /* CONFIG_FRAME_POINTER */
|
|
|
+
|