Commit 1a914247 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: signals & exceptions

From: Martin Schwidefsky <schwidefsky@de.ibm.com>
From: Ulrich Weigand <uweigand@de.ibm.com>

s390 core changes:
 - Add signo between signal frame and the signal return instruction on the
   user stack for backtrace over signal handlers.
 - Add hfp floating point exceptions.
 - Use a single function for region, segment and page translation exceptions.
 - Discard SIGTRAP for single stepped instructions if the trapping instruction
   is repeated (normal memory faults) or if another signal is delivered anyway.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent d9ca90fb
......@@ -40,6 +40,7 @@ typedef struct
__u8 callee_used_stack[__SIGNAL_FRAMESIZE32];
struct sigcontext32 sc;
_sigregs32 sregs;
int signo;
__u8 retcode[S390_SYSCALL_SIZE];
} sigframe32;
......@@ -497,6 +498,10 @@ static void setup_frame32(int sig, struct k_sigaction *ka,
To avoid breaking binary compatibility, they are passed as args. */
regs->gprs[4] = current->thread.trap_no;
regs->gprs[5] = current->thread.prot_addr;
/* Place signal number on stack to allow backtrace from handler. */
if (__put_user(regs->gprs[2], (int __user *) &frame->signo))
goto give_sigsegv;
return;
give_sigsegv:
......
......@@ -70,7 +70,7 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NEED_RESCHED)
.macro CLEANUP_SAVE_ALL_BASE psworg,savearea,sync
l %r1,SP_PSW+4(%r15)
cli 1(%r1),0xcf
jne 0f
bne BASED(0f)
mvc \savearea(16),SP_R12(%r15)
0: st %r13,SP_R13(%r15)
.endm
......@@ -140,9 +140,9 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NEED_RESCHED)
.macro CLEANUP_RESTORE_ALL
l %r1,SP_PSW+4(%r15)
cli 0(%r1),0x82
jne 0f
bne BASED(0f)
mvc SP_PSW(8,%r15),__LC_RETURN_PSW
j 1f
b BASED(1f)
0: l %r1,SP_R15(%r15)
mvc SP_PSW(8,%r15),SP_PSW(%r1)
mvc SP_R0(64,%r15),SP_R0(%r1)
......@@ -157,9 +157,9 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NEED_RESCHED)
tm SP_PSW+1(%r15),0x01 # test problem state bit
bnz BASED(0f) # from user -> not critical
clc SP_PSW+4(4,%r15),BASED(.Lcritical_end)
jnl 0f
bnl BASED(0f)
clc SP_PSW+4(4,%r15),BASED(.Lcritical_start)
jl 0f
bl BASED(0f)
l %r1,BASED(.Lcleanup_critical)
basr %r14,%r1
0:
......@@ -291,6 +291,7 @@ sysc_reschedule:
# _TIF_SIGPENDING is set, call do_signal
#
sysc_sigpending:
ni __TI_flags+3(%r9),255-_TIF_SINGLE_STEP # clear TIF_SINGLE_STEP
la %r2,SP_PTREGS(%r15) # load pt_regs
sr %r3,%r3 # clear *oldset
l %r1,BASED(.Ldo_signal)
......@@ -311,7 +312,7 @@ sysc_restart:
b BASED(sysc_do_restart) # restart svc
#
# _TIF_SINGLE_STEP is set, call do_debugger_trap
# _TIF_SINGLE_STEP is set, call do_single_step
#
sysc_singlestep:
ni __TI_flags+3(%r9),255-_TIF_SINGLE_STEP # clear TIF_SINGLE_STEP
......@@ -319,7 +320,7 @@ sysc_singlestep:
la %r2,SP_PTREGS(%r15) # address of register-save area
l %r1,BASED(.Lhandle_per) # load adr. of per handler
la %r14,BASED(sysc_return) # load adr. of system return
br %r1 # branch to do_debugger_trap
br %r1 # branch to do_single_step
__critical_end:
......@@ -460,8 +461,9 @@ pgm_check_handler:
SAVE_ALL __LC_PGM_OLD_PSW,__LC_SAVE_AREA,1
l %r3,__LC_PGM_ILC # load program interruption code
la %r8,0x7f
l %r7,BASED(.Ljump_table)
nr %r8,%r3
pgm_do_call:
l %r7,BASED(.Ljump_table)
sll %r8,2
GET_THREAD_INFO
l %r7,0(%r8,%r7) # load address of handler routine
......@@ -492,20 +494,12 @@ pgm_per_std:
mvc __THREAD_per+__PER_atmid(2,%r1),__LC_PER_ATMID
mvc __THREAD_per+__PER_address(4,%r1),__LC_PER_ADDRESS
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
la %r4,0x7f
oi __TI_flags+3(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP
l %r3,__LC_PGM_ILC # load program interruption code
nr %r4,%r3 # clear per-event-bit and ilc
be BASED(pgm_per_only) # only per or per+check ?
l %r1,BASED(.Ljump_table)
sll %r4,2
l %r1,0(%r4,%r1) # load address of handler routine
la %r2,SP_PTREGS(%r15) # address of register-save area
basr %r14,%r1 # branch to interrupt-handler
pgm_per_only:
la %r2,SP_PTREGS(15) # address of register-save area
l %r1,BASED(.Lhandle_per) # load adr. of per handler
la %r14,BASED(sysc_return) # load adr. of system return
br %r1 # branch to do_debugger_trap
la %r8,0x7f
nr %r8,%r3 # clear per-event-bit and ilc
be BASED(sysc_return) # only per or per+check ?
b BASED(pgm_do_call)
#
# it was a single stepped SVC that is causing all the trouble
......@@ -745,7 +739,7 @@ cleanup_sysc_leave:
.Ldo_extint: .long do_extint
.Ldo_signal: .long do_signal
.Ldo_softirq: .long do_softirq
.Lhandle_per: .long do_debugger_trap
.Lhandle_per: .long do_single_step
.Ljump_table: .long pgm_check_table
.Lschedule: .long schedule
.Lclone: .long sys_clone
......
......@@ -282,6 +282,7 @@ sysc_reschedule:
# _TIF_SIGPENDING is set, call do_signal
#
sysc_sigpending:
ni __TI_flags+7(%r9),255-_TIF_SINGLE_STEP # clear TIF_SINGLE_STEP
la %r2,SP_PTREGS(%r15) # load pt_regs
sgr %r3,%r3 # clear *oldset
brasl %r14,do_signal # call do_signal
......@@ -301,7 +302,7 @@ sysc_restart:
j sysc_do_restart # restart svc
#
# _TIF_SINGLE_STEP is set, call do_debugger_trap
# _TIF_SINGLE_STEP is set, call do_single_step
#
sysc_singlestep:
ni __TI_flags+7(%r9),255-_TIF_SINGLE_STEP # clear TIF_SINGLE_STEP
......@@ -309,7 +310,7 @@ sysc_singlestep:
sth %r0,SP_TRAP(%r15) # set trap indication to pgm check
la %r2,SP_PTREGS(%r15) # address of register-save area
larl %r14,sysc_return # load adr. of system return
jg do_debugger_trap # branch to do_debugger_trap
jg do_single_step # branch to do_sigtrap
__critical_end:
......@@ -497,6 +498,7 @@ pgm_check_handler:
lgf %r3,__LC_PGM_ILC # load program interruption code
lghi %r8,0x7f
ngr %r8,%r3
pgm_do_call:
sll %r8,3
GET_THREAD_INFO
larl %r1,pgm_check_table
......@@ -527,19 +529,12 @@ pgm_per_std:
mvc __THREAD_per+__PER_atmid(2,%r1),__LC_PER_ATMID
mvc __THREAD_per+__PER_address(8,%r1),__LC_PER_ADDRESS
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
lghi %r4,0x7f
oi __TI_flags+7(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP
lgf %r3,__LC_PGM_ILC # load program interruption code
nr %r4,%r3 # clear per-event-bit and ilc
je pgm_per_only # only per of per+check ?
sll %r4,3
larl %r1,pgm_check_table
lg %r1,0(%r4,%r1) # load address of handler routine
la %r2,SP_PTREGS(%r15) # address of register-save area
basr %r14,%r1 # branch to interrupt-handler
pgm_per_only:
la %r2,SP_PTREGS(15) # address of register-save area
larl %r14,sysc_return # load adr. of system return
jg do_debugger_trap
lghi %r8,0x7f
ngr %r8,%r3 # clear per-event-bit and ilc
je sysc_return
j pgm_do_call
#
# it was a single stepped SVC that is causing all the trouble
......
......@@ -39,6 +39,7 @@ typedef struct
__u8 callee_used_stack[__SIGNAL_FRAMESIZE];
struct sigcontext sc;
_sigregs sregs;
int signo;
__u8 retcode[S390_SYSCALL_SIZE];
} sigframe;
......@@ -350,6 +351,10 @@ static void setup_frame(int sig, struct k_sigaction *ka,
To avoid breaking binary compatibility, they are passed as args. */
regs->gprs[4] = current->thread.trap_no;
regs->gprs[5] = current->thread.prot_addr;
/* Place signal number on stack to allow backtrace from handler. */
if (__put_user(regs->gprs[2], (int __user *) &frame->signo))
goto give_sigsegv;
return;
give_sigsegv:
......
......@@ -54,9 +54,7 @@ int sysctl_userprocess_debug = 0;
#endif
extern pgm_check_handler_t do_protection_exception;
extern pgm_check_handler_t do_segment_exception;
extern pgm_check_handler_t do_region_exception;
extern pgm_check_handler_t do_page_exception;
extern pgm_check_handler_t do_dat_exception;
extern pgm_check_handler_t do_pseudo_page_fault;
#ifdef CONFIG_PFAULT
extern int pfault_init(void);
......@@ -300,14 +298,10 @@ static inline void *get_check_address(struct pt_regs *regs)
return (void *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN);
}
int do_debugger_trap(struct pt_regs *regs)
void do_single_step(struct pt_regs *regs)
{
if ((regs->psw.mask & PSW_MASK_PSTATE) &&
(current->ptrace & PT_PTRACED)) {
force_sig(SIGTRAP,current);
return 0;
}
return 1;
if ((current->ptrace & PT_PTRACED) != 0)
force_sig(SIGTRAP, current);
}
#define DO_ERROR(signr, str, name) \
......@@ -329,12 +323,24 @@ asmlinkage void name(struct pt_regs * regs, long interruption_code) \
DO_ERROR(SIGSEGV, "Unknown program exception", default_trap_handler)
DO_ERROR_INFO(SIGBUS, "addressing exception", addressing_exception,
BUS_ADRERR, get_check_address(regs))
DO_ERROR_INFO(SIGILL, "addressing exception", addressing_exception,
ILL_ILLADR, get_check_address(regs))
DO_ERROR_INFO(SIGILL, "execute exception", execute_exception,
ILL_ILLOPN, get_check_address(regs))
DO_ERROR_INFO(SIGFPE, "fixpoint divide exception", divide_exception,
FPE_INTDIV, get_check_address(regs))
DO_ERROR_INFO(SIGFPE, "fixpoint overflow exception", overflow_exception,
FPE_INTOVF, get_check_address(regs))
DO_ERROR_INFO(SIGFPE, "HFP overflow exception", hfp_overflow_exception,
FPE_FLTOVF, get_check_address(regs))
DO_ERROR_INFO(SIGFPE, "HFP underflow exception", hfp_underflow_exception,
FPE_FLTUND, get_check_address(regs))
DO_ERROR_INFO(SIGFPE, "HFP significance exception", hfp_significance_exception,
FPE_FLTRES, get_check_address(regs))
DO_ERROR_INFO(SIGFPE, "HFP divide exception", hfp_divide_exception,
FPE_FLTDIV, get_check_address(regs))
DO_ERROR_INFO(SIGFPE, "HFP square root exception", hfp_sqrt_exception,
FPE_FLTINV, get_check_address(regs))
DO_ERROR_INFO(SIGILL, "operand exception", operand_exception,
ILL_ILLOPN, get_check_address(regs))
DO_ERROR_INFO(SIGILL, "privileged operation", privileged_op,
......@@ -388,19 +394,15 @@ asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code)
if (regs->psw.mask & PSW_MASK_PSTATE)
local_irq_enable();
if (regs->psw.mask & PSW_MASK_PSTATE)
get_user(*((__u16 *) opcode), (__u16 __user *)location);
else
*((__u16 *)opcode)=*((__u16 *)location);
if (*((__u16 *)opcode)==S390_BREAKPOINT_U16)
{
if(do_debugger_trap(regs))
signal = SIGILL;
}
if (regs->psw.mask & PSW_MASK_PSTATE) {
get_user(*((__u16 *) opcode), (__u16 __user *) location);
if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
if (current->ptrace & PT_PTRACED)
force_sig(SIGTRAP, current);
else
signal = SIGILL;
#ifdef CONFIG_MATHEMU
else if (regs->psw.mask & PSW_MASK_PSTATE)
{
if (opcode[0] == 0xb3) {
} else if (opcode[0] == 0xb3) {
get_user(*((__u16 *) (opcode+2)), location+1);
signal = math_emu_b3(opcode, regs);
} else if (opcode[0] == 0xed) {
......@@ -416,12 +418,12 @@ asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code)
} else if (*((__u16 *) opcode) == 0xb29d) {
get_user(*((__u16 *) (opcode+2)), location+1);
signal = math_emu_lfpc(opcode, regs);
#endif
} else
signal = SIGILL;
}
#endif
else
} else
signal = SIGILL;
if (signal == SIGFPE)
do_fp_trap(regs, location,
current->thread.fp_regs.fpc, interruption_code);
......@@ -445,9 +447,9 @@ specification_exception(struct pt_regs * regs, long interruption_code)
* We got all needed information from the lowcore and can
* now safely switch on interrupts.
*/
if (regs->psw.mask & PSW_MASK_PSTATE)
if (regs->psw.mask & PSW_MASK_PSTATE)
local_irq_enable();
if (regs->psw.mask & PSW_MASK_PSTATE) {
get_user(*((__u16 *) opcode), location);
switch (opcode[0]) {
......@@ -479,6 +481,7 @@ specification_exception(struct pt_regs * regs, long interruption_code)
}
} else
signal = SIGILL;
if (signal == SIGFPE)
do_fp_trap(regs, location,
current->thread.fp_regs.fpc, interruption_code);
......@@ -605,19 +608,29 @@ void __init trap_init(void)
pgm_check_table[5] = &addressing_exception;
pgm_check_table[6] = &specification_exception;
pgm_check_table[7] = &data_exception;
pgm_check_table[8] = &overflow_exception;
pgm_check_table[9] = &divide_exception;
pgm_check_table[0x10] = &do_segment_exception;
pgm_check_table[0x11] = &do_page_exception;
pgm_check_table[0x0A] = &overflow_exception;
pgm_check_table[0x0B] = &divide_exception;
pgm_check_table[0x0C] = &hfp_overflow_exception;
pgm_check_table[0x0D] = &hfp_underflow_exception;
pgm_check_table[0x0E] = &hfp_significance_exception;
pgm_check_table[0x0F] = &hfp_divide_exception;
pgm_check_table[0x10] = &do_dat_exception;
pgm_check_table[0x11] = &do_dat_exception;
pgm_check_table[0x12] = &translation_exception;
pgm_check_table[0x13] = &special_op_exception;
#ifndef CONFIG_ARCH_S390X
pgm_check_table[0x14] = &do_pseudo_page_fault;
#else /* CONFIG_ARCH_S390X */
pgm_check_table[0x38] = &addressing_exception;
pgm_check_table[0x3B] = &do_region_exception;
pgm_check_table[0x38] = &do_dat_exception;
pgm_check_table[0x39] = &do_dat_exception;
pgm_check_table[0x3A] = &do_dat_exception;
pgm_check_table[0x3B] = &do_dat_exception;
#endif /* CONFIG_ARCH_S390X */
pgm_check_table[0x15] = &operand_exception;
pgm_check_table[0x1C] = &privileged_op;
pgm_check_table[0x1D] = &hfp_sqrt_exception;
pgm_check_table[0x40] = &do_monitor_call;
if (MACHINE_IS_VM) {
......
......@@ -159,7 +159,8 @@ static void force_sigsegv(struct pt_regs *regs, unsigned long error_code,
* 11 Page translation -> Not present (nullification)
* 3b Region third trans. -> Not present (nullification)
*/
extern inline void do_exception(struct pt_regs *regs, unsigned long error_code)
extern inline void
do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection)
{
struct task_struct *tsk;
struct mm_struct *mm;
......@@ -177,7 +178,7 @@ extern inline void do_exception(struct pt_regs *regs, unsigned long error_code)
* as a special case because the translation exception code
* field is not guaranteed to contain valid data in this case.
*/
if (error_code == 4 && !(S390_lowcore.trans_exc_code & 4)) {
if (is_protection && !(S390_lowcore.trans_exc_code & 4)) {
/* Low-address protection hit in kernel mode means
NULL pointer write access in kernel mode. */
......@@ -232,7 +233,7 @@ extern inline void do_exception(struct pt_regs *regs, unsigned long error_code)
*/
good_area:
si_code = SEGV_ACCERR;
if (error_code != 4) {
if (!is_protection) {
/* page not present, check vm flags */
if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
goto bad_area;
......@@ -247,7 +248,7 @@ extern inline void do_exception(struct pt_regs *regs, unsigned long error_code)
* make sure we exit gracefully rather than endlessly redo
* the fault.
*/
switch (handle_mm_fault(mm, vma, address, error_code == 4)) {
switch (handle_mm_fault(mm, vma, address, is_protection)) {
case VM_FAULT_MINOR:
tsk->min_flt++;
break;
......@@ -263,6 +264,11 @@ extern inline void do_exception(struct pt_regs *regs, unsigned long error_code)
}
up_read(&mm->mmap_sem);
/*
* The instruction that caused the program check will
* be repeated. Don't signal single step via SIGTRAP.
*/
clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
return;
/*
......@@ -337,28 +343,15 @@ extern inline void do_exception(struct pt_regs *regs, unsigned long error_code)
void do_protection_exception(struct pt_regs *regs, unsigned long error_code)
{
regs->psw.addr -= (error_code >> 16);
do_exception(regs, 4);
do_exception(regs, 4, 1);
}
void do_segment_exception(struct pt_regs *regs, unsigned long error_code)
void do_dat_exception(struct pt_regs *regs, unsigned long error_code)
{
do_exception(regs, 0x10);
do_exception(regs, error_code & 0xff, 0);
}
void do_page_exception(struct pt_regs *regs, unsigned long error_code)
{
do_exception(regs, 0x11);
}
#ifdef CONFIG_ARCH_S390X
void
do_region_exception(struct pt_regs *regs, unsigned long error_code)
{
do_exception(regs, 0x3b);
}
#else /* CONFIG_ARCH_S390X */
#ifndef CONFIG_ARCH_S390X
typedef struct _pseudo_wait_t {
struct _pseudo_wait_t *next;
......@@ -456,6 +449,11 @@ do_pseudo_page_fault(struct pt_regs *regs, unsigned long error_code)
wait_struct.next = pseudo_lock_queue;
pseudo_lock_queue = &wait_struct;
spin_unlock(&pseudo_wait_spinlock);
/*
* The instruction that caused the program check will
* be repeated. Don't signal single step via SIGTRAP.
*/
clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
/* go to sleep */
wait_event(wait_struct.queue, wait_struct.resolved);
}
......
......@@ -85,7 +85,7 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
#define TIF_RESTART_SVC 4 /* restart svc with new svc number */
#define TIF_SYSCALL_AUDIT 5 /* syscall auditing active */
#define TIF_SINGLE_STEP 6 /* single stepped svc */
#define TIF_SINGLE_STEP 6 /* deliver sigtrap on return to user */
#define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */
#define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling
TIF_NEED_RESCHED */
......
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