csky: Reconstruct signal processing
Linux kernel has provided some apis for arch signal's implementation. For example: restore_saved_sigmask() set_current_blocked() restore_altstack() But in last version of csky signal.c didn't use them and some codes are confusing, so reconstruct signal.c with reference to riscv's code. Now csky signal.c implementation are very close to riscv and we can get the following benefits: - Clear code structure - The signal code of riscv and csky can be reviewed together - Promoting the unification of arch's signal implementation Also modified the related code in entry.S Signed-off-by: Guo Ren <ren_guo@c-sky.com> Cc: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Родитель
f4625ee0e4
Коммит
bf24168293
|
@ -16,9 +16,6 @@
|
||||||
#define LSAVE_A4 40
|
#define LSAVE_A4 40
|
||||||
#define LSAVE_A5 44
|
#define LSAVE_A5 44
|
||||||
|
|
||||||
#define EPC_INCREASE 2
|
|
||||||
#define EPC_KEEP 0
|
|
||||||
|
|
||||||
.macro USPTOKSP
|
.macro USPTOKSP
|
||||||
mtcr sp, ss1
|
mtcr sp, ss1
|
||||||
mfcr sp, ss0
|
mfcr sp, ss0
|
||||||
|
@ -29,10 +26,6 @@
|
||||||
mfcr sp, ss1
|
mfcr sp, ss1
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro INCTRAP rx
|
|
||||||
addi \rx, EPC_INCREASE
|
|
||||||
.endm
|
|
||||||
|
|
||||||
.macro SAVE_ALL epc_inc
|
.macro SAVE_ALL epc_inc
|
||||||
mtcr r13, ss2
|
mtcr r13, ss2
|
||||||
mfcr r13, epsr
|
mfcr r13, epsr
|
||||||
|
|
|
@ -21,4 +21,6 @@
|
||||||
|
|
||||||
#define SYSTRACE_SAVENUM 2
|
#define SYSTRACE_SAVENUM 2
|
||||||
|
|
||||||
|
#define TRAP0_SIZE 2
|
||||||
|
|
||||||
#endif /* __ASM_CSKY_REGDEF_H */
|
#endif /* __ASM_CSKY_REGDEF_H */
|
||||||
|
|
|
@ -14,18 +14,11 @@
|
||||||
#define LSAVE_A2 32
|
#define LSAVE_A2 32
|
||||||
#define LSAVE_A3 36
|
#define LSAVE_A3 36
|
||||||
|
|
||||||
#define EPC_INCREASE 4
|
|
||||||
#define EPC_KEEP 0
|
|
||||||
|
|
||||||
#define KSPTOUSP
|
#define KSPTOUSP
|
||||||
#define USPTOKSP
|
#define USPTOKSP
|
||||||
|
|
||||||
#define usp cr<14, 1>
|
#define usp cr<14, 1>
|
||||||
|
|
||||||
.macro INCTRAP rx
|
|
||||||
addi \rx, EPC_INCREASE
|
|
||||||
.endm
|
|
||||||
|
|
||||||
.macro SAVE_ALL epc_inc
|
.macro SAVE_ALL epc_inc
|
||||||
subi sp, 152
|
subi sp, 152
|
||||||
stw tls, (sp, 0)
|
stw tls, (sp, 0)
|
||||||
|
|
|
@ -21,4 +21,6 @@
|
||||||
|
|
||||||
#define SYSTRACE_SAVENUM 5
|
#define SYSTRACE_SAVENUM 5
|
||||||
|
|
||||||
|
#define TRAP0_SIZE 4
|
||||||
|
|
||||||
#endif /* __ASM_CSKY_REGDEF_H */
|
#endif /* __ASM_CSKY_REGDEF_H */
|
||||||
|
|
|
@ -12,11 +12,10 @@
|
||||||
* If *ptr != oldval && return 1,
|
* If *ptr != oldval && return 1,
|
||||||
* else *ptr = newval return 0.
|
* else *ptr = newval return 0.
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_CPU_HAS_LDSTEX
|
|
||||||
ENTRY(csky_cmpxchg)
|
ENTRY(csky_cmpxchg)
|
||||||
USPTOKSP
|
USPTOKSP
|
||||||
mfcr a3, epc
|
mfcr a3, epc
|
||||||
INCTRAP a3
|
addi a3, TRAP0_SIZE
|
||||||
|
|
||||||
subi sp, 8
|
subi sp, 8
|
||||||
stw a3, (sp, 0)
|
stw a3, (sp, 0)
|
||||||
|
@ -24,6 +23,7 @@ ENTRY(csky_cmpxchg)
|
||||||
stw a3, (sp, 4)
|
stw a3, (sp, 4)
|
||||||
|
|
||||||
psrset ee
|
psrset ee
|
||||||
|
#ifdef CONFIG_CPU_HAS_LDSTEX
|
||||||
1:
|
1:
|
||||||
ldex a3, (a2)
|
ldex a3, (a2)
|
||||||
cmpne a0, a3
|
cmpne a0, a3
|
||||||
|
@ -33,27 +33,7 @@ ENTRY(csky_cmpxchg)
|
||||||
bez a3, 1b
|
bez a3, 1b
|
||||||
2:
|
2:
|
||||||
sync.is
|
sync.is
|
||||||
mvc a0
|
|
||||||
ldw a3, (sp, 0)
|
|
||||||
mtcr a3, epc
|
|
||||||
ldw a3, (sp, 4)
|
|
||||||
mtcr a3, epsr
|
|
||||||
addi sp, 8
|
|
||||||
KSPTOUSP
|
|
||||||
rte
|
|
||||||
END(csky_cmpxchg)
|
|
||||||
#else
|
#else
|
||||||
ENTRY(csky_cmpxchg)
|
|
||||||
USPTOKSP
|
|
||||||
mfcr a3, epc
|
|
||||||
INCTRAP a3
|
|
||||||
|
|
||||||
subi sp, 8
|
|
||||||
stw a3, (sp, 0)
|
|
||||||
mfcr a3, epsr
|
|
||||||
stw a3, (sp, 4)
|
|
||||||
|
|
||||||
psrset ee
|
|
||||||
1:
|
1:
|
||||||
ldw a3, (a2)
|
ldw a3, (a2)
|
||||||
cmpne a0, a3
|
cmpne a0, a3
|
||||||
|
@ -61,6 +41,7 @@ ENTRY(csky_cmpxchg)
|
||||||
2:
|
2:
|
||||||
stw a1, (a2)
|
stw a1, (a2)
|
||||||
3:
|
3:
|
||||||
|
#endif
|
||||||
mvc a0
|
mvc a0
|
||||||
ldw a3, (sp, 0)
|
ldw a3, (sp, 0)
|
||||||
mtcr a3, epc
|
mtcr a3, epc
|
||||||
|
@ -71,6 +52,7 @@ ENTRY(csky_cmpxchg)
|
||||||
rte
|
rte
|
||||||
END(csky_cmpxchg)
|
END(csky_cmpxchg)
|
||||||
|
|
||||||
|
#ifndef CONFIG_CPU_HAS_LDSTEX
|
||||||
/*
|
/*
|
||||||
* Called from tlbmodified exception
|
* Called from tlbmodified exception
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -91,7 +91,7 @@ ENTRY(csky_\name)
|
||||||
mfcr a3, ss2
|
mfcr a3, ss2
|
||||||
mfcr r6, ss3
|
mfcr r6, ss3
|
||||||
mfcr a2, ss4
|
mfcr a2, ss4
|
||||||
SAVE_ALL EPC_KEEP
|
SAVE_ALL 0
|
||||||
.endm
|
.endm
|
||||||
.macro tlbop_end is_write
|
.macro tlbop_end is_write
|
||||||
RD_MEH a2
|
RD_MEH a2
|
||||||
|
@ -117,7 +117,7 @@ jbsr csky_cmpxchg_fixup
|
||||||
tlbop_end 1
|
tlbop_end 1
|
||||||
|
|
||||||
ENTRY(csky_systemcall)
|
ENTRY(csky_systemcall)
|
||||||
SAVE_ALL EPC_INCREASE
|
SAVE_ALL TRAP0_SIZE
|
||||||
|
|
||||||
psrset ee, ie
|
psrset ee, ie
|
||||||
|
|
||||||
|
@ -190,11 +190,9 @@ ENTRY(ret_from_fork)
|
||||||
ldw r8, (r9, TINFO_FLAGS)
|
ldw r8, (r9, TINFO_FLAGS)
|
||||||
ANDI_R3 r8, (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT)
|
ANDI_R3 r8, (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT)
|
||||||
cmpnei r8, 0
|
cmpnei r8, 0
|
||||||
bf 3f
|
bf ret_from_exception
|
||||||
mov a0, sp /* sp = pt_regs pointer */
|
mov a0, sp /* sp = pt_regs pointer */
|
||||||
jbsr syscall_trace_exit
|
jbsr syscall_trace_exit
|
||||||
3:
|
|
||||||
jbsr ret_from_exception
|
|
||||||
|
|
||||||
ret_from_exception:
|
ret_from_exception:
|
||||||
ld syscallid, (sp, LSAVE_PSR)
|
ld syscallid, (sp, LSAVE_PSR)
|
||||||
|
@ -209,34 +207,29 @@ ret_from_exception:
|
||||||
bmaski r10, THREAD_SHIFT
|
bmaski r10, THREAD_SHIFT
|
||||||
andn r9, r10
|
andn r9, r10
|
||||||
|
|
||||||
resume_userspace:
|
|
||||||
ldw r8, (r9, TINFO_FLAGS)
|
ldw r8, (r9, TINFO_FLAGS)
|
||||||
andi r8, (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED)
|
andi r8, (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED)
|
||||||
cmpnei r8, 0
|
cmpnei r8, 0
|
||||||
bt exit_work
|
bt exit_work
|
||||||
1: RESTORE_ALL
|
1:
|
||||||
|
RESTORE_ALL
|
||||||
|
|
||||||
exit_work:
|
exit_work:
|
||||||
|
lrw syscallid, ret_from_exception
|
||||||
|
mov lr, syscallid
|
||||||
|
|
||||||
btsti r8, TIF_NEED_RESCHED
|
btsti r8, TIF_NEED_RESCHED
|
||||||
bt work_resched
|
bt work_resched
|
||||||
/* If thread_info->flag is empty, RESTORE_ALL */
|
|
||||||
cmpnei r8, 0
|
mov a0, sp
|
||||||
bf 1b
|
mov a1, r8
|
||||||
mov a0, r8
|
jmpi do_notify_resume
|
||||||
mov a1, sp
|
|
||||||
jbsr do_notify_resume /* do signals */
|
|
||||||
br resume_userspace
|
|
||||||
|
|
||||||
work_resched:
|
work_resched:
|
||||||
lrw syscallid, ret_from_exception
|
|
||||||
mov r15, syscallid /* Return address in link */
|
|
||||||
jmpi schedule
|
jmpi schedule
|
||||||
|
|
||||||
ENTRY(sys_rt_sigreturn)
|
|
||||||
jmpi do_rt_sigreturn
|
|
||||||
|
|
||||||
ENTRY(csky_trap)
|
ENTRY(csky_trap)
|
||||||
SAVE_ALL EPC_KEEP
|
SAVE_ALL 0
|
||||||
psrset ee
|
psrset ee
|
||||||
mov a0, sp /* Push Stack pointer arg */
|
mov a0, sp /* Push Stack pointer arg */
|
||||||
jbsr trap_c /* Call C-level trap handler */
|
jbsr trap_c /* Call C-level trap handler */
|
||||||
|
@ -252,7 +245,7 @@ ENTRY(csky_get_tls)
|
||||||
|
|
||||||
/* increase epc for continue */
|
/* increase epc for continue */
|
||||||
mfcr a0, epc
|
mfcr a0, epc
|
||||||
INCTRAP a0
|
addi a0, TRAP0_SIZE
|
||||||
mtcr a0, epc
|
mtcr a0, epc
|
||||||
|
|
||||||
/* get current task thread_info with kernel 8K stack */
|
/* get current task thread_info with kernel 8K stack */
|
||||||
|
@ -269,7 +262,7 @@ ENTRY(csky_get_tls)
|
||||||
rte
|
rte
|
||||||
|
|
||||||
ENTRY(csky_irq)
|
ENTRY(csky_irq)
|
||||||
SAVE_ALL EPC_KEEP
|
SAVE_ALL 0
|
||||||
psrset ee
|
psrset ee
|
||||||
|
|
||||||
#ifdef CONFIG_PREEMPT
|
#ifdef CONFIG_PREEMPT
|
||||||
|
|
|
@ -1,26 +1,10 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
|
|
||||||
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/mm.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/signal.h>
|
#include <linux/signal.h>
|
||||||
#include <linux/syscalls.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/wait.h>
|
|
||||||
#include <linux/ptrace.h>
|
|
||||||
#include <linux/unistd.h>
|
|
||||||
#include <linux/stddef.h>
|
|
||||||
#include <linux/highuid.h>
|
|
||||||
#include <linux/personality.h>
|
|
||||||
#include <linux/tty.h>
|
|
||||||
#include <linux/binfmts.h>
|
|
||||||
#include <linux/tracehook.h>
|
|
||||||
#include <linux/freezer.h>
|
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/syscalls.h>
|
||||||
|
#include <linux/tracehook.h>
|
||||||
|
|
||||||
#include <asm/setup.h>
|
|
||||||
#include <asm/pgtable.h>
|
|
||||||
#include <asm/traps.h>
|
#include <asm/traps.h>
|
||||||
#include <asm/ucontext.h>
|
#include <asm/ucontext.h>
|
||||||
#include <asm/vdso.h>
|
#include <asm/vdso.h>
|
||||||
|
@ -29,110 +13,117 @@
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_HAS_FPU
|
#ifdef CONFIG_CPU_HAS_FPU
|
||||||
#include <abi/fpu.h>
|
#include <abi/fpu.h>
|
||||||
|
static int restore_fpu_state(struct sigcontext __user *sc)
|
||||||
static int restore_fpu_state(struct sigcontext *sc)
|
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct user_fp user_fp;
|
struct user_fp user_fp;
|
||||||
|
|
||||||
err = copy_from_user(&user_fp, &sc->sc_user_fp, sizeof(user_fp));
|
err = __copy_from_user(&user_fp, &sc->sc_user_fp, sizeof(user_fp));
|
||||||
|
|
||||||
restore_from_user_fp(&user_fp);
|
restore_from_user_fp(&user_fp);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int save_fpu_state(struct sigcontext *sc)
|
static int save_fpu_state(struct sigcontext __user *sc)
|
||||||
{
|
{
|
||||||
struct user_fp user_fp;
|
struct user_fp user_fp;
|
||||||
|
|
||||||
save_to_user_fp(&user_fp);
|
save_to_user_fp(&user_fp);
|
||||||
|
|
||||||
return copy_to_user(&sc->sc_user_fp, &user_fp, sizeof(user_fp));
|
return __copy_to_user(&sc->sc_user_fp, &user_fp, sizeof(user_fp));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline int restore_fpu_state(struct sigcontext *sc) { return 0; }
|
#define restore_fpu_state(sigcontext) (0)
|
||||||
static inline int save_fpu_state(struct sigcontext *sc) { return 0; }
|
#define save_fpu_state(sigcontext) (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct rt_sigframe {
|
struct rt_sigframe {
|
||||||
int sig;
|
|
||||||
struct siginfo *pinfo;
|
|
||||||
void *puc;
|
|
||||||
struct siginfo info;
|
struct siginfo info;
|
||||||
struct ucontext uc;
|
struct ucontext uc;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static long restore_sigcontext(struct pt_regs *regs,
|
||||||
restore_sigframe(struct pt_regs *regs,
|
struct sigcontext __user *sc)
|
||||||
struct sigcontext *sc, int *pr2)
|
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
/* Always make any pending restarted system calls return -EINTR */
|
/* sc_pt_regs is structured the same as the start of pt_regs */
|
||||||
current_thread_info()->task->restart_block.fn = do_no_restart_syscall;
|
err |= __copy_from_user(regs, &sc->sc_pt_regs, sizeof(struct pt_regs));
|
||||||
|
|
||||||
err |= copy_from_user(regs, &sc->sc_pt_regs, sizeof(struct pt_regs));
|
|
||||||
|
|
||||||
|
/* Restore the floating-point state. */
|
||||||
err |= restore_fpu_state(sc);
|
err |= restore_fpu_state(sc);
|
||||||
|
|
||||||
*pr2 = regs->a0;
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
asmlinkage int
|
SYSCALL_DEFINE0(rt_sigreturn)
|
||||||
do_rt_sigreturn(void)
|
|
||||||
{
|
{
|
||||||
sigset_t set;
|
|
||||||
int a0;
|
|
||||||
struct pt_regs *regs = current_pt_regs();
|
struct pt_regs *regs = current_pt_regs();
|
||||||
struct rt_sigframe *frame = (struct rt_sigframe *)(regs->usp);
|
struct rt_sigframe __user *frame;
|
||||||
|
struct task_struct *task;
|
||||||
|
sigset_t set;
|
||||||
|
|
||||||
|
/* Always make any pending restarted system calls return -EINTR */
|
||||||
|
current->restart_block.fn = do_no_restart_syscall;
|
||||||
|
|
||||||
|
frame = (struct rt_sigframe __user *)regs->usp;
|
||||||
|
|
||||||
if (!access_ok(frame, sizeof(*frame)))
|
if (!access_ok(frame, sizeof(*frame)))
|
||||||
goto badframe;
|
goto badframe;
|
||||||
|
|
||||||
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
|
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
|
||||||
goto badframe;
|
goto badframe;
|
||||||
|
|
||||||
sigdelsetmask(&set, (sigmask(SIGKILL) | sigmask(SIGSTOP)));
|
set_current_blocked(&set);
|
||||||
spin_lock_irq(¤t->sighand->siglock);
|
|
||||||
current->blocked = set;
|
|
||||||
recalc_sigpending();
|
|
||||||
spin_unlock_irq(¤t->sighand->siglock);
|
|
||||||
|
|
||||||
if (restore_sigframe(regs, &frame->uc.uc_mcontext, &a0))
|
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
|
||||||
goto badframe;
|
goto badframe;
|
||||||
|
|
||||||
return a0;
|
if (restore_altstack(&frame->uc.uc_stack))
|
||||||
|
goto badframe;
|
||||||
|
|
||||||
|
return regs->a0;
|
||||||
|
|
||||||
badframe:
|
badframe:
|
||||||
force_sig(SIGSEGV, current);
|
task = current;
|
||||||
|
force_sig(SIGSEGV, task);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int setup_sigframe(struct sigcontext *sc, struct pt_regs *regs)
|
static int setup_sigcontext(struct rt_sigframe __user *frame,
|
||||||
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
struct sigcontext __user *sc = &frame->uc.uc_mcontext;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
err |= copy_to_user(&sc->sc_pt_regs, regs, sizeof(struct pt_regs));
|
err |= __copy_to_user(&sc->sc_pt_regs, regs, sizeof(struct pt_regs));
|
||||||
err |= save_fpu_state(sc);
|
err |= save_fpu_state(sc);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void *
|
static inline void __user *get_sigframe(struct ksignal *ksig,
|
||||||
get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size)
|
struct pt_regs *regs, size_t framesize)
|
||||||
{
|
{
|
||||||
unsigned long usp;
|
unsigned long sp;
|
||||||
|
/* Default to using normal stack */
|
||||||
|
sp = regs->usp;
|
||||||
|
|
||||||
/* Default to using normal stack. */
|
/*
|
||||||
usp = regs->usp;
|
* If we are on the alternate signal stack and would overflow it, don't.
|
||||||
|
* Return an always-bogus address instead so we will die with SIGSEGV.
|
||||||
|
*/
|
||||||
|
if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize)))
|
||||||
|
return (void __user __force *)(-1UL);
|
||||||
|
|
||||||
/* This is the X/Open sanctioned signal stack switching. */
|
/* This is the X/Open sanctioned signal stack switching. */
|
||||||
if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(usp)) {
|
sp = sigsp(sp, ksig) - framesize;
|
||||||
if (!on_sig_stack(usp))
|
|
||||||
usp = current->sas_ss_sp + current->sas_ss_size;
|
/* Align the stack frame. */
|
||||||
}
|
sp &= -8UL;
|
||||||
return (void *)((usp - frame_size) & -8UL);
|
|
||||||
|
return (void __user *)sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -140,208 +131,128 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct rt_sigframe *frame;
|
struct rt_sigframe *frame;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
struct csky_vdso *vdso = current->mm->context.vdso;
|
struct csky_vdso *vdso = current->mm->context.vdso;
|
||||||
|
|
||||||
frame = get_sigframe(&ksig->ka, regs, sizeof(*frame));
|
frame = get_sigframe(ksig, regs, sizeof(*frame));
|
||||||
if (!frame)
|
if (!access_ok(frame, sizeof(*frame)))
|
||||||
return 1;
|
return -EFAULT;
|
||||||
|
|
||||||
err |= __put_user(ksig->sig, &frame->sig);
|
|
||||||
err |= __put_user(&frame->info, &frame->pinfo);
|
|
||||||
err |= __put_user(&frame->uc, &frame->puc);
|
|
||||||
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
|
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
|
||||||
|
|
||||||
/* Create the ucontext. */
|
/* Create the ucontext. */
|
||||||
err |= __put_user(0, &frame->uc.uc_flags);
|
err |= __put_user(0, &frame->uc.uc_flags);
|
||||||
err |= __put_user(0, &frame->uc.uc_link);
|
err |= __put_user(NULL, &frame->uc.uc_link);
|
||||||
err |= __put_user((void *)current->sas_ss_sp,
|
err |= __save_altstack(&frame->uc.uc_stack, regs->usp);
|
||||||
&frame->uc.uc_stack.ss_sp);
|
err |= setup_sigcontext(frame, regs);
|
||||||
err |= __put_user(sas_ss_flags(regs->usp),
|
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
|
||||||
&frame->uc.uc_stack.ss_flags);
|
|
||||||
err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
|
|
||||||
err |= setup_sigframe(&frame->uc.uc_mcontext, regs);
|
|
||||||
err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
|
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
goto give_sigsegv;
|
return -EFAULT;
|
||||||
|
|
||||||
/* Set up registers for signal handler */
|
/* Set up to return from userspace. */
|
||||||
regs->usp = (unsigned long)frame;
|
regs->lr = (unsigned long)(vdso->rt_signal_retcode);
|
||||||
regs->pc = (unsigned long)ksig->ka.sa.sa_handler;
|
|
||||||
regs->lr = (unsigned long)vdso->rt_signal_retcode;
|
|
||||||
|
|
||||||
adjust_stack:
|
|
||||||
regs->a0 = ksig->sig; /* first arg is signo */
|
|
||||||
regs->a1 = (unsigned long)(&(frame->info));
|
|
||||||
regs->a2 = (unsigned long)(&(frame->uc));
|
|
||||||
return err;
|
|
||||||
|
|
||||||
give_sigsegv:
|
|
||||||
if (ksig->sig == SIGSEGV)
|
|
||||||
ksig->ka.sa.sa_handler = SIG_DFL;
|
|
||||||
force_sig(SIGSEGV, current);
|
|
||||||
goto adjust_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* OK, we're invoking a handler
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
handle_signal(struct ksignal *ksig, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
sigset_t *oldset = sigmask_to_save();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set up the stack frame, regardless of SA_SIGINFO,
|
* Set up registers for signal handler.
|
||||||
* and pass info anyway.
|
* Registers that we don't modify keep the value they had from
|
||||||
|
* user-space at the time we took the signal.
|
||||||
|
* We always pass siginfo and mcontext, regardless of SA_SIGINFO,
|
||||||
|
* since some things rely on this (e.g. glibc's debug/segfault.c).
|
||||||
*/
|
*/
|
||||||
ret = setup_rt_frame(ksig, oldset, regs);
|
regs->pc = (unsigned long)ksig->ka.sa.sa_handler;
|
||||||
|
regs->usp = (unsigned long)frame;
|
||||||
if (ret != 0) {
|
regs->a0 = ksig->sig; /* a0: signal number */
|
||||||
force_sigsegv(ksig->sig, current);
|
regs->a1 = (unsigned long)(&(frame->info)); /* a1: siginfo pointer */
|
||||||
return ret;
|
regs->a2 = (unsigned long)(&(frame->uc)); /* a2: ucontext pointer */
|
||||||
}
|
|
||||||
|
|
||||||
/* Block the signal if we were successful. */
|
|
||||||
spin_lock_irq(¤t->sighand->siglock);
|
|
||||||
sigorsets(¤t->blocked, ¤t->blocked, &ksig->ka.sa.sa_mask);
|
|
||||||
if (!(ksig->ka.sa.sa_flags & SA_NODEFER))
|
|
||||||
sigaddset(¤t->blocked, ksig->sig);
|
|
||||||
recalc_sigpending();
|
|
||||||
spin_unlock_irq(¤t->sighand->siglock);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
|
||||||
* Note that 'init' is a special process: it doesn't get signals it doesn't
|
|
||||||
* want to handle. Thus you cannot kill init even with a SIGKILL even by
|
|
||||||
* mistake.
|
|
||||||
*
|
|
||||||
* Note that we go through the signals twice: once to check the signals
|
|
||||||
* that the kernel can handle, and then we build all the user-level signal
|
|
||||||
* handling stack-frames in one go after that.
|
|
||||||
*/
|
|
||||||
static void do_signal(struct pt_regs *regs)
|
|
||||||
{
|
{
|
||||||
unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
|
sigset_t *oldset = sigmask_to_save();
|
||||||
struct ksignal ksig;
|
int ret;
|
||||||
|
|
||||||
/*
|
/* Are we from a system call? */
|
||||||
* We want the common case to go fast, which
|
|
||||||
* is why we may in certain cases get here from
|
|
||||||
* kernel mode. Just return without doing anything
|
|
||||||
* if so.
|
|
||||||
*/
|
|
||||||
if (!user_mode(regs))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we were from a system call, check for system call restarting...
|
|
||||||
*/
|
|
||||||
if (in_syscall(regs)) {
|
if (in_syscall(regs)) {
|
||||||
|
/* Avoid additional syscall restarting via ret_from_exception */
|
||||||
forget_syscall(regs);
|
forget_syscall(regs);
|
||||||
|
|
||||||
continue_addr = regs->pc;
|
/* If so, check system call restarting.. */
|
||||||
#if defined(__CSKYABIV2__)
|
switch (regs->a0) {
|
||||||
restart_addr = continue_addr - 4;
|
case -ERESTART_RESTARTBLOCK:
|
||||||
#else
|
case -ERESTARTNOHAND:
|
||||||
restart_addr = continue_addr - 2;
|
regs->a0 = -EINTR;
|
||||||
#endif
|
break;
|
||||||
retval = regs->a0;
|
|
||||||
/*
|
case -ERESTARTSYS:
|
||||||
* Prepare for system call restart. We do this here so that a
|
if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
|
||||||
* debugger will see the already changed.
|
regs->a0 = -EINTR;
|
||||||
*/
|
break;
|
||||||
switch (retval) {
|
}
|
||||||
|
/* fallthrough */
|
||||||
|
case -ERESTARTNOINTR:
|
||||||
|
regs->a0 = regs->orig_a0;
|
||||||
|
regs->pc -= TRAP0_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up the stack frame */
|
||||||
|
ret = setup_rt_frame(ksig, oldset, regs);
|
||||||
|
|
||||||
|
signal_setup_done(ret, ksig, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_signal(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct ksignal ksig;
|
||||||
|
|
||||||
|
if (get_signal(&ksig)) {
|
||||||
|
/* Actually deliver the signal */
|
||||||
|
handle_signal(&ksig, regs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Did we come from a system call? */
|
||||||
|
if (in_syscall(regs)) {
|
||||||
|
/* Avoid additional syscall restarting via ret_from_exception */
|
||||||
|
forget_syscall(regs);
|
||||||
|
|
||||||
|
/* Restart the system call - no handlers present */
|
||||||
|
switch (regs->a0) {
|
||||||
case -ERESTARTNOHAND:
|
case -ERESTARTNOHAND:
|
||||||
case -ERESTARTSYS:
|
case -ERESTARTSYS:
|
||||||
case -ERESTARTNOINTR:
|
case -ERESTARTNOINTR:
|
||||||
regs->a0 = regs->orig_a0;
|
regs->a0 = regs->orig_a0;
|
||||||
regs->pc = restart_addr;
|
regs->pc -= TRAP0_SIZE;
|
||||||
break;
|
break;
|
||||||
case -ERESTART_RESTARTBLOCK:
|
case -ERESTART_RESTARTBLOCK:
|
||||||
regs->a0 = -EINTR;
|
regs->a0 = regs->orig_a0;
|
||||||
|
regs_syscallid(regs) = __NR_restart_syscall;
|
||||||
|
regs->pc -= TRAP0_SIZE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try_to_freeze())
|
|
||||||
goto no_signal;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the signal to deliver. When running under ptrace, at this
|
* If there is no signal to deliver, we just put the saved
|
||||||
* point the debugger may change all our registers ...
|
* sigmask back.
|
||||||
*/
|
*/
|
||||||
if (get_signal(&ksig)) {
|
restore_saved_sigmask();
|
||||||
/*
|
|
||||||
* Depending on the signal settings we may need to revert the
|
|
||||||
* decision to restart the system call. But skip this if a
|
|
||||||
* debugger has chosen to restart at a different PC.
|
|
||||||
*/
|
|
||||||
if (regs->pc == restart_addr) {
|
|
||||||
if (retval == -ERESTARTNOHAND ||
|
|
||||||
(retval == -ERESTARTSYS &&
|
|
||||||
!(ksig.ka.sa.sa_flags & SA_RESTART))) {
|
|
||||||
regs->a0 = -EINTR;
|
|
||||||
regs->pc = continue_addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Whee! Actually deliver the signal. */
|
|
||||||
if (handle_signal(&ksig, regs) == 0) {
|
|
||||||
/*
|
|
||||||
* A signal was successfully delivered; the saved
|
|
||||||
* sigmask will have been stored in the signal frame,
|
|
||||||
* and will be restored by sigreturn, so we can simply
|
|
||||||
* clear the TIF_RESTORE_SIGMASK flag.
|
|
||||||
*/
|
|
||||||
if (test_thread_flag(TIF_RESTORE_SIGMASK))
|
|
||||||
clear_thread_flag(TIF_RESTORE_SIGMASK);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
no_signal:
|
|
||||||
if (in_syscall(regs)) {
|
|
||||||
forget_syscall(regs);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle restarting a different system call. As above,
|
|
||||||
* if a debugger has chosen to restart at a different PC,
|
|
||||||
* ignore the restart.
|
|
||||||
*/
|
|
||||||
if (retval == -ERESTART_RESTARTBLOCK
|
|
||||||
&& regs->pc == continue_addr) {
|
|
||||||
#if defined(__CSKYABIV2__)
|
|
||||||
regs->regs[3] = __NR_restart_syscall;
|
|
||||||
regs->pc -= 4;
|
|
||||||
#else
|
|
||||||
regs->regs[9] = __NR_restart_syscall;
|
|
||||||
regs->pc -= 2;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there's no signal to deliver, we just put the saved
|
|
||||||
* sigmask back.
|
|
||||||
*/
|
|
||||||
if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
|
|
||||||
clear_thread_flag(TIF_RESTORE_SIGMASK);
|
|
||||||
sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asmlinkage void
|
/*
|
||||||
do_notify_resume(unsigned int thread_flags, struct pt_regs *regs)
|
* notification of userspace execution resumption
|
||||||
|
* - triggered by the _TIF_WORK_MASK flags
|
||||||
|
*/
|
||||||
|
asmlinkage void do_notify_resume(struct pt_regs *regs,
|
||||||
|
unsigned long thread_info_flags)
|
||||||
{
|
{
|
||||||
if (thread_flags & _TIF_SIGPENDING)
|
/* Handle pending signal delivery */
|
||||||
|
if (thread_info_flags & _TIF_SIGPENDING)
|
||||||
do_signal(regs);
|
do_signal(regs);
|
||||||
|
|
||||||
if (thread_flags & _TIF_NOTIFY_RESUME) {
|
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||||
tracehook_notify_resume(regs);
|
tracehook_notify_resume(regs);
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче