[S390] signal race with restarting system calls
For a ERESTARTNOHAND/ERESTARTSYS/ERESTARTNOINTR restarting system call do_signal will prepare the restart of the system call with a rewind of the PSW before calling get_signal_to_deliver (where the debugger might take control). For A ERESTART_RESTARTBLOCK restarting system call do_signal will set -EINTR as return code. There are two issues with this approach: 1) strace never sees ERESTARTNOHAND, ERESTARTSYS, ERESTARTNOINTR or ERESTART_RESTARTBLOCK as the rewinding already took place or the return code has been changed to -EINTR 2) if get_signal_to_deliver does not return with a signal to deliver the restart via the repeat of the svc instruction is left in place. This opens a race if another signal is made pending before the system call instruction can be reexecuted. The original system call will be restarted even if the second signal would have ended the system call with -EINTR. These two issues can be solved by dropping the early rewind of the system call before get_signal_to_deliver has been called and by using the TIF_RESTART_SVC magic to do the restart if no signal has to be delivered. The only situation where the system call restart via the repeat of the svc instruction is appropriate is when a SA_RESTART signal is delivered to user space. Unfortunately this breaks inferior calls by the debugger again. The system call number and the length of the system call instruction is lost over the inferior call and user space will see ERESTARTNOHAND/ ERESTARTSYS/ERESTARTNOINTR/ERESTART_RESTARTBLOCK. To correct this a new ptrace interface is added to save/restore the system call number and system call instruction length. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
3ee49c8f12
commit
20b40a794b
10 changed files with 142 additions and 91 deletions
|
@ -328,8 +328,7 @@ struct pt_regs
|
||||||
psw_t psw;
|
psw_t psw;
|
||||||
unsigned long gprs[NUM_GPRS];
|
unsigned long gprs[NUM_GPRS];
|
||||||
unsigned long orig_gpr2;
|
unsigned long orig_gpr2;
|
||||||
unsigned short ilc;
|
unsigned int svc_code;
|
||||||
unsigned short svcnr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -487,6 +486,8 @@ typedef struct
|
||||||
#define PTRACE_POKETEXT_AREA 0x5004
|
#define PTRACE_POKETEXT_AREA 0x5004
|
||||||
#define PTRACE_POKEDATA_AREA 0x5005
|
#define PTRACE_POKEDATA_AREA 0x5005
|
||||||
#define PTRACE_GET_LAST_BREAK 0x5006
|
#define PTRACE_GET_LAST_BREAK 0x5006
|
||||||
|
#define PTRACE_PEEK_SYSTEM_CALL 0x5007
|
||||||
|
#define PTRACE_POKE_SYSTEM_CALL 0x5008
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PT_PROT definition is loosely based on hppa bsd definition in
|
* PT_PROT definition is loosely based on hppa bsd definition in
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#define _ASM_SYSCALL_H 1
|
#define _ASM_SYSCALL_H 1
|
||||||
|
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -25,7 +26,7 @@ extern const unsigned int sys_call_table[];
|
||||||
static inline long syscall_get_nr(struct task_struct *task,
|
static inline long syscall_get_nr(struct task_struct *task,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
return regs->svcnr ? regs->svcnr : -1;
|
return regs->svc_code ? (regs->svc_code & 0xffff) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void syscall_rollback(struct task_struct *task,
|
static inline void syscall_rollback(struct task_struct *task,
|
||||||
|
@ -37,7 +38,7 @@ static inline void syscall_rollback(struct task_struct *task,
|
||||||
static inline long syscall_get_error(struct task_struct *task,
|
static inline long syscall_get_error(struct task_struct *task,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
return (regs->gprs[2] >= -4096UL) ? -regs->gprs[2] : 0;
|
return IS_ERR_VALUE(regs->gprs[2]) ? regs->gprs[2] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline long syscall_get_return_value(struct task_struct *task,
|
static inline long syscall_get_return_value(struct task_struct *task,
|
||||||
|
|
|
@ -48,6 +48,7 @@ struct thread_info {
|
||||||
unsigned int cpu; /* current CPU */
|
unsigned int cpu; /* current CPU */
|
||||||
int preempt_count; /* 0 => preemptable, <0 => BUG */
|
int preempt_count; /* 0 => preemptable, <0 => BUG */
|
||||||
struct restart_block restart_block;
|
struct restart_block restart_block;
|
||||||
|
unsigned int system_call;
|
||||||
__u64 user_timer;
|
__u64 user_timer;
|
||||||
__u64 system_timer;
|
__u64 system_timer;
|
||||||
unsigned long last_break; /* last breaking-event-address. */
|
unsigned long last_break; /* last breaking-event-address. */
|
||||||
|
|
|
@ -45,8 +45,7 @@ int main(void)
|
||||||
DEFINE(__PT_PSW, offsetof(struct pt_regs, psw));
|
DEFINE(__PT_PSW, offsetof(struct pt_regs, psw));
|
||||||
DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs));
|
DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs));
|
||||||
DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2));
|
DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2));
|
||||||
DEFINE(__PT_ILC, offsetof(struct pt_regs, ilc));
|
DEFINE(__PT_SVC_CODE, offsetof(struct pt_regs, svc_code));
|
||||||
DEFINE(__PT_SVCNR, offsetof(struct pt_regs, svcnr));
|
|
||||||
DEFINE(__PT_SIZE, sizeof(struct pt_regs));
|
DEFINE(__PT_SIZE, sizeof(struct pt_regs));
|
||||||
BLANK();
|
BLANK();
|
||||||
DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain));
|
DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain));
|
||||||
|
|
|
@ -342,7 +342,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
restore_fp_regs(¤t->thread.fp_regs);
|
restore_fp_regs(¤t->thread.fp_regs);
|
||||||
regs->svcnr = 0; /* disable syscall checks */
|
regs->svc_code = 0; /* disable syscall checks */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,8 +43,7 @@ SP_R13 = STACK_FRAME_OVERHEAD + __PT_GPRS + 52
|
||||||
SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 56
|
SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 56
|
||||||
SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 60
|
SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 60
|
||||||
SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2
|
SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2
|
||||||
SP_ILC = STACK_FRAME_OVERHEAD + __PT_ILC
|
SP_SVC_CODE = STACK_FRAME_OVERHEAD + __PT_SVC_CODE
|
||||||
SP_SVCNR = STACK_FRAME_OVERHEAD + __PT_SVCNR
|
|
||||||
SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE
|
SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE
|
||||||
|
|
||||||
_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
|
_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
|
||||||
|
@ -229,7 +228,7 @@ sysc_saveall:
|
||||||
SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
|
SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
|
||||||
CREATE_STACK_FRAME __LC_SAVE_AREA
|
CREATE_STACK_FRAME __LC_SAVE_AREA
|
||||||
mvc SP_PSW(8,%r15),__LC_SVC_OLD_PSW
|
mvc SP_PSW(8,%r15),__LC_SVC_OLD_PSW
|
||||||
mvc SP_ILC(4,%r15),__LC_SVC_ILC
|
mvc SP_SVC_CODE(4,%r15),__LC_SVC_ILC
|
||||||
l %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
l %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||||
sysc_vtime:
|
sysc_vtime:
|
||||||
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
|
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
|
||||||
|
@ -239,12 +238,12 @@ sysc_update:
|
||||||
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
|
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
|
||||||
sysc_do_svc:
|
sysc_do_svc:
|
||||||
xr %r7,%r7
|
xr %r7,%r7
|
||||||
icm %r7,3,SP_SVCNR(%r15) # load svc number and test for svc 0
|
icm %r7,3,SP_SVC_CODE+2(%r15)# load svc number and test for svc 0
|
||||||
bnz BASED(sysc_nr_ok) # svc number > 0
|
bnz BASED(sysc_nr_ok) # svc number > 0
|
||||||
# svc 0: system call number in %r1
|
# svc 0: system call number in %r1
|
||||||
cl %r1,BASED(.Lnr_syscalls)
|
cl %r1,BASED(.Lnr_syscalls)
|
||||||
bnl BASED(sysc_nr_ok)
|
bnl BASED(sysc_nr_ok)
|
||||||
sth %r1,SP_SVCNR(%r15)
|
sth %r1,SP_SVC_CODE+2(%r15)
|
||||||
lr %r7,%r1 # copy svc number to %r7
|
lr %r7,%r1 # copy svc number to %r7
|
||||||
sysc_nr_ok:
|
sysc_nr_ok:
|
||||||
sll %r7,2 # svc number *4
|
sll %r7,2 # svc number *4
|
||||||
|
@ -335,10 +334,11 @@ sysc_notify_resume:
|
||||||
#
|
#
|
||||||
sysc_restart:
|
sysc_restart:
|
||||||
ni __TI_flags+3(%r12),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC
|
ni __TI_flags+3(%r12),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC
|
||||||
l %r7,SP_R2(%r15) # load new svc number
|
|
||||||
mvc SP_R2(4,%r15),SP_ORIG_R2(%r15) # restore first argument
|
|
||||||
lm %r2,%r6,SP_R2(%r15) # load svc arguments
|
lm %r2,%r6,SP_R2(%r15) # load svc arguments
|
||||||
sth %r7,SP_SVCNR(%r15)
|
xr %r7,%r7 # svc 0 returns -ENOSYS
|
||||||
|
clc SP_SVC_CODE+2(%r15),BASED(.Lnr_syscalls+2)
|
||||||
|
bnl BASED(sysc_nr_ok) # invalid svc number -> do svc 0
|
||||||
|
icm %r7,3,SP_SVC_CODE+2(%r15)# load new svc number
|
||||||
b BASED(sysc_nr_ok) # restart svc
|
b BASED(sysc_nr_ok) # restart svc
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -346,7 +346,7 @@ sysc_restart:
|
||||||
#
|
#
|
||||||
sysc_singlestep:
|
sysc_singlestep:
|
||||||
ni __TI_flags+3(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
|
ni __TI_flags+3(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
|
||||||
xc SP_SVCNR(2,%r15),SP_SVCNR(%r15) # clear svc number
|
xc SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15) # clear svc code
|
||||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||||
l %r1,BASED(.Lhandle_per) # load adr. of per handler
|
l %r1,BASED(.Lhandle_per) # load adr. of per handler
|
||||||
la %r14,BASED(sysc_return) # load adr. of system return
|
la %r14,BASED(sysc_return) # load adr. of system return
|
||||||
|
@ -361,7 +361,7 @@ sysc_tracesys:
|
||||||
la %r2,SP_PTREGS(%r15) # load pt_regs
|
la %r2,SP_PTREGS(%r15) # load pt_regs
|
||||||
la %r3,0
|
la %r3,0
|
||||||
xr %r0,%r0
|
xr %r0,%r0
|
||||||
icm %r0,3,SP_SVCNR(%r15)
|
icm %r0,3,SP_SVC_CODE(%r15)
|
||||||
st %r0,SP_R2(%r15)
|
st %r0,SP_R2(%r15)
|
||||||
basr %r14,%r1
|
basr %r14,%r1
|
||||||
cl %r2,BASED(.Lnr_syscalls)
|
cl %r2,BASED(.Lnr_syscalls)
|
||||||
|
@ -454,7 +454,7 @@ ENTRY(pgm_check_handler)
|
||||||
bnz BASED(pgm_per) # got per exception -> special case
|
bnz BASED(pgm_per) # got per exception -> special case
|
||||||
SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA
|
SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA
|
||||||
CREATE_STACK_FRAME __LC_SAVE_AREA
|
CREATE_STACK_FRAME __LC_SAVE_AREA
|
||||||
xc SP_ILC(4,%r15),SP_ILC(%r15)
|
xc SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15)
|
||||||
mvc SP_PSW(8,%r15),__LC_PGM_OLD_PSW
|
mvc SP_PSW(8,%r15),__LC_PGM_OLD_PSW
|
||||||
l %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
l %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||||
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
|
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
|
||||||
|
@ -531,7 +531,7 @@ pgm_svcper:
|
||||||
SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA
|
SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA
|
||||||
CREATE_STACK_FRAME __LC_SAVE_AREA
|
CREATE_STACK_FRAME __LC_SAVE_AREA
|
||||||
mvc SP_PSW(8,%r15),__LC_SVC_OLD_PSW
|
mvc SP_PSW(8,%r15),__LC_SVC_OLD_PSW
|
||||||
mvc SP_ILC(4,%r15),__LC_SVC_ILC
|
mvc SP_SVC_CODE(4,%r15),__LC_SVC_ILC
|
||||||
l %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
l %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||||
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
|
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
|
||||||
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
|
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
|
||||||
|
@ -550,7 +550,7 @@ pgm_svcper:
|
||||||
#
|
#
|
||||||
kernel_per:
|
kernel_per:
|
||||||
REENABLE_IRQS
|
REENABLE_IRQS
|
||||||
xc SP_SVCNR(2,%r15),SP_SVCNR(%r15)
|
xc SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15)
|
||||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||||
l %r1,BASED(.Lhandle_per) # load adr. of per handler
|
l %r1,BASED(.Lhandle_per) # load adr. of per handler
|
||||||
basr %r14,%r1 # branch to do_single_step
|
basr %r14,%r1 # branch to do_single_step
|
||||||
|
@ -966,7 +966,7 @@ cleanup_system_call:
|
||||||
st %r15,12(%r12)
|
st %r15,12(%r12)
|
||||||
CREATE_STACK_FRAME __LC_SAVE_AREA
|
CREATE_STACK_FRAME __LC_SAVE_AREA
|
||||||
mvc SP_PSW(8,%r15),__LC_SVC_OLD_PSW
|
mvc SP_PSW(8,%r15),__LC_SVC_OLD_PSW
|
||||||
mvc SP_ILC(4,%r15),__LC_SVC_ILC
|
mvc SP_SVC_CODE(4,%r15),__LC_SVC_ILC
|
||||||
mvc 0(4,%r12),__LC_THREAD_INFO
|
mvc 0(4,%r12),__LC_THREAD_INFO
|
||||||
cleanup_vtime:
|
cleanup_vtime:
|
||||||
clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+12)
|
clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+12)
|
||||||
|
|
|
@ -43,8 +43,7 @@ SP_R13 = STACK_FRAME_OVERHEAD + __PT_GPRS + 104
|
||||||
SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 112
|
SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 112
|
||||||
SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 120
|
SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 120
|
||||||
SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2
|
SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2
|
||||||
SP_ILC = STACK_FRAME_OVERHEAD + __PT_ILC
|
SP_SVC_CODE = STACK_FRAME_OVERHEAD + __PT_SVC_CODE
|
||||||
SP_SVCNR = STACK_FRAME_OVERHEAD + __PT_SVCNR
|
|
||||||
SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE
|
SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE
|
||||||
|
|
||||||
STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER
|
STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER
|
||||||
|
@ -250,7 +249,7 @@ sysc_saveall:
|
||||||
SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
|
SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
|
||||||
CREATE_STACK_FRAME __LC_SAVE_AREA
|
CREATE_STACK_FRAME __LC_SAVE_AREA
|
||||||
mvc SP_PSW(16,%r15),__LC_SVC_OLD_PSW
|
mvc SP_PSW(16,%r15),__LC_SVC_OLD_PSW
|
||||||
mvc SP_ILC(4,%r15),__LC_SVC_ILC
|
mvc SP_SVC_CODE(4,%r15),__LC_SVC_ILC
|
||||||
lg %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
lg %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||||
sysc_vtime:
|
sysc_vtime:
|
||||||
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
|
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
|
||||||
|
@ -260,14 +259,14 @@ sysc_update:
|
||||||
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
|
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
|
||||||
LAST_BREAK
|
LAST_BREAK
|
||||||
sysc_do_svc:
|
sysc_do_svc:
|
||||||
llgh %r7,SP_SVCNR(%r15)
|
llgh %r7,SP_SVC_CODE+2(%r15)
|
||||||
slag %r7,%r7,2 # shift and test for svc 0
|
slag %r7,%r7,2 # shift and test for svc 0
|
||||||
jnz sysc_nr_ok
|
jnz sysc_nr_ok
|
||||||
# svc 0: system call number in %r1
|
# svc 0: system call number in %r1
|
||||||
llgfr %r1,%r1 # clear high word in r1
|
llgfr %r1,%r1 # clear high word in r1
|
||||||
cghi %r1,NR_syscalls
|
cghi %r1,NR_syscalls
|
||||||
jnl sysc_nr_ok
|
jnl sysc_nr_ok
|
||||||
sth %r1,SP_SVCNR(%r15)
|
sth %r1,SP_SVC_CODE+2(%r15)
|
||||||
slag %r7,%r1,2 # shift and test for svc 0
|
slag %r7,%r1,2 # shift and test for svc 0
|
||||||
sysc_nr_ok:
|
sysc_nr_ok:
|
||||||
larl %r10,sys_call_table
|
larl %r10,sys_call_table
|
||||||
|
@ -358,11 +357,12 @@ sysc_notify_resume:
|
||||||
#
|
#
|
||||||
sysc_restart:
|
sysc_restart:
|
||||||
ni __TI_flags+7(%r12),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC
|
ni __TI_flags+7(%r12),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC
|
||||||
lg %r7,SP_R2(%r15) # load new svc number
|
|
||||||
mvc SP_R2(8,%r15),SP_ORIG_R2(%r15) # restore first argument
|
|
||||||
lmg %r2,%r6,SP_R2(%r15) # load svc arguments
|
lmg %r2,%r6,SP_R2(%r15) # load svc arguments
|
||||||
sth %r7,SP_SVCNR(%r15)
|
lghi %r7,0 # svc 0 returns -ENOSYS
|
||||||
slag %r7,%r7,2
|
lh %r1,SP_SVC_CODE+2(%r15) # load new svc number
|
||||||
|
cghi %r1,NR_syscalls
|
||||||
|
jnl sysc_nr_ok # invalid svc number -> do svc 0
|
||||||
|
slag %r7,%r1,2
|
||||||
j sysc_nr_ok # restart svc
|
j sysc_nr_ok # restart svc
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -370,7 +370,7 @@ sysc_restart:
|
||||||
#
|
#
|
||||||
sysc_singlestep:
|
sysc_singlestep:
|
||||||
ni __TI_flags+7(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
|
ni __TI_flags+7(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
|
||||||
xc SP_SVCNR(2,%r15),SP_SVCNR(%r15) # clear svc number
|
xc SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15) # clear svc code
|
||||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||||
larl %r14,sysc_return # load adr. of system return
|
larl %r14,sysc_return # load adr. of system return
|
||||||
jg do_per_trap
|
jg do_per_trap
|
||||||
|
@ -382,7 +382,7 @@ sysc_singlestep:
|
||||||
sysc_tracesys:
|
sysc_tracesys:
|
||||||
la %r2,SP_PTREGS(%r15) # load pt_regs
|
la %r2,SP_PTREGS(%r15) # load pt_regs
|
||||||
la %r3,0
|
la %r3,0
|
||||||
llgh %r0,SP_SVCNR(%r15)
|
llgh %r0,SP_SVC_CODE+2(%r15)
|
||||||
stg %r0,SP_R2(%r15)
|
stg %r0,SP_R2(%r15)
|
||||||
brasl %r14,do_syscall_trace_enter
|
brasl %r14,do_syscall_trace_enter
|
||||||
lghi %r0,NR_syscalls
|
lghi %r0,NR_syscalls
|
||||||
|
@ -470,7 +470,7 @@ ENTRY(pgm_check_handler)
|
||||||
jnz pgm_per # got per exception -> special case
|
jnz pgm_per # got per exception -> special case
|
||||||
SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA
|
SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA
|
||||||
CREATE_STACK_FRAME __LC_SAVE_AREA
|
CREATE_STACK_FRAME __LC_SAVE_AREA
|
||||||
xc SP_ILC(4,%r15),SP_ILC(%r15)
|
xc SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15)
|
||||||
mvc SP_PSW(16,%r15),__LC_PGM_OLD_PSW
|
mvc SP_PSW(16,%r15),__LC_PGM_OLD_PSW
|
||||||
lg %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
lg %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||||
HANDLE_SIE_INTERCEPT
|
HANDLE_SIE_INTERCEPT
|
||||||
|
@ -551,7 +551,7 @@ pgm_svcper:
|
||||||
SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA
|
SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA
|
||||||
CREATE_STACK_FRAME __LC_SAVE_AREA
|
CREATE_STACK_FRAME __LC_SAVE_AREA
|
||||||
mvc SP_PSW(16,%r15),__LC_SVC_OLD_PSW
|
mvc SP_PSW(16,%r15),__LC_SVC_OLD_PSW
|
||||||
mvc SP_ILC(4,%r15),__LC_SVC_ILC
|
mvc SP_SVC_CODE(4,%r15),__LC_SVC_ILC
|
||||||
lg %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
lg %r12,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||||
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
|
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
|
||||||
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
|
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
|
||||||
|
@ -571,7 +571,7 @@ pgm_svcper:
|
||||||
#
|
#
|
||||||
kernel_per:
|
kernel_per:
|
||||||
REENABLE_IRQS
|
REENABLE_IRQS
|
||||||
xc SP_SVCNR(2,%r15),SP_SVCNR(%r15) # clear svc number
|
xc SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15) # clear svc number
|
||||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||||
brasl %r14,do_per_trap
|
brasl %r14,do_per_trap
|
||||||
j pgm_exit
|
j pgm_exit
|
||||||
|
@ -973,7 +973,7 @@ cleanup_system_call:
|
||||||
stg %r11,0(%r12)
|
stg %r11,0(%r12)
|
||||||
CREATE_STACK_FRAME __LC_SAVE_AREA
|
CREATE_STACK_FRAME __LC_SAVE_AREA
|
||||||
mvc SP_PSW(16,%r15),__LC_SVC_OLD_PSW
|
mvc SP_PSW(16,%r15),__LC_SVC_OLD_PSW
|
||||||
mvc SP_ILC(4,%r15),__LC_SVC_ILC
|
mvc SP_SVC_CODE(4,%r15),__LC_SVC_ILC
|
||||||
mvc 8(8,%r12),__LC_THREAD_INFO
|
mvc 8(8,%r12),__LC_THREAD_INFO
|
||||||
cleanup_vtime:
|
cleanup_vtime:
|
||||||
clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+24)
|
clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+24)
|
||||||
|
|
|
@ -42,6 +42,7 @@ enum s390_regset {
|
||||||
REGSET_GENERAL,
|
REGSET_GENERAL,
|
||||||
REGSET_FP,
|
REGSET_FP,
|
||||||
REGSET_LAST_BREAK,
|
REGSET_LAST_BREAK,
|
||||||
|
REGSET_SYSTEM_CALL,
|
||||||
REGSET_GENERAL_EXTENDED,
|
REGSET_GENERAL_EXTENDED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -303,6 +304,13 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
|
||||||
high order bit but older gdb's rely on it */
|
high order bit but older gdb's rely on it */
|
||||||
data |= PSW_ADDR_AMODE;
|
data |= PSW_ADDR_AMODE;
|
||||||
#endif
|
#endif
|
||||||
|
if (addr == (addr_t) &dummy->regs.psw.addr)
|
||||||
|
/*
|
||||||
|
* The debugger changed the instruction address,
|
||||||
|
* reset system call restart, see signal.c:do_signal
|
||||||
|
*/
|
||||||
|
task_thread_info(child)->system_call = 0;
|
||||||
|
|
||||||
*(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data;
|
*(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data;
|
||||||
|
|
||||||
} else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
|
} else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
|
||||||
|
@ -610,6 +618,11 @@ static int __poke_user_compat(struct task_struct *child,
|
||||||
/* Build a 64 bit psw address from 31 bit address. */
|
/* Build a 64 bit psw address from 31 bit address. */
|
||||||
task_pt_regs(child)->psw.addr =
|
task_pt_regs(child)->psw.addr =
|
||||||
(__u64) tmp & PSW32_ADDR_INSN;
|
(__u64) tmp & PSW32_ADDR_INSN;
|
||||||
|
/*
|
||||||
|
* The debugger changed the instruction address,
|
||||||
|
* reset system call restart, see signal.c:do_signal
|
||||||
|
*/
|
||||||
|
task_thread_info(child)->system_call = 0;
|
||||||
} else {
|
} else {
|
||||||
/* gpr 0-15 */
|
/* gpr 0-15 */
|
||||||
*(__u32*)((addr_t) &task_pt_regs(child)->psw
|
*(__u32*)((addr_t) &task_pt_regs(child)->psw
|
||||||
|
@ -737,7 +750,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
|
||||||
* debugger stored an invalid system call number. Skip
|
* debugger stored an invalid system call number. Skip
|
||||||
* the system call and the system call restart handling.
|
* the system call and the system call restart handling.
|
||||||
*/
|
*/
|
||||||
regs->svcnr = 0;
|
regs->svc_code = 0;
|
||||||
ret = -1;
|
ret = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -899,6 +912,26 @@ static int s390_last_break_get(struct task_struct *target,
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int s390_system_call_get(struct task_struct *target,
|
||||||
|
const struct user_regset *regset,
|
||||||
|
unsigned int pos, unsigned int count,
|
||||||
|
void *kbuf, void __user *ubuf)
|
||||||
|
{
|
||||||
|
unsigned int *data = &task_thread_info(target)->system_call;
|
||||||
|
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
||||||
|
data, 0, sizeof(unsigned int));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s390_system_call_set(struct task_struct *target,
|
||||||
|
const struct user_regset *regset,
|
||||||
|
unsigned int pos, unsigned int count,
|
||||||
|
const void *kbuf, const void __user *ubuf)
|
||||||
|
{
|
||||||
|
unsigned int *data = &task_thread_info(target)->system_call;
|
||||||
|
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||||
|
data, 0, sizeof(unsigned int));
|
||||||
|
}
|
||||||
|
|
||||||
static const struct user_regset s390_regsets[] = {
|
static const struct user_regset s390_regsets[] = {
|
||||||
[REGSET_GENERAL] = {
|
[REGSET_GENERAL] = {
|
||||||
.core_note_type = NT_PRSTATUS,
|
.core_note_type = NT_PRSTATUS,
|
||||||
|
@ -925,6 +958,14 @@ static const struct user_regset s390_regsets[] = {
|
||||||
.get = s390_last_break_get,
|
.get = s390_last_break_get,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
|
[REGSET_SYSTEM_CALL] = {
|
||||||
|
.core_note_type = NT_S390_SYSTEM_CALL,
|
||||||
|
.n = 1,
|
||||||
|
.size = sizeof(unsigned int),
|
||||||
|
.align = sizeof(unsigned int),
|
||||||
|
.get = s390_system_call_get,
|
||||||
|
.set = s390_system_call_set,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct user_regset_view user_s390_view = {
|
static const struct user_regset_view user_s390_view = {
|
||||||
|
@ -1104,6 +1145,14 @@ static const struct user_regset s390_compat_regsets[] = {
|
||||||
.align = sizeof(long),
|
.align = sizeof(long),
|
||||||
.get = s390_compat_last_break_get,
|
.get = s390_compat_last_break_get,
|
||||||
},
|
},
|
||||||
|
[REGSET_SYSTEM_CALL] = {
|
||||||
|
.core_note_type = NT_S390_SYSTEM_CALL,
|
||||||
|
.n = 1,
|
||||||
|
.size = sizeof(compat_uint_t),
|
||||||
|
.align = sizeof(compat_uint_t),
|
||||||
|
.get = s390_system_call_get,
|
||||||
|
.set = s390_system_call_set,
|
||||||
|
},
|
||||||
[REGSET_GENERAL_EXTENDED] = {
|
[REGSET_GENERAL_EXTENDED] = {
|
||||||
.core_note_type = NT_S390_HIGH_GPRS,
|
.core_note_type = NT_S390_HIGH_GPRS,
|
||||||
.n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),
|
.n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <asm/ucontext.h>
|
#include <asm/ucontext.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/lowcore.h>
|
#include <asm/lowcore.h>
|
||||||
|
#include <asm/compat.h>
|
||||||
#include "entry.h"
|
#include "entry.h"
|
||||||
|
|
||||||
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
||||||
|
@ -156,7 +157,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
|
||||||
current->thread.fp_regs.fpc &= FPC_VALID_MASK;
|
current->thread.fp_regs.fpc &= FPC_VALID_MASK;
|
||||||
|
|
||||||
restore_fp_regs(¤t->thread.fp_regs);
|
restore_fp_regs(¤t->thread.fp_regs);
|
||||||
regs->svcnr = 0; /* disable syscall checks */
|
regs->svc_code = 0; /* disable syscall checks */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +402,6 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka,
|
||||||
*/
|
*/
|
||||||
void do_signal(struct pt_regs *regs)
|
void do_signal(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned long retval = 0, continue_addr = 0, restart_addr = 0;
|
|
||||||
siginfo_t info;
|
siginfo_t info;
|
||||||
int signr;
|
int signr;
|
||||||
struct k_sigaction ka;
|
struct k_sigaction ka;
|
||||||
|
@ -421,54 +421,43 @@ void do_signal(struct pt_regs *regs)
|
||||||
else
|
else
|
||||||
oldset = ¤t->blocked;
|
oldset = ¤t->blocked;
|
||||||
|
|
||||||
/* Are we from a system call? */
|
/*
|
||||||
if (regs->svcnr) {
|
* Get signal to deliver. When running under ptrace, at this point
|
||||||
continue_addr = regs->psw.addr;
|
* the debugger may change all our registers, including the system
|
||||||
restart_addr = continue_addr - regs->ilc;
|
* call information.
|
||||||
retval = regs->gprs[2];
|
*/
|
||||||
|
current_thread_info()->system_call = regs->svc_code;
|
||||||
/* Prepare for system call restart. We do this here so that a
|
|
||||||
debugger will see the already changed PSW. */
|
|
||||||
switch (retval) {
|
|
||||||
case -ERESTARTNOHAND:
|
|
||||||
case -ERESTARTSYS:
|
|
||||||
case -ERESTARTNOINTR:
|
|
||||||
regs->gprs[2] = regs->orig_gpr2;
|
|
||||||
regs->psw.addr = restart_addr;
|
|
||||||
break;
|
|
||||||
case -ERESTART_RESTARTBLOCK:
|
|
||||||
regs->gprs[2] = -EINTR;
|
|
||||||
}
|
|
||||||
regs->svcnr = 0; /* Don't deal with this again. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get signal to deliver. When running under ptrace, at this point
|
|
||||||
the debugger may change all our registers ... */
|
|
||||||
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
||||||
|
regs->svc_code = current_thread_info()->system_call;
|
||||||
/* Depending on the signal settings we may need to revert the
|
|
||||||
decision to restart the system call. */
|
|
||||||
if (signr > 0 && regs->psw.addr == restart_addr) {
|
|
||||||
if (retval == -ERESTARTNOHAND
|
|
||||||
|| (retval == -ERESTARTSYS
|
|
||||||
&& !(current->sighand->action[signr-1].sa.sa_flags
|
|
||||||
& SA_RESTART))) {
|
|
||||||
regs->gprs[2] = -EINTR;
|
|
||||||
regs->psw.addr = continue_addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signr > 0) {
|
if (signr > 0) {
|
||||||
/* Whee! Actually deliver the signal. */
|
/* Whee! Actually deliver the signal. */
|
||||||
int ret;
|
if (regs->svc_code > 0) {
|
||||||
#ifdef CONFIG_COMPAT
|
/* Check for system call restarting. */
|
||||||
if (is_compat_task()) {
|
switch (regs->gprs[2]) {
|
||||||
ret = handle_signal32(signr, &ka, &info, oldset, regs);
|
case -ERESTART_RESTARTBLOCK:
|
||||||
}
|
case -ERESTARTNOHAND:
|
||||||
else
|
regs->gprs[2] = -EINTR;
|
||||||
#endif
|
break;
|
||||||
ret = handle_signal(signr, &ka, &info, oldset, regs);
|
case -ERESTARTSYS:
|
||||||
if (!ret) {
|
if (!(ka.sa.sa_flags & SA_RESTART)) {
|
||||||
|
regs->gprs[2] = -EINTR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fallthrough */
|
||||||
|
case -ERESTARTNOINTR:
|
||||||
|
regs->gprs[2] = regs->orig_gpr2;
|
||||||
|
regs->psw.addr = regs->psw.addr -
|
||||||
|
(regs->svc_code >> 16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* No longer in a system call */
|
||||||
|
regs->svc_code = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((is_compat_task() ?
|
||||||
|
handle_signal32(signr, &ka, &info, oldset, regs) :
|
||||||
|
handle_signal(signr, &ka, &info, oldset, regs)) == 0) {
|
||||||
/*
|
/*
|
||||||
* A signal was successfully delivered; the saved
|
* A signal was successfully delivered; the saved
|
||||||
* sigmask will have been stored in the signal frame,
|
* sigmask will have been stored in the signal frame,
|
||||||
|
@ -482,11 +471,28 @@ void do_signal(struct pt_regs *regs)
|
||||||
* Let tracing know that we've done the handler setup.
|
* Let tracing know that we've done the handler setup.
|
||||||
*/
|
*/
|
||||||
tracehook_signal_handler(signr, &info, &ka, regs,
|
tracehook_signal_handler(signr, &info, &ka, regs,
|
||||||
test_thread_flag(TIF_SINGLE_STEP));
|
test_thread_flag(TIF_SINGLE_STEP));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* No handlers present - check for system call restart */
|
||||||
|
if (regs->svc_code > 0) {
|
||||||
|
switch (regs->gprs[2]) {
|
||||||
|
case -ERESTART_RESTARTBLOCK:
|
||||||
|
/* Restart with sys_restart_syscall */
|
||||||
|
regs->svc_code = __NR_restart_syscall;
|
||||||
|
/* fallthrough */
|
||||||
|
case -ERESTARTNOHAND:
|
||||||
|
case -ERESTARTSYS:
|
||||||
|
case -ERESTARTNOINTR:
|
||||||
|
/* Restart system call with magic TIF bit. */
|
||||||
|
regs->gprs[2] = regs->orig_gpr2;
|
||||||
|
set_thread_flag(TIF_RESTART_SVC);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there's no signal to deliver, we just put the saved sigmask back.
|
* If there's no signal to deliver, we just put the saved sigmask back.
|
||||||
*/
|
*/
|
||||||
|
@ -494,13 +500,6 @@ void do_signal(struct pt_regs *regs)
|
||||||
clear_thread_flag(TIF_RESTORE_SIGMASK);
|
clear_thread_flag(TIF_RESTORE_SIGMASK);
|
||||||
sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
|
sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restart a different system call. */
|
|
||||||
if (retval == -ERESTART_RESTARTBLOCK
|
|
||||||
&& regs->psw.addr == continue_addr) {
|
|
||||||
regs->gprs[2] = __NR_restart_syscall;
|
|
||||||
set_thread_flag(TIF_RESTART_SVC);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_notify_resume(struct pt_regs *regs)
|
void do_notify_resume(struct pt_regs *regs)
|
||||||
|
|
|
@ -395,6 +395,7 @@ typedef struct elf64_shdr {
|
||||||
#define NT_S390_CTRS 0x304 /* s390 control registers */
|
#define NT_S390_CTRS 0x304 /* s390 control registers */
|
||||||
#define NT_S390_PREFIX 0x305 /* s390 prefix register */
|
#define NT_S390_PREFIX 0x305 /* s390 prefix register */
|
||||||
#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */
|
#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */
|
||||||
|
#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */
|
||||||
#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */
|
#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue