1/* 2 * Copyright (C) 2010-2013 Imagination Technologies Ltd. 3 * 4 * This file is subject to the terms and conditions of the GNU General Public 5 * License. See the file "COPYING" in the main directory of this archive 6 * for more details. 7 */ 8 9#include <linux/oprofile.h> 10#include <linux/uaccess.h> 11#include <asm/processor.h> 12#include <asm/stacktrace.h> 13 14#include "backtrace.h" 15 16static void user_backtrace_fp(unsigned long __user *fp, unsigned int depth) 17{ 18 while (depth-- && access_ok(VERIFY_READ, fp, 8)) { 19 unsigned long addr; 20 unsigned long __user *fpnew; 21 if (__copy_from_user_inatomic(&addr, fp + 1, sizeof(addr))) 22 break; 23 addr -= 4; 24 25 oprofile_add_trace(addr); 26 27 /* stack grows up, so frame pointers must decrease */ 28 if (__copy_from_user_inatomic(&fpnew, fp + 0, sizeof(fpnew))) 29 break; 30 if (fpnew >= fp) 31 break; 32 fp = fpnew; 33 } 34} 35 36static int kernel_backtrace_frame(struct stackframe *frame, void *data) 37{ 38 unsigned int *depth = data; 39 40 oprofile_add_trace(frame->pc); 41 42 /* decrement depth and stop if we reach 0 */ 43 if ((*depth)-- == 0) 44 return 1; 45 46 /* otherwise onto the next frame */ 47 return 0; 48} 49 50void metag_backtrace(struct pt_regs * const regs, unsigned int depth) 51{ 52 if (user_mode(regs)) { 53 unsigned long *fp = (unsigned long *)regs->ctx.AX[1].U0; 54 user_backtrace_fp((unsigned long __user __force *)fp, depth); 55 } else { 56 struct stackframe frame; 57 frame.fp = regs->ctx.AX[1].U0; /* A0FrP */ 58 frame.sp = user_stack_pointer(regs); /* A0StP */ 59 frame.lr = 0; /* from stack */ 60 frame.pc = regs->ctx.CurrPC; /* PC */ 61 walk_stackframe(&frame, &kernel_backtrace_frame, &depth); 62 } 63} 64