summaryrefslogtreecommitdiff
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.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/drivers/gator/gator_backtrace.c b/drivers/gator/gator_backtrace.c
new file mode 100644
index 00000000000..50783d69594
--- /dev/null
+++ b/drivers/gator/gator_backtrace.c
@@ -0,0 +1,114 @@
+/**
+ * Copyright (C) ARM Limited 2010-2012. 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 {
+ unsigned long fp; // points to prev_lr
+ unsigned long lr;
+};
+
+static void arm_backtrace_eabi(int cpu, int buftype, struct pt_regs * const regs, unsigned int depth)
+{
+#if defined(__arm__)
+ struct frame_tail_eabi *tail;
+ struct frame_tail_eabi *next;
+ struct frame_tail_eabi *ptrtail;
+ struct frame_tail_eabi buftail;
+ unsigned long fp = regs->ARM_fp;
+ unsigned long sp = regs->ARM_sp;
+ unsigned long lr = regs->ARM_lr;
+ int is_user_mode = user_mode(regs);
+
+ if (!is_user_mode) {
+ return;
+ }
+
+ /* entry preamble may not have executed */
+ gator_add_trace(cpu, buftype, lr);
+
+ /* check tail is valid */
+ if (fp == 0 || fp < sp) {
+ return;
+ }
+
+ tail = (struct frame_tail_eabi *)(fp - 4);
+
+ 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;
+ ptrtail = &buftail;
+
+ lr = ptrtail[0].lr;
+ gator_add_trace(cpu, buftype, lr);
+
+ /* frame pointers should progress back up the stack, towards higher addresses */
+ next = (struct frame_tail_eabi *)(lr - 4);
+ if (tail >= next || lr == 0) {
+ fp = ptrtail[0].fp;
+ next = (struct frame_tail_eabi *)(fp - 4);
+ /* check tail is valid */
+ if (tail >= next || fp == 0) {
+ return;
+ }
+ }
+
+ tail = next;
+ }
+#endif
+}
+
+#if defined(__arm__)
+static DEFINE_PER_CPU(int, backtrace_buffer);
+static int report_trace(struct stackframe *frame, void *d)
+{
+ struct module *mod;
+ unsigned int *depth = d, addr = frame->pc, cookie = NO_COOKIE, cpu = smp_processor_id();
+
+ if (*depth) {
+ mod = __module_address(addr);
+ if (mod) {
+ cookie = get_cookie(cpu, per_cpu(backtrace_buffer, cpu), current, NULL, mod, true);
+ addr = addr - (unsigned long)mod->module_core;
+ }
+ 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, int buftype, struct pt_regs * const regs)
+{
+#if defined(__arm__)
+#ifdef GATOR_KERNEL_STACK_UNWINDING
+ int depth = gator_backtrace_depth;
+#else
+ int depth = 1;
+#endif
+ struct stackframe frame;
+ if (depth == 0)
+ depth = 1;
+ frame.fp = regs->ARM_fp;
+ frame.sp = regs->ARM_sp;
+ frame.lr = regs->ARM_lr;
+ frame.pc = regs->ARM_pc;
+ per_cpu(backtrace_buffer, cpu) = buftype;
+ walk_stackframe(&frame, report_trace, &depth);
+#else
+ marshal_backtrace(PC_REG & ~1, NO_COOKIE);
+#endif
+}