linux/arch/microblaze/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 * `ptrace' system call
   3 *
   4 * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
   5 * Copyright (C) 2007-2009 PetaLogix
   6 * Copyright (C) 2004-2007 John Williams <john.williams@petalogix.com>
   7 *
   8 * derived from arch/v850/kernel/ptrace.c
   9 *
  10 * Copyright (C) 2002,03 NEC Electronics Corporation
  11 * Copyright (C) 2002,03 Miles Bader <miles@gnu.org>
  12 *
  13 * Derived from arch/mips/kernel/ptrace.c:
  14 *
  15 * Copyright (C) 1992 Ross Biro
  16 * Copyright (C) Linus Torvalds
  17 * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
  18 * Copyright (C) 1996 David S. Miller
  19 * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
  20 * Copyright (C) 1999 MIPS Technologies, Inc.
  21 *
  22 * This file is subject to the terms and conditions of the GNU General
  23 * Public License. See the file COPYING in the main directory of this
  24 * archive for more details.
  25 */
  26
  27#include <linux/kernel.h>
  28#include <linux/mm.h>
  29#include <linux/sched.h>
  30#include <linux/ptrace.h>
  31#include <linux/signal.h>
  32#include <linux/elf.h>
  33#include <linux/audit.h>
  34#include <linux/seccomp.h>
  35#include <linux/tracehook.h>
  36
  37#include <linux/errno.h>
  38#include <asm/processor.h>
  39#include <linux/uaccess.h>
  40#include <asm/asm-offsets.h>
  41#include <asm/cacheflush.h>
  42#include <asm/syscall.h>
  43#include <linux/io.h>
  44
  45/* Returns the address where the register at REG_OFFS in P is stashed away. */
  46static microblaze_reg_t *reg_save_addr(unsigned reg_offs,
  47                                        struct task_struct *t)
  48{
  49        struct pt_regs *regs;
  50
  51        /*
  52         * Three basic cases:
  53         *
  54         * (1)  A register normally saved before calling the scheduler, is
  55         *      available in the kernel entry pt_regs structure at the top
  56         *      of the kernel stack. The kernel trap/irq exit path takes
  57         *      care to save/restore almost all registers for ptrace'd
  58         *      processes.
  59         *
  60         * (2)  A call-clobbered register, where the process P entered the
  61         *      kernel via [syscall] trap, is not stored anywhere; that's
  62         *      OK, because such registers are not expected to be preserved
  63         *      when the trap returns anyway (so we don't actually bother to
  64         *      test for this case).
  65         *
  66         * (3)  A few registers not used at all by the kernel, and so
  67         *      normally never saved except by context-switches, are in the
  68         *      context switch state.
  69         */
  70
  71        /* Register saved during kernel entry (or not available). */
  72        regs = task_pt_regs(t);
  73
  74        return (microblaze_reg_t *)((char *)regs + reg_offs);
  75}
  76
  77long arch_ptrace(struct task_struct *child, long request,
  78                 unsigned long addr, unsigned long data)
  79{
  80        int rval;
  81        unsigned long val = 0;
  82
  83        switch (request) {
  84        /* Read/write the word at location ADDR in the registers. */
  85        case PTRACE_PEEKUSR:
  86        case PTRACE_POKEUSR:
  87                pr_debug("PEEKUSR/POKEUSR : 0x%08lx\n", addr);
  88                rval = 0;
  89                if (addr >= PT_SIZE && request == PTRACE_PEEKUSR) {
  90                        /*
  91                         * Special requests that don't actually correspond
  92                         * to offsets in struct pt_regs.
  93                         */
  94                        if (addr == PT_TEXT_ADDR) {
  95                                val = child->mm->start_code;
  96                        } else if (addr == PT_DATA_ADDR) {
  97                                val = child->mm->start_data;
  98                        } else if (addr == PT_TEXT_LEN) {
  99                                val = child->mm->end_code
 100                                        - child->mm->start_code;
 101                        } else {
 102                                rval = -EIO;
 103                        }
 104                } else if (addr < PT_SIZE && (addr & 0x3) == 0) {
 105                        microblaze_reg_t *reg_addr = reg_save_addr(addr, child);
 106                        if (request == PTRACE_PEEKUSR)
 107                                val = *reg_addr;
 108                        else {
 109#if 1
 110                                *reg_addr = data;
 111#else
 112                                /* MS potential problem on WB system
 113                                 * Be aware that reg_addr is virtual address
 114                                 * virt_to_phys conversion is necessary.
 115                                 * This could be sensible solution.
 116                                 */
 117                                u32 paddr = virt_to_phys((u32)reg_addr);
 118                                invalidate_icache_range(paddr, paddr + 4);
 119                                *reg_addr = data;
 120                                flush_dcache_range(paddr, paddr + 4);
 121#endif
 122                        }
 123                } else
 124                        rval = -EIO;
 125
 126                if (rval == 0 && request == PTRACE_PEEKUSR)
 127                        rval = put_user(val, (unsigned long __user *)data);
 128                break;
 129        default:
 130                rval = ptrace_request(child, request, addr, data);
 131        }
 132        return rval;
 133}
 134
 135asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 136{
 137        long ret = 0;
 138
 139        secure_computing_strict(regs->r12);
 140
 141        if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 142            tracehook_report_syscall_entry(regs))
 143                /*
 144                 * Tracing decided this syscall should not happen.
 145                 * We'll return a bogus call number to get an ENOSYS
 146                 * error, but leave the original number in regs->regs[0].
 147                 */
 148                ret = -1L;
 149
 150        audit_syscall_entry(EM_MICROBLAZE, regs->r12, regs->r5, regs->r6,
 151                            regs->r7, regs->r8);
 152
 153        return ret ?: regs->r12;
 154}
 155
 156asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
 157{
 158        int step;
 159
 160        audit_syscall_exit(regs);
 161
 162        step = test_thread_flag(TIF_SINGLESTEP);
 163        if (step || test_thread_flag(TIF_SYSCALL_TRACE))
 164                tracehook_report_syscall_exit(regs, step);
 165}
 166
 167void ptrace_disable(struct task_struct *child)
 168{
 169        /* nothing to do */
 170}
 171