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 <linux/uaccess.h>
  14#include <asm/compat.h>
  15#include <asm/oprofile_impl.h>
  16
  17#define STACK_SP(STACK)         *(STACK)
  18
  19#define STACK_LR64(STACK)       *((unsigned long *)(STACK) + 2)
  20#define STACK_LR32(STACK)       *((unsigned int *)(STACK) + 1)
  21
  22#ifdef CONFIG_PPC64
  23#define STACK_LR(STACK)         STACK_LR64(STACK)
  24#else
  25#define STACK_LR(STACK)         STACK_LR32(STACK)
  26#endif
  27
  28static unsigned int user_getsp32(unsigned int sp, int is_first)
  29{
  30        unsigned int stack_frame[2];
  31        void __user *p = compat_ptr(sp);
  32
  33        if (!access_ok(VERIFY_READ, p, sizeof(stack_frame)))
  34                return 0;
  35
  36        /*
  37         * The most likely reason for this is that we returned -EFAULT,
  38         * which means that we've done all that we can do from
  39         * interrupt context.
  40         */
  41        if (__copy_from_user_inatomic(stack_frame, p, sizeof(stack_frame)))
  42                return 0;
  43
  44        if (!is_first)
  45                oprofile_add_trace(STACK_LR32(stack_frame));
  46
  47        /*
  48         * We do not enforce increasing stack addresses here because
  49         * we may transition to a different stack, eg a signal handler.
  50         */
  51        return STACK_SP(stack_frame);
  52}
  53
  54#ifdef CONFIG_PPC64
  55static unsigned long user_getsp64(unsigned long sp, int is_first)
  56{
  57        unsigned long stack_frame[3];
  58
  59        if (!access_ok(VERIFY_READ, (void __user *)sp, sizeof(stack_frame)))
  60                return 0;
  61
  62        if (__copy_from_user_inatomic(stack_frame, (void __user *)sp,
  63                                        sizeof(stack_frame)))
  64                return 0;
  65
  66        if (!is_first)
  67                oprofile_add_trace(STACK_LR64(stack_frame));
  68
  69        return STACK_SP(stack_frame);
  70}
  71#endif
  72
  73static unsigned long kernel_getsp(unsigned long sp, int is_first)
  74{
  75        unsigned long *stack_frame = (unsigned long *)sp;
  76
  77        if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD))
  78                return 0;
  79
  80        if (!is_first)
  81                oprofile_add_trace(STACK_LR(stack_frame));
  82
  83        /*
  84         * We do not enforce increasing stack addresses here because
  85         * we might be transitioning from an interrupt stack to a kernel
  86         * stack. validate_sp() is designed to understand this, so just
  87         * use it.
  88         */
  89        return STACK_SP(stack_frame);
  90}
  91
  92void op_powerpc_backtrace(struct pt_regs * const regs, unsigned int depth)
  93{
  94        unsigned long sp = regs->gpr[1];
  95        int first_frame = 1;
  96
  97        /* We ditch the top stackframe so need to loop through an extra time */
  98        depth += 1;
  99
 100        if (!user_mode(regs)) {
 101                while (depth--) {
 102                        sp = kernel_getsp(sp, first_frame);
 103                        if (!sp)
 104                                break;
 105                        first_frame = 0;
 106                }
 107        } else {
 108                pagefault_disable();
 109#ifdef CONFIG_PPC64
 110                if (!is_32bit_task()) {
 111                        while (depth--) {
 112                                sp = user_getsp64(sp, first_frame);
 113                                if (!sp)
 114                                        break;
 115                                first_frame = 0;
 116                        }
 117                        pagefault_enable();
 118                        return;
 119                }
 120#endif
 121
 122                while (depth--) {
 123                        sp = user_getsp32(sp, first_frame);
 124                        if (!sp)
 125                                break;
 126                        first_frame = 0;
 127                }
 128                pagefault_enable();
 129        }
 130}
 131