/*
* arch/s390/kernel/ptrace.c
*
* S390 version
* Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*
* Based on PowerPC version
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* Derived from "arch/m68k/kernel/ptrace.c"
* Copyright (C) 1994 by Hamish Macdonald
* Taken from linux/kernel/ptrace.c and modified for M680x0.
* linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
*
* Modified by Cort Dougan (cort@cs.nmt.edu)
*
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file README.legal in the main directory of
* this archive for more details.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
#include <linux/signal.h>
#include <linux/elf.h>
#include <linux/regset.h>
#include <linux/tracehook.h>
#include <linux/seccomp.h>
#include <trace/syscall.h>
#include <asm/compat.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include "entry.h"
#ifdef CONFIG_COMPAT
#include "compat_ptrace.h"
#endif
#define CREATE_TRACE_POINTS
#include <trace/events/syscalls.h>
enum s390_regset {
REGSET_GENERAL,
REGSET_FP,
REGSET_LAST_BREAK,
REGSET_GENERAL_EXTENDED,
};
static void
FixPerRegisters(struct task_struct *task)
{
struct pt_regs *regs;
per_struct *per_info;
per_cr_words cr_words;
regs = task_pt_regs(task);
per_info = (per_struct *) &task->thread.per_info;
per_info->control_regs.bits.em_instruction_fetch =
per_info->single_step | per_info->instruction_fetch;
if (per_info->single_step) {
per_info->control_regs.bits.starting_addr = 0;
#ifdef CONFIG_COMPAT
if (is_compat_task())
per_info->control_regs.bits.ending_addr = 0x7fffffffUL;
else
#endif
per_info->control_regs.bits.ending_addr = PSW_ADDR_INSN;
} else {
per_info->control_regs.bits.starting_addr =
per_info->starting_addr;
per_info->control_regs.bits.ending_addr =
per_info->ending_addr;
}
/*
* if any of the control reg tracing bits are on
* we switch on per in the psw
*/
if (per_info->control_regs.words.cr[0] & PER_EM_MASK)
regs->psw.mask |= PSW_MASK_PER;
else
regs->psw.mask &= ~PSW_MASK_PER;
if (per_info->control_regs.bits.em_storage_alteration)
per_info->control_regs.bits.storage_alt_space_ctl = 1;
else
per_info->control_regs.bits.storage_alt_space_ctl = 0;
if (task == current) {
__ctl_store(cr_words, 9, 11);
if (memcmp(&cr_words, &per_info->control_regs.words,
sizeof(cr_words)) != 0)
__ctl_load(per_info->control_regs.words, 9, 11);
}
}
void user_enable_single_step(struct task_struct *task)
{
task->thread.per_info.single_step = 1;
FixPerRegisters(task);
}
void user_disable_single_step(struct task_struct *task)
{
task->thread.per_info.single_step = 0;
FixPerRegisters(task);
}
/*
* Called by kernel/ptrace.c when detaching..
*
* Make sure single step bits etc are not set.
*/
void
ptrace_disable(struct task_struct *child)
{
/* make sure the single step bit is not set. */
user_disable_single_step(child);
}
#ifndef CONFIG_64BIT
# define __ADDR_MASK 3
#else
# define __ADDR_MASK 7
#endif
/*
* Read the word at offset addr from the user area of a process. The
* trouble here is that the information is littered over different
* locations. The process registers are found on the kernel stack,
* the floating point stuff and the trace settings are stored in
* the task structure. In addition the different structures in
* struct user contain pad bytes that should be read as zeroes.
* Lovely...
*/
static unsigned long __peek_user(struct task_struct *child, addr_t addr)
{
struct user *dummy = NULL;
addr_t offset, tmp;
if (addr < (addr_t) &dummy->regs.acrs