linux/arch/xtensa/oprofile/backtrace.c
<<
>>
Prefs
   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                if (kernel_text_address(pc))
 136                        oprofile_add_trace(pc);
 137
 138                if (pc == (unsigned long) &common_exception_return) {
 139                        regs = (struct pt_regs *)a1;
 140                        if (user_mode(regs)) {
 141                                pc = regs->pc;
 142                                if (pc != 0 && pc <= TASK_SIZE)
 143                                        oprofile_add_trace(pc);
 144                                else
 145                                        return;
 146                                return xtensa_backtrace_user(regs, depth);
 147                        }
 148                        a0 = regs->areg[0];
 149                        a1 = regs->areg[1];
 150                        continue;
 151                }
 152
 153                psp = (unsigned long *)a1;
 154
 155                a0 = *(psp - 4);
 156                a1 = *(psp - 3);
 157
 158                if (a1 <= (unsigned long)psp)
 159                        return;
 160
 161        }
 162        return;
 163}
 164
 165void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth)
 166{
 167        if (user_mode(regs))
 168                xtensa_backtrace_user(regs, depth);
 169        else
 170                xtensa_backtrace_kernel(regs, depth);
 171}
 172