summaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/ptrace.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2011-10-30 15:16:51 +0100
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-10-30 15:16:43 +0100
commitd4e81b35b882d96f059afdb0f98e5b6025973b09 (patch)
tree5485be2f8bb0e31d153bf2b7a8bfa3bcabc457ac /arch/s390/kernel/ptrace.c
parentb50511e41aa51a89b4176784a670582424bc7db6 (diff)
[S390] allow all addressing modes
The user space program can change its addressing mode between the 24-bit, 31-bit and the 64-bit mode if the kernel is 64 bit. Currently the kernel always forces the standard amode on signal delivery and signal return and on ptrace: 64-bit for a 64-bit process, 31-bit for a compat process and 31-bit kernels. Change the signal and ptrace code to allow the full range of addressing modes. Signal handlers are run in the standard addressing mode for the process. One caveat is that even an 31-bit compat process can switch to the 64-bit mode. The next signal will switch back into the 31-bit mode and there is no room in the 31-bit compat signal frame to store the information that the program came from the 64-bit mode. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/ptrace.c')
-rw-r--r--arch/s390/kernel/ptrace.c25
1 files changed, 10 insertions, 15 deletions
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 41e20763886..450931a45b6 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -170,8 +170,7 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr)
tmp = *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr);
if (addr == (addr_t) &dummy->regs.psw.mask)
/* Return a clean psw mask. */
- tmp = psw_user_bits | (tmp & PSW_MASK_USER) |
- PSW_MASK_EA | PSW_MASK_BA;
+ tmp = psw_user_bits | (tmp & PSW_MASK_USER);
} else if (addr < (addr_t) &dummy->regs.orig_gpr2) {
/*
@@ -286,26 +285,17 @@ static inline void __poke_user_per(struct task_struct *child,
static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
{
struct user *dummy = NULL;
- addr_t offset, tmp;
+ addr_t offset;
if (addr < (addr_t) &dummy->regs.acrs) {
/*
* psw and gprs are stored on the stack
*/
- tmp = (data & ~PSW_MASK_USER) ^ psw_user_bits;
if (addr == (addr_t) &dummy->regs.psw.mask &&
-#ifdef CONFIG_COMPAT
- tmp != PSW_MASK_BA &&
-#endif
- tmp != (PSW_MASK_EA | PSW_MASK_BA))
+ ((data & ~PSW_MASK_USER) != psw_user_bits ||
+ ((data & PSW_MASK_EA) && !(data & PSW_MASK_BA))))
/* Invalid psw mask. */
return -EINVAL;
-#ifndef CONFIG_64BIT
- if (addr == (addr_t) &dummy->regs.psw.addr)
- /* I'd like to reject addresses without the
- high order bit but older gdb's rely on it */
- data |= PSW_ADDR_AMODE;
-#endif
if (addr == (addr_t) &dummy->regs.psw.addr)
/*
* The debugger changed the instruction address,
@@ -517,7 +507,8 @@ static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
tmp = psw32_user_bits | (tmp & PSW32_MASK_USER);
} else if (addr == (addr_t) &dummy32->regs.psw.addr) {
/* Fake a 31 bit psw address. */
- tmp = (__u32) regs->psw.addr | PSW32_ADDR_AMODE;
+ tmp = (__u32) regs->psw.addr |
+ (__u32)(regs->psw.mask & PSW_MASK_BA);
} else {
/* gpr 0-15 */
tmp = *(__u32 *)((addr_t) &regs->psw + addr*2 + 4);
@@ -615,10 +606,14 @@ static int __poke_user_compat(struct task_struct *child,
/* Invalid psw mask. */
return -EINVAL;
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
+ (regs->psw.mask & PSW_MASK_BA) |
(__u64)(tmp & PSW32_MASK_USER) << 32;
} else if (addr == (addr_t) &dummy32->regs.psw.addr) {
/* Build a 64 bit psw address from 31 bit address. */
regs->psw.addr = (__u64) tmp & PSW32_ADDR_INSN;
+ /* Transfer 31 bit amode bit to psw mask. */
+ regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) |
+ (__u64)(tmp & PSW32_ADDR_AMODE);
/*
* The debugger changed the instruction address,
* reset system call restart, see signal.c:do_signal