Commit d4e81b35 authored by Martin Schwidefsky's avatar Martin Schwidefsky

[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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent b50511e4
...@@ -269,7 +269,7 @@ typedef struct ...@@ -269,7 +269,7 @@ typedef struct
#define PSW_MASK_EA 0x0000000100000000UL #define PSW_MASK_EA 0x0000000100000000UL
#define PSW_MASK_BA 0x0000000080000000UL #define PSW_MASK_BA 0x0000000080000000UL
#define PSW_MASK_USER 0x00003F0000000000UL #define PSW_MASK_USER 0x00003F0180000000UL
#define PSW_ADDR_AMODE 0x0000000000000000UL #define PSW_ADDR_AMODE 0x0000000000000000UL
#define PSW_ADDR_INSN 0xFFFFFFFFFFFFFFFFUL #define PSW_ADDR_INSN 0xFFFFFFFFFFFFFFFFUL
......
...@@ -302,7 +302,8 @@ static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) ...@@ -302,7 +302,8 @@ static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs)
regs32.psw.mask = psw32_user_bits | regs32.psw.mask = psw32_user_bits |
((__u32)(regs->psw.mask >> 32) & PSW32_MASK_USER); ((__u32)(regs->psw.mask >> 32) & PSW32_MASK_USER);
regs32.psw.addr = PSW32_ADDR_AMODE | (__u32) regs->psw.addr; regs32.psw.addr = (__u32) regs->psw.addr |
(__u32)(regs->psw.mask & PSW_MASK_BA);
for (i = 0; i < NUM_GPRS; i++) for (i = 0; i < NUM_GPRS; i++)
regs32.gprs[i] = (__u32) regs->gprs[i]; regs32.gprs[i] = (__u32) regs->gprs[i];
save_access_regs(current->thread.acrs); save_access_regs(current->thread.acrs);
...@@ -328,7 +329,8 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) ...@@ -328,7 +329,8 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
if (err) if (err)
return err; return err;
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
(__u64)(regs32.psw.mask & PSW32_MASK_USER) << 32; (__u64)(regs32.psw.mask & PSW32_MASK_USER) << 32 |
(__u64)(regs32.psw.addr & PSW32_ADDR_AMODE);
regs->psw.addr = (__u64)(regs32.psw.addr & PSW32_ADDR_INSN); regs->psw.addr = (__u64)(regs32.psw.addr & PSW32_ADDR_INSN);
for (i = 0; i < NUM_GPRS; i++) for (i = 0; i < NUM_GPRS; i++)
regs->gprs[i] = (__u64) regs32.gprs[i]; regs->gprs[i] = (__u64) regs32.gprs[i];
...@@ -496,9 +498,9 @@ static int setup_frame32(int sig, struct k_sigaction *ka, ...@@ -496,9 +498,9 @@ static int setup_frame32(int sig, struct k_sigaction *ka,
/* Set up to return from userspace. If provided, use a stub /* Set up to return from userspace. If provided, use a stub
already in userspace. */ already in userspace. */
if (ka->sa.sa_flags & SA_RESTORER) { if (ka->sa.sa_flags & SA_RESTORER) {
regs->gprs[14] = (__u64) ka->sa.sa_restorer; regs->gprs[14] = (__u64) ka->sa.sa_restorer | PSW32_ADDR_AMODE;
} else { } else {
regs->gprs[14] = (__u64) frame->retcode; regs->gprs[14] = (__u64) frame->retcode | PSW32_ADDR_AMODE;
if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn,
(u16 __user *)(frame->retcode))) (u16 __user *)(frame->retcode)))
goto give_sigsegv; goto give_sigsegv;
...@@ -510,6 +512,7 @@ static int setup_frame32(int sig, struct k_sigaction *ka, ...@@ -510,6 +512,7 @@ static int setup_frame32(int sig, struct k_sigaction *ka,
/* Set up registers for signal handler */ /* Set up registers for signal handler */
regs->gprs[15] = (__u64) frame; regs->gprs[15] = (__u64) frame;
regs->psw.mask |= PSW_MASK_BA; /* force amode 31 */
regs->psw.addr = (__u64) ka->sa.sa_handler; regs->psw.addr = (__u64) ka->sa.sa_handler;
regs->gprs[2] = map_signal(sig); regs->gprs[2] = map_signal(sig);
...@@ -573,6 +576,7 @@ static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, ...@@ -573,6 +576,7 @@ static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,
/* Set up registers for signal handler */ /* Set up registers for signal handler */
regs->gprs[15] = (__u64) frame; regs->gprs[15] = (__u64) frame;
regs->psw.mask |= PSW_MASK_BA; /* force amode 31 */
regs->psw.addr = (__u64) ka->sa.sa_handler; regs->psw.addr = (__u64) ka->sa.sa_handler;
regs->gprs[2] = map_signal(sig); regs->gprs[2] = map_signal(sig);
......
...@@ -170,8 +170,7 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr) ...@@ -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); tmp = *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr);
if (addr == (addr_t) &dummy->regs.psw.mask) if (addr == (addr_t) &dummy->regs.psw.mask)
/* Return a clean psw mask. */ /* Return a clean psw mask. */
tmp = psw_user_bits | (tmp & PSW_MASK_USER) | tmp = psw_user_bits | (tmp & PSW_MASK_USER);
PSW_MASK_EA | PSW_MASK_BA;
} else if (addr < (addr_t) &dummy->regs.orig_gpr2) { } else if (addr < (addr_t) &dummy->regs.orig_gpr2) {
/* /*
...@@ -286,26 +285,17 @@ static inline void __poke_user_per(struct task_struct *child, ...@@ -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) static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
{ {
struct user *dummy = NULL; struct user *dummy = NULL;
addr_t offset, tmp; addr_t offset;
if (addr < (addr_t) &dummy->regs.acrs) { if (addr < (addr_t) &dummy->regs.acrs) {
/* /*
* psw and gprs are stored on the stack * psw and gprs are stored on the stack
*/ */
tmp = (data & ~PSW_MASK_USER) ^ psw_user_bits;
if (addr == (addr_t) &dummy->regs.psw.mask && if (addr == (addr_t) &dummy->regs.psw.mask &&
#ifdef CONFIG_COMPAT ((data & ~PSW_MASK_USER) != psw_user_bits ||
tmp != PSW_MASK_BA && ((data & PSW_MASK_EA) && !(data & PSW_MASK_BA))))
#endif
tmp != (PSW_MASK_EA | PSW_MASK_BA))
/* Invalid psw mask. */ /* Invalid psw mask. */
return -EINVAL; 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) if (addr == (addr_t) &dummy->regs.psw.addr)
/* /*
* The debugger changed the instruction address, * The debugger changed the instruction address,
...@@ -517,7 +507,8 @@ static u32 __peek_user_compat(struct task_struct *child, addr_t addr) ...@@ -517,7 +507,8 @@ static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
tmp = psw32_user_bits | (tmp & PSW32_MASK_USER); tmp = psw32_user_bits | (tmp & PSW32_MASK_USER);
} else if (addr == (addr_t) &dummy32->regs.psw.addr) { } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
/* Fake a 31 bit psw address. */ /* 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 { } else {
/* gpr 0-15 */ /* gpr 0-15 */
tmp = *(__u32 *)((addr_t) &regs->psw + addr*2 + 4); tmp = *(__u32 *)((addr_t) &regs->psw + addr*2 + 4);
...@@ -615,10 +606,14 @@ static int __poke_user_compat(struct task_struct *child, ...@@ -615,10 +606,14 @@ static int __poke_user_compat(struct task_struct *child,
/* Invalid psw mask. */ /* Invalid psw mask. */
return -EINVAL; return -EINVAL;
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
(regs->psw.mask & PSW_MASK_BA) |
(__u64)(tmp & PSW32_MASK_USER) << 32; (__u64)(tmp & PSW32_MASK_USER) << 32;
} else if (addr == (addr_t) &dummy32->regs.psw.addr) { } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
/* Build a 64 bit psw address from 31 bit address. */ /* Build a 64 bit psw address from 31 bit address. */
regs->psw.addr = (__u64) tmp & PSW32_ADDR_INSN; 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, * The debugger changed the instruction address,
* reset system call restart, see signal.c:do_signal * reset system call restart, see signal.c:do_signal
......
...@@ -117,7 +117,7 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) ...@@ -117,7 +117,7 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
/* Copy a 'clean' PSW mask to the user to avoid leaking /* Copy a 'clean' PSW mask to the user to avoid leaking
information about whether PER is currently on. */ information about whether PER is currently on. */
user_sregs.regs.psw.mask = psw_user_bits | PSW_MASK_EA | PSW_MASK_BA | user_sregs.regs.psw.mask = psw_user_bits |
(regs->psw.mask & PSW_MASK_USER); (regs->psw.mask & PSW_MASK_USER);
user_sregs.regs.psw.addr = regs->psw.addr; user_sregs.regs.psw.addr = regs->psw.addr;
memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs)); memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs));
...@@ -145,9 +145,13 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) ...@@ -145,9 +145,13 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
err = __copy_from_user(&user_sregs, sregs, sizeof(_sigregs)); err = __copy_from_user(&user_sregs, sregs, sizeof(_sigregs));
if (err) if (err)
return err; return err;
/* Use regs->psw.mask instead of psw_user_bits to preserve PER bit. */
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
(user_sregs.regs.psw.mask & PSW_MASK_USER); (user_sregs.regs.psw.mask & PSW_MASK_USER);
regs->psw.addr = PSW_ADDR_AMODE | user_sregs.regs.psw.addr; /* Check for invalid amode */
if (regs->psw.mask & PSW_MASK_EA)
regs->psw.mask |= PSW_MASK_BA;
regs->psw.addr = user_sregs.regs.psw.addr;
memcpy(&regs->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); memcpy(&regs->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs));
memcpy(&current->thread.acrs, &user_sregs.regs.acrs, memcpy(&current->thread.acrs, &user_sregs.regs.acrs,
sizeof(sregs->regs.acrs)); sizeof(sregs->regs.acrs));
...@@ -290,6 +294,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, ...@@ -290,6 +294,7 @@ static int setup_frame(int sig, struct k_sigaction *ka,
/* Set up registers for signal handler */ /* Set up registers for signal handler */
regs->gprs[15] = (unsigned long) frame; regs->gprs[15] = (unsigned long) frame;
regs->psw.mask |= PSW_MASK_EA | PSW_MASK_BA; /* 64 bit amode */
regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE; regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE;
regs->gprs[2] = map_signal(sig); regs->gprs[2] = map_signal(sig);
...@@ -358,6 +363,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, ...@@ -358,6 +363,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
/* Set up registers for signal handler */ /* Set up registers for signal handler */
regs->gprs[15] = (unsigned long) frame; regs->gprs[15] = (unsigned long) frame;
regs->psw.mask |= PSW_MASK_EA | PSW_MASK_BA; /* 64 bit amode */
regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE; regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE;
regs->gprs[2] = map_signal(sig); regs->gprs[2] = map_signal(sig);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment