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/sched/task_stack.h>
  31#include <linux/ptrace.h>
  32#include <linux/signal.h>
  33#include <linux/elf.h>
  34#include <linux/audit.h>
  35#include <linux/seccomp.h>
  36#include <linux/tracehook.h>
  37
  38#include <linux/errno.h>
  39#include <asm/processor.h>
  40#include <linux/uaccess.h>
  41#include <asm/asm-offsets.h>
  42#include <asm/cacheflush.h>
  43#include <asm/syscall.h>
  44#include <linux/io.h>
  45
  46/* Returns the address where the register at REG_OFFS in P is stashed away. */
  47static microblaze_reg_t *reg_save_addr(unsigned reg_offs,
  48                                        struct task_struct *t)
  49{
  50        struct pt_regs *regs;
  51
  52        /*
  53         * Three basic cases:
  54         *
  55         * (1)  A register normally saved before calling the scheduler, is
  56         *      available in the kernel entry pt_regs structure at the top
  57         *      of the kernel stack. The kernel trap/irq exit path takes
  58         *      care to save/restore almost all registers for ptrace'd
  59         *      processes.
  60         *
  61         * (2)  A call-clobbered register, where the process P entered the
  62         *      kernel via [syscall] trap, is not stored anywhere; that's
  63         *      OK, because such registers are not expected to be preserved
  64         *      when the trap returns anyway (so we don't actually bother to
  65         *      test for this case).
  66         *
  67         * (3)  A few registers not used at all by the kernel, and so
  68         *      normally never saved except by context-switches, are in the
  69         *      context switch state.
  70         */
  71
  72        /* Register saved during kernel entry (or not available). */
  73        regs = task_pt_regs(t);
  74
  75        return (microblaze_reg_t *)((char *)regs + reg_offs);
  76}
  77
  78long arch_ptrace(struct task_struct *child, long request,
  79                 unsigned long addr, unsigned long data)
  80{
  81        int rval;
  82        unsigned long val = 0;
  83
  84        switch (request) {
  85        /* Read/write the word at location ADDR in the registers. */
  86        case PTRACE_PEEKUSR:
  87        case PTRACE_POKEUSR:
  88                pr_debug("PEEKUSR/POKEUSR : 0x%08lx\n", addr);
  89                rval = 0;
  90                if (addr >= PT_SIZE && request == PTRACE_PEEKUSR) {
  91                        /*
  92                         * Special requests that don't actually correspond
  93                         * to offsets in struct pt_regs.
  94                         */
  95                        if (addr == PT_TEXT_ADDR) {
  96                                val = child->mm->start_code;
  97                        } else if (addr == PT_DATA_ADDR) {
  98                                val = child->mm->start_data;
  99                        } else if (addr == PT_TEXT_LEN) {
 100                                val = child->mm->end_code
 101                                        - child->mm->start_code;
 102                        } else {
 103                                rval = -EIO;
 104                        }
 105                } else if (addr < PT_SIZE && (addr & 0x3) == 0) {
 106                        microblaze_reg_t *reg_addr = reg_save_addr(addr, child);
 107                        if (request == PTRACE_PEEKUSR)
 108                                val = *reg_addr;
 109                        else {
 110#if 1
 111                                *reg_addr = data;
 112#else
 113                                /* MS potential problem on WB system
 114                                 * Be aware that reg_addr is virtual address
 115                                 * virt_to_phys conversion is necessary.
 116                                 * This could be sensible solution.
 117                                 */
 118                                u32 paddr = virt_to_phys((u32)reg_addr);
 119                                invalidate_icache_range(paddr, paddr + 4);
 120                                *reg_addr = data;
 121                                flush_dcache_range(paddr, paddr + 4);
 122#endif
 123                        }
 124                } else
 125                        rval = -EIO;
 126
 127                if (rval == 0 && request == PTRACE_PEEKUSR)
 128                        rval = put_user(val, (unsigned long __user *)data);
 129                break;
 130        default:
 131                rval = ptrace_request(child, request, addr, data);
 132        }
 133        return rval;
 134}
 135
 136asmlinkage unsigned long do_syscall_trace_enter(struct pt_regs *regs)
 137{
 138        unsigned long ret = 0;
 139
 140        secure_computing_strict(regs->r12);
 141
 142        if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 143            tracehook_report_syscall_entry(regs))
 144                /*
 145                 * Tracing decided this syscall should not happen.
 146                 * We'll return a bogus call number to get an ENOSYS
 147                 * error, but leave the original number in regs->regs[0].
 148                 */
 149                ret = -1L;
 150
 151        audit_syscall_entry(regs->r12, regs->r5, regs->r6, 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