|  | /* | 
|  | * AVR32 specific backtracing code for oprofile | 
|  | * | 
|  | * Copyright 2008 Weinmann GmbH | 
|  | * | 
|  | * Author: Nikolaus Voss <[email protected]> | 
|  | * | 
|  | * 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); | 
|  | } | 
|  | } | 
|  |  | 
|  |  |