1/** 2 * @file backtrace.c 3 * 4 * @remark Copyright 2008 Tensilica Inc. 5 * @remark Read the file COPYING 6 * 7 */ 8 9#include <linux/oprofile.h> 10#include <linux/sched.h> 11#include <linux/mm.h> 12#include <asm/ptrace.h> 13#include <asm/uaccess.h> 14#include <asm/traps.h> 15 16/* Address of common_exception_return, used to check the 17 * transition from kernel to user space. 18 */ 19extern int common_exception_return; 20 21/* A struct that maps to the part of the frame containing the a0 and 22 * a1 registers. 23 */ 24struct frame_start { 25 unsigned long a0; 26 unsigned long a1; 27}; 28 29static void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth) 30{ 31 unsigned long windowstart = regs->windowstart; 32 unsigned long windowbase = regs->windowbase; 33 unsigned long a0 = regs->areg[0]; 34 unsigned long a1 = regs->areg[1]; 35 unsigned long pc = MAKE_PC_FROM_RA(a0, regs->pc); 36 int index; 37 38 /* First add the current PC to the trace. */ 39 if (pc != 0 && pc <= TASK_SIZE) 40 oprofile_add_trace(pc); 41 else 42 return; 43 44 /* Two steps: 45 * 46 * 1. Look through the register window for the 47 * previous PCs in the call trace. 48 * 49 * 2. Look on the stack. 50 */ 51 52 /* Step 1. */ 53 /* Rotate WINDOWSTART to move the bit corresponding to 54 * the current window to the bit #0. 55 */ 56 windowstart = (windowstart << WSBITS | windowstart) >> windowbase; 57 58 /* Look for bits that are set, they correspond to 59 * valid windows. 60 */ 61 for (index = WSBITS - 1; (index > 0) && depth; depth--, index--) 62 if (windowstart & (1 << index)) { 63 /* Read a0 and a1 from the 64 * corresponding position in AREGs. 65 */ 66 a0 = regs->areg[index * 4]; 67 a1 = regs->areg[index * 4 + 1]; 68 /* Get the PC from a0 and a1. */ 69 pc = MAKE_PC_FROM_RA(a0, pc); 70 71 /* Add the PC to the trace. */ 72 if (pc != 0 && pc <= TASK_SIZE) 73 oprofile_add_trace(pc); 74 else 75 return; 76 } 77 78 /* Step 2. */ 79 /* We are done with the register window, we need to 80 * look through the stack. 81 */ 82 if (depth > 0) { 83 /* Start from the a1 register. */ 84 /* a1 = regs->areg[1]; */ 85 while (a0 != 0 && depth--) { 86 87 struct frame_start frame_start; 88 /* Get the location for a1, a0 for the 89 * previous frame from the current a1. 90 */ 91 unsigned long *psp = (unsigned long *)a1; 92 psp -= 4; 93 94 /* Check if the region is OK to access. */ 95 if (!access_ok(VERIFY_READ, psp, sizeof(frame_start))) 96 return; 97 /* Copy a1, a0 from user space stack frame. */ 98 if (__copy_from_user_inatomic(&frame_start, psp, 99 sizeof(frame_start))) 100 return; 101 102 a0 = frame_start.a0; 103 a1 = frame_start.a1; 104 pc = MAKE_PC_FROM_RA(a0, pc); 105 106 if (pc != 0 && pc <= TASK_SIZE) 107 oprofile_add_trace(pc); 108 else 109 return; 110 } 111 } 112} 113 114static void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth) 115{ 116 unsigned long pc = regs->pc; 117 unsigned long *psp; 118 unsigned long sp_start, sp_end; 119 unsigned long a0 = regs->areg[0]; 120 unsigned long a1 = regs->areg[1]; 121 122 sp_start = a1 & ~(THREAD_SIZE-1); 123 sp_end = sp_start + THREAD_SIZE; 124 125 /* Spill the register window to the stack first. */ 126 spill_registers(); 127 128 /* Read the stack frames one by one and create the PC 129 * from the a0 and a1 registers saved there. 130 */ 131 while (a1 > sp_start && a1 < sp_end && depth--) { 132 pc = MAKE_PC_FROM_RA(a0, pc); 133 134 /* Add the PC to the trace. */ 135 oprofile_add_trace(pc); 136 if (pc == (unsigned long) &common_exception_return) { 137 regs = (struct pt_regs *)a1; 138 if (user_mode(regs)) { 139 pc = regs->pc; 140 if (pc != 0 && pc <= TASK_SIZE) 141 oprofile_add_trace(pc); 142 else 143 return; 144 return xtensa_backtrace_user(regs, depth); 145 } 146 a0 = regs->areg[0]; 147 a1 = regs->areg[1]; 148 continue; 149 } 150 151 psp = (unsigned long *)a1; 152 153 a0 = *(psp - 4); 154 a1 = *(psp - 3); 155 156 if (a1 <= (unsigned long)psp) 157 return; 158 159 } 160 return; 161} 162 163void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth) 164{ 165 if (user_mode(regs)) 166 xtensa_backtrace_user(regs, depth); 167 else 168 xtensa_backtrace_kernel(regs, depth); 169} 170