| /* | |
| * AVR32 specific backtracing code for oprofile | |
| * | |
| * Copyright 2008 Weinmann GmbH | |
| * | |
| * Author: Nikolaus Voss <n.voss@weinmann.de> | |
| * | |
| * Based on i386 oprofile backtrace code by John Levon and David Smith | |
| * | |
| * 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. | |
| * | |
| */ | |
| #include <linux/oprofile.h> | |
| #include <linux/sched.h> | |
| #include <linux/uaccess.h> | |
| /* The first two words of each frame on the stack look like this if we have | |
| * frame pointers */ | |
| struct frame_head { | |
| unsigned long lr; | |
| struct frame_head *fp; | |
| }; | |
| /* copied from arch/avr32/kernel/process.c */ | |
| static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p) | |
| { | |
| return (p > (unsigned long)tinfo) | |
| && (p < (unsigned long)tinfo + THREAD_SIZE - 3); | |
| } | |
| /* copied from arch/x86/oprofile/backtrace.c */ | |
| static struct frame_head *dump_user_backtrace(struct frame_head *head) | |
| { | |
| struct frame_head bufhead[2]; | |
| /* Also check accessibility of one struct frame_head beyond */ | |
| if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | |
| return NULL; | |
| if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | |
| return NULL; | |
| oprofile_add_trace(bufhead[0].lr); | |
| /* frame pointers should strictly progress back up the stack | |
| * (towards higher addresses) */ | |
| if (bufhead[0].fp <= head) | |
| return NULL; | |
| return bufhead[0].fp; | |
| } | |
| void avr32_backtrace(struct pt_regs * const regs, unsigned int depth) | |
| { | |
| /* Get first frame pointer */ | |
| struct frame_head *head = (struct frame_head *)(regs->r7); | |
| if (!user_mode(regs)) { | |
| #ifdef CONFIG_FRAME_POINTER | |
| /* | |
| * Traverse the kernel stack from frame to frame up to | |
| * "depth" steps. | |
| */ | |
| while (depth-- && valid_stack_ptr(task_thread_info(current), | |
| (unsigned long)head)) { | |
| oprofile_add_trace(head->lr); | |
| if (head->fp <= head) | |
| break; | |
| head = head->fp; | |
| } | |
| #endif | |
| } else { | |
| /* Assume we have frame pointers in user mode process */ | |
| while (depth-- && head) | |
| head = dump_user_backtrace(head); | |
| } | |
| } | |