linux/arch/powerpc/oprofile/backtrace.c
<<
>>
Prefs
   1/**
   2 * Copyright (C) 2005 Brian Rogan <bcr6@cornell.edu>, IBM
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of the GNU General Public License
   6 * as published by the Free Software Foundation; either version
   7 * 2 of the License, or (at your option) any later version.
   8**/
   9
  10#include <linux/oprofile.h>
  11#include <linux/sched.h>
  12#include <asm/processor.h>
  13#include <asm/uaccess.h>
  14#include <asm/compat.h>
  15
  16#define STACK_SP(STACK)         *(STACK)
  17
  18#define STACK_LR64(STACK)       *((unsigned long *)(STACK) + 2)
  19#define STACK_LR32(STACK)       *((unsigned int *)(STACK) + 1)
  20
  21#ifdef CONFIG_PPC64
  22#define STACK_LR(STACK)         STACK_LR64(STACK)
  23#else
  24#define STACK_LR(STACK)         STACK_LR32(STACK)
  25#endif
  26
  27static unsigned int user_getsp32(unsigned int sp, int is_first)
  28{
  29        unsigned int stack_frame[2];
  30        void __user *p = compat_ptr(sp);
  31
  32        if (!access_ok(VERIFY_READ, p, sizeof(stack_frame)))
  33                return 0;
  34
  35        /*
  36         * The most likely reason for this is that we returned -EFAULT,
  37         * which means that we've done all that we can do from
  38         * interrupt context.
  39         */
  40        if (__copy_from_user_inatomic(stack_frame, p, sizeof(stack_frame)))
  41                return 0;
  42
  43        if (!is_first)
  44                oprofile_add_trace(STACK_LR32(stack_frame));
  45
  46        /*
  47         * We do not enforce increasing stack addresses here because
  48         * we may transition to a different stack, eg a signal handler.
  49         */
  50        return STACK_SP(stack_frame);
  51}
  52
  53#ifdef CONFIG_PPC64
  54static unsigned long user_getsp64(unsigned long sp, int is_first)
  55{
  56        unsigned long stack_frame[3];
  57
  58        if (!access_ok(VERIFY_READ, (void __user *)sp, sizeof(stack_frame)))
  59                return 0;
  60
  61        if (__copy_from_user_inatomic(stack_frame, (void __user *)sp,
  62                                        sizeof(stack_frame)))
  63                return 0;
  64
  65        if (!is_first)
  66                oprofile_add_trace(STACK_LR64(stack_frame));
  67
  68        return STACK_SP(stack_frame);
  69}
  70#endif
  71
  72static unsigned long kernel_getsp(unsigned long sp, int is_first)
  73{
  74        unsigned long *stack_frame = (unsigned long *)sp;
  75
  76        if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD))
  77                return 0;
  78
  79        if (!is_first)
  80                oprofile_add_trace(STACK_LR(stack_frame));
  81
  82        /*
  83         * We do not enforce increasing stack addresses here because
  84         * we might be transitioning from an interrupt stack to a kernel
  85         * stack. validate_sp() is designed to understand this, so just
  86         * use it.
  87         */
  88        return STACK_SP(stack_frame);
  89}
  90
  91void op_powerpc_backtrace(struct pt_regs * const regs, unsigned int depth)
  92{
  93        unsigned long sp = regs->gpr[1];
  94        int first_frame = 1;
  95
  96        /* We ditch the top stackframe so need to loop through an extra time */
  97        depth += 1;
  98
  99        if (!user_mode(regs)) {
 100                while (depth--) {
 101                        sp = kernel_getsp(sp, first_frame);
 102                        if (!sp)
 103                                break;
 104                        first_frame = 0;
 105                }
 106        } else {
 107#ifdef CONFIG_PPC64
 108                if (!is_32bit_task()) {
 109                        while (depth--) {
 110                                sp = user_getsp64(sp, first_frame);
 111                                if (!sp)
 112                                        break;
 113                                first_frame = 0;
 114                        }
 115
 116                        return;
 117                }
 118#endif
 119
 120                while (depth--) {
 121                        sp = user_getsp32(sp, first_frame);
 122                        if (!sp)
 123                                break;
 124                        first_frame = 0;
 125                }
 126        }
 127}
 128