| /* | 
 |  * 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); | 
 | 	} | 
 | } | 
 |  | 
 |  |