aboutsummaryrefslogtreecommitdiff
path: root/drivers/gator/gator_backtrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gator/gator_backtrace.c')
-rw-r--r--drivers/gator/gator_backtrace.c139
1 files changed, 139 insertions, 0 deletions
diff --git a/drivers/gator/gator_backtrace.c b/drivers/gator/gator_backtrace.c
new file mode 100644
index 000000000000..6bd8ea2308c7
--- /dev/null
+++ b/drivers/gator/gator_backtrace.c
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*
+ * EABI backtrace stores {fp,lr} on the stack.
+ */
+struct frame_tail_eabi {
+ union {
+ struct {
+ unsigned long fp; // points to prev_lr
+ unsigned long lr;
+ };
+ // Used to read 32 bit fp/lr from a 64 bit kernel
+ struct {
+ u32 fp_32;
+ u32 lr_32;
+ };
+ };
+};
+
+static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth)
+{
+#if defined(__arm__) || defined(__aarch64__)
+ struct frame_tail_eabi *tail;
+ struct frame_tail_eabi *next;
+ struct frame_tail_eabi buftail;
+#if defined(__arm__)
+ const bool is_compat = false;
+ unsigned long fp = regs->ARM_fp;
+ unsigned long sp = regs->ARM_sp;
+ unsigned long lr = regs->ARM_lr;
+ const int frame_offset = 4;
+#else
+ // Is userspace aarch32 (32 bit)
+ const bool is_compat = compat_user_mode(regs);
+ unsigned long fp = (is_compat ? regs->regs[11] : regs->regs[29]);
+ unsigned long sp = (is_compat ? regs->compat_sp : regs->sp);
+ unsigned long lr = (is_compat ? regs->compat_lr : regs->regs[30]);
+ const int frame_offset = (is_compat ? 4 : 0);
+#endif
+ int is_user_mode = user_mode(regs);
+
+ if (!is_user_mode) {
+ return;
+ }
+
+ /* entry preamble may not have executed */
+ gator_add_trace(cpu, lr);
+
+ /* check tail is valid */
+ if (fp == 0 || fp < sp) {
+ return;
+ }
+
+ tail = (struct frame_tail_eabi *)(fp - frame_offset);
+
+ while (depth-- && tail && !((unsigned long)tail & 3)) {
+ /* Also check accessibility of one struct frame_tail beyond */
+ if (!access_ok(VERIFY_READ, tail, sizeof(struct frame_tail_eabi)))
+ return;
+ if (__copy_from_user_inatomic(&buftail, tail, sizeof(struct frame_tail_eabi)))
+ return;
+
+ lr = (is_compat ? buftail.lr_32 : buftail.lr);
+ gator_add_trace(cpu, lr);
+
+ /* frame pointers should progress back up the stack, towards higher addresses */
+ next = (struct frame_tail_eabi *)(lr - frame_offset);
+ if (tail >= next || lr == 0) {
+ fp = (is_compat ? buftail.fp_32 : buftail.fp);
+ next = (struct frame_tail_eabi *)(fp - frame_offset);
+ /* check tail is valid */
+ if (tail >= next || fp == 0) {
+ return;
+ }
+ }
+
+ tail = next;
+ }
+#endif
+}
+
+#if defined(__arm__) || defined(__aarch64__)
+static int report_trace(struct stackframe *frame, void *d)
+{
+ unsigned int *depth = d, cookie = NO_COOKIE;
+ unsigned long addr = frame->pc;
+
+ if (*depth) {
+#if defined(MODULE)
+ unsigned int cpu = get_physical_cpu();
+ struct module *mod = __module_address(addr);
+ if (mod) {
+ cookie = get_cookie(cpu, current, mod->name, false);
+ addr = addr - (unsigned long)mod->module_core;
+ }
+#endif
+ marshal_backtrace(addr & ~1, cookie);
+ (*depth)--;
+ }
+
+ return *depth == 0;
+}
+#endif
+
+// Uncomment the following line to enable kernel stack unwinding within gator, note it can also be defined from the Makefile
+// #define GATOR_KERNEL_STACK_UNWINDING
+static void kernel_backtrace(int cpu, struct pt_regs *const regs)
+{
+#if defined(__arm__) || defined(__aarch64__)
+#ifdef GATOR_KERNEL_STACK_UNWINDING
+ int depth = gator_backtrace_depth;
+#else
+ int depth = 1;
+#endif
+ struct stackframe frame;
+ if (depth == 0)
+ depth = 1;
+#if defined(__arm__)
+ frame.fp = regs->ARM_fp;
+ frame.sp = regs->ARM_sp;
+ frame.lr = regs->ARM_lr;
+ frame.pc = regs->ARM_pc;
+#else
+ frame.fp = regs->regs[29];
+ frame.sp = regs->sp;
+ frame.pc = regs->pc;
+#endif
+ walk_stackframe(&frame, report_trace, &depth);
+#else
+ marshal_backtrace(PC_REG & ~1, NO_COOKIE);
+#endif
+}