aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-03-16 19:28:15 -0700
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-03-16 19:28:15 -0700
commit47455911674d65fba28d43f4135c28ee40c75bac (patch)
treebb109710af11a8e499e971a230fff7316cc49095 /arch
parent5379058b718ac6354ba99cc74d10c28d632dc28a (diff)
parentf510aa3bdb095c5253f6bee9e0f5a3a9ac69ded4 (diff)
Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus
* 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus: [MIPS] Kconfig: Move missplaced NR_CPUS default from SMTC to VSMP. [MIPS] Lockdep: Fix recursion bug. [MIPS] RTLX: Handle copy_*_user return values. [MIPS] RTLX: Protect rtlx_{read,write} with mutex. [MIPS] RTLX: Harden against compiler reordering and optimization. [MIPS] RTLX: Don't use volatile; it's fragile. [MIPS] Lasat: Downgrade 64-bit kernel from experimental to broken. [MIPS] Compat: Fix build if CONFIG_SYSVIPC is disabled. [CHAR] lcd: Fix two warnings. [MIPS] FPU ownership management & preemption fixes [MIPS] Check FCSR for pending interrupts, alternative version [MIPS] IP27, IP35: Fix warnings.
Diffstat (limited to 'arch')
-rw-r--r--arch/mips/Kconfig4
-rw-r--r--arch/mips/kernel/kspd.c18
-rw-r--r--arch/mips/kernel/linux32.c12
-rw-r--r--arch/mips/kernel/r2300_switch.S10
-rw-r--r--arch/mips/kernel/r4k_fpu.S16
-rw-r--r--arch/mips/kernel/r4k_switch.S10
-rw-r--r--arch/mips/kernel/rtlx.c104
-rw-r--r--arch/mips/kernel/signal-common.h3
-rw-r--r--arch/mips/kernel/signal.c75
-rw-r--r--arch/mips/kernel/signal32.c56
-rw-r--r--arch/mips/kernel/signal_n32.c6
-rw-r--r--arch/mips/kernel/traps.c84
-rw-r--r--arch/mips/math-emu/kernel_linkage.c8
13 files changed, 242 insertions, 164 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 2b9c65c3b5d..5f29018a653 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -250,7 +250,7 @@ config LASAT
select R5000_CPU_SCACHE
select SYS_HAS_CPU_R5000
select SYS_SUPPORTS_32BIT_KERNEL
- select SYS_SUPPORTS_64BIT_KERNEL if EXPERIMENTAL
+ select SYS_SUPPORTS_64BIT_KERNEL if BROKEN
select SYS_SUPPORTS_LITTLE_ENDIAN
select GENERIC_HARDIRQS_NO__DO_IRQ
@@ -1559,6 +1559,7 @@ config MIPS_MT_SMP
select CPU_MIPSR2_IRQ_VI
select CPU_MIPSR2_SRS
select MIPS_MT
+ select NR_CPUS_DEFAULT_2
select SMP
select SYS_SUPPORTS_SMP
help
@@ -1573,7 +1574,6 @@ config MIPS_MT_SMTC
select CPU_MIPSR2_IRQ_VI
select CPU_MIPSR2_SRS
select MIPS_MT
- select NR_CPUS_DEFAULT_2
select NR_CPUS_DEFAULT_8
select SMP
select SYS_SUPPORTS_SMP
diff --git a/arch/mips/kernel/kspd.c b/arch/mips/kernel/kspd.c
index 241ee7a2906..29eadd404fa 100644
--- a/arch/mips/kernel/kspd.c
+++ b/arch/mips/kernel/kspd.c
@@ -191,6 +191,8 @@ void sp_work_handle_request(void)
struct mtsp_syscall_generic generic;
struct mtsp_syscall_ret ret;
struct kspd_notifications *n;
+ unsigned long written;
+ mm_segment_t old_fs;
struct timeval tv;
struct timezone tz;
int cmd;
@@ -201,7 +203,11 @@ void sp_work_handle_request(void)
ret.retval = -1;
- if (!rtlx_read(RTLX_CHANNEL_SYSIO, &sc, sizeof(struct mtsp_syscall), 0)) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ if (!rtlx_read(RTLX_CHANNEL_SYSIO, &sc, sizeof(struct mtsp_syscall))) {
+ set_fs(old_fs);
printk(KERN_ERR "Expected request but nothing to read\n");
return;
}
@@ -209,7 +215,8 @@ void sp_work_handle_request(void)
size = sc.size;
if (size) {
- if (!rtlx_read(RTLX_CHANNEL_SYSIO, &generic, size, 0)) {
+ if (!rtlx_read(RTLX_CHANNEL_SYSIO, &generic, size)) {
+ set_fs(old_fs);
printk(KERN_ERR "Expected request but nothing to read\n");
return;
}
@@ -282,8 +289,11 @@ void sp_work_handle_request(void)
if (vpe_getuid(SP_VPE))
sp_setfsuidgid( 0, 0);
- if ((rtlx_write(RTLX_CHANNEL_SYSIO, &ret, sizeof(struct mtsp_syscall_ret), 0))
- < sizeof(struct mtsp_syscall_ret))
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ written = rtlx_write(RTLX_CHANNEL_SYSIO, &ret, sizeof(ret));
+ set_fs(old_fs);
+ if (written < sizeof(ret))
printk("KSPD: sp_work_handle_request failed to send to SP\n");
}
diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c
index 1df544c1f96..37849edd064 100644
--- a/arch/mips/kernel/linux32.c
+++ b/arch/mips/kernel/linux32.c
@@ -311,6 +311,8 @@ asmlinkage int sys32_sched_rr_get_interval(compat_pid_t pid,
return ret;
}
+#ifdef CONFIG_SYSVIPC
+
asmlinkage long
sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
{
@@ -368,6 +370,16 @@ sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
return err;
}
+#else
+
+asmlinkage long
+sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_SYSVIPC */
+
#ifdef CONFIG_MIPS32_N32
asmlinkage long sysn32_semctl(int semid, int semnum, int cmd, u32 arg)
{
diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S
index 656bde2e11b..28c2e2e6af7 100644
--- a/arch/mips/kernel/r2300_switch.S
+++ b/arch/mips/kernel/r2300_switch.S
@@ -49,8 +49,7 @@ LEAF(resume)
#ifndef CONFIG_CPU_HAS_LLSC
sw zero, ll_bit
#endif
- mfc0 t1, CP0_STATUS
- sw t1, THREAD_STATUS(a0)
+ mfc0 t2, CP0_STATUS
cpu_save_nonscratch a0
sw ra, THREAD_REG31(a0)
@@ -60,8 +59,8 @@ LEAF(resume)
lw t3, TASK_THREAD_INFO(a0)
lw t0, TI_FLAGS(t3)
li t1, _TIF_USEDFPU
- and t2, t0, t1
- beqz t2, 1f
+ and t1, t0
+ beqz t1, 1f
nor t1, zero, t1
and t0, t0, t1
@@ -74,10 +73,13 @@ LEAF(resume)
li t1, ~ST0_CU1
and t0, t0, t1
sw t0, ST_OFF(t3)
+ /* clear thread_struct CU1 bit */
+ and t2, t1
fpu_save_single a0, t0 # clobbers t0
1:
+ sw t2, THREAD_STATUS(a0)
/*
* The order of restoring the registers takes care of the race
* updating $28, $29 and kernelsp without disabling ints.
diff --git a/arch/mips/kernel/r4k_fpu.S b/arch/mips/kernel/r4k_fpu.S
index 59c1577ecbb..dbd42adc52e 100644
--- a/arch/mips/kernel/r4k_fpu.S
+++ b/arch/mips/kernel/r4k_fpu.S
@@ -114,14 +114,6 @@ LEAF(_save_fp_context32)
*/
LEAF(_restore_fp_context)
EX lw t0, SC_FPC_CSR(a0)
-
- /* Fail if the CSR has exceptions pending */
- srl t1, t0, 5
- and t1, t0
- andi t1, 0x1f << 7
- bnez t1, fault
- nop
-
#ifdef CONFIG_64BIT
EX ldc1 $f1, SC_FPREGS+8(a0)
EX ldc1 $f3, SC_FPREGS+24(a0)
@@ -165,14 +157,6 @@ LEAF(_restore_fp_context)
LEAF(_restore_fp_context32)
/* Restore an o32 sigcontext. */
EX lw t0, SC32_FPC_CSR(a0)
-
- /* Fail if the CSR has exceptions pending */
- srl t1, t0, 5
- and t1, t0
- andi t1, 0x1f << 7
- bnez t1, fault
- nop
-
EX ldc1 $f0, SC32_FPREGS+0(a0)
EX ldc1 $f2, SC32_FPREGS+16(a0)
EX ldc1 $f4, SC32_FPREGS+32(a0)
diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S
index cc566cf1224..c7698fd9955 100644
--- a/arch/mips/kernel/r4k_switch.S
+++ b/arch/mips/kernel/r4k_switch.S
@@ -48,8 +48,7 @@
#ifndef CONFIG_CPU_HAS_LLSC
sw zero, ll_bit
#endif
- mfc0 t1, CP0_STATUS
- LONG_S t1, THREAD_STATUS(a0)
+ mfc0 t2, CP0_STATUS
cpu_save_nonscratch a0
LONG_S ra, THREAD_REG31(a0)
@@ -59,8 +58,8 @@
PTR_L t3, TASK_THREAD_INFO(a0)
LONG_L t0, TI_FLAGS(t3)
li t1, _TIF_USEDFPU
- and t2, t0, t1
- beqz t2, 1f
+ and t1, t0
+ beqz t1, 1f
nor t1, zero, t1
and t0, t0, t1
@@ -73,10 +72,13 @@
li t1, ~ST0_CU1
and t0, t0, t1
LONG_S t0, ST_OFF(t3)
+ /* clear thread_struct CU1 bit */
+ and t2, t1
fpu_save_double a0 t0 t1 # c0_status passed in t0
# clobbers t1
1:
+ LONG_S t2, THREAD_STATUS(a0)
/*
* The order of restoring the registers takes care of the race
diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c
index e14ae09eda2..e6e3047151a 100644
--- a/arch/mips/kernel/rtlx.c
+++ b/arch/mips/kernel/rtlx.c
@@ -54,6 +54,7 @@ static struct chan_waitqueues {
wait_queue_head_t rt_queue;
wait_queue_head_t lx_queue;
atomic_t in_open;
+ struct mutex mutex;
} channel_wqs[RTLX_CHANNELS];
static struct irqaction irq;
@@ -146,7 +147,7 @@ static void stopping(int vpe)
int rtlx_open(int index, int can_sleep)
{
- volatile struct rtlx_info **p;
+ struct rtlx_info **p;
struct rtlx_channel *chan;
enum rtlx_state state;
int ret = 0;
@@ -179,13 +180,24 @@ int rtlx_open(int index, int can_sleep)
}
}
+ smp_rmb();
if (*p == NULL) {
if (can_sleep) {
- __wait_event_interruptible(channel_wqs[index].lx_queue,
- *p != NULL,
- ret);
- if (ret)
+ DEFINE_WAIT(wait);
+
+ for (;;) {
+ prepare_to_wait(&channel_wqs[index].lx_queue, &wait, TASK_INTERRUPTIBLE);
+ smp_rmb();
+ if (*p != NULL)
+ break;
+ if (!signal_pending(current)) {
+ schedule();
+ continue;
+ }
+ ret = -ERESTARTSYS;
goto out_fail;
+ }
+ finish_wait(&channel_wqs[index].lx_queue, &wait);
} else {
printk(" *vpe_get_shared is NULL. "
"Has an SP program been loaded?\n");
@@ -277,56 +289,52 @@ unsigned int rtlx_write_poll(int index)
return write_spacefree(chan->rt_read, chan->rt_write, chan->buffer_size);
}
-static inline void copy_to(void *dst, void *src, size_t count, int user)
-{
- if (user)
- copy_to_user(dst, src, count);
- else
- memcpy(dst, src, count);
-}
-
-static inline void copy_from(void *dst, void *src, size_t count, int user)
+ssize_t rtlx_read(int index, void __user *buff, size_t count, int user)
{
- if (user)
- copy_from_user(dst, src, count);
- else
- memcpy(dst, src, count);
-}
-
-ssize_t rtlx_read(int index, void *buff, size_t count, int user)
-{
- size_t fl = 0L;
+ size_t lx_write, fl = 0L;
struct rtlx_channel *lx;
+ unsigned long failed;
if (rtlx == NULL)
return -ENOSYS;
lx = &rtlx->channel[index];
+ mutex_lock(&channel_wqs[index].mutex);
+ smp_rmb();
+ lx_write = lx->lx_write;
+
/* find out how much in total */
count = min(count,
- (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read)
+ (size_t)(lx_write + lx->buffer_size - lx->lx_read)
% lx->buffer_size);
/* then how much from the read pointer onwards */
- fl = min( count, (size_t)lx->buffer_size - lx->lx_read);
+ fl = min(count, (size_t)lx->buffer_size - lx->lx_read);
- copy_to(buff, &lx->lx_buffer[lx->lx_read], fl, user);
+ failed = copy_to_user(buff, lx->lx_buffer + lx->lx_read, fl);
+ if (failed)
+ goto out;
/* and if there is anything left at the beginning of the buffer */
- if ( count - fl )
- copy_to (buff + fl, lx->lx_buffer, count - fl, user);
+ if (count - fl)
+ failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl);
- /* update the index */
- lx->lx_read += count;
- lx->lx_read %= lx->buffer_size;
+out:
+ count -= failed;
+
+ smp_wmb();
+ lx->lx_read = (lx->lx_read + count) % lx->buffer_size;
+ smp_wmb();
+ mutex_unlock(&channel_wqs[index].mutex);
return count;
}
-ssize_t rtlx_write(int index, void *buffer, size_t count, int user)
+ssize_t rtlx_write(int index, const void __user *buffer, size_t count, int user)
{
struct rtlx_channel *rt;
+ size_t rt_read;
size_t fl;
if (rtlx == NULL)
@@ -334,24 +342,35 @@ ssize_t rtlx_write(int index, void *buffer, size_t count, int user)
rt = &rtlx->channel[index];
+ mutex_lock(&channel_wqs[index].mutex);
+ smp_rmb();
+ rt_read = rt->rt_read;
+
/* total number of bytes to copy */
count = min(count,
- (size_t)write_spacefree(rt->rt_read, rt->rt_write,
- rt->buffer_size));
+ (size_t)write_spacefree(rt_read, rt->rt_write, rt->buffer_size));
/* first bit from write pointer to the end of the buffer, or count */
fl = min(count, (size_t) rt->buffer_size - rt->rt_write);
- copy_from (&rt->rt_buffer[rt->rt_write], buffer, fl, user);
+ failed = copy_from_user(rt->rt_buffer + rt->rt_write, buffer, fl);
+ if (failed)
+ goto out;
/* if there's any left copy to the beginning of the buffer */
- if( count - fl )
- copy_from (rt->rt_buffer, buffer + fl, count - fl, user);
+ if (count - fl) {
+ failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl);
+ }
+
+out:
+ count -= cailed;
- rt->rt_write += count;
- rt->rt_write %= rt->buffer_size;
+ smp_wmb();
+ rt->rt_write = (rt->rt_write + count) % rt->buffer_size;
+ smp_wmb();
+ mutex_unlock(&channel_wqs[index].mutex);
- return(count);
+ return count;
}
@@ -403,7 +422,7 @@ static ssize_t file_read(struct file *file, char __user * buffer, size_t count,
return 0; // -EAGAIN makes cat whinge
}
- return rtlx_read(minor, buffer, count, 1);
+ return rtlx_read(minor, buffer, count);
}
static ssize_t file_write(struct file *file, const char __user * buffer,
@@ -429,7 +448,7 @@ static ssize_t file_write(struct file *file, const char __user * buffer,
return ret;
}
- return rtlx_write(minor, (void *)buffer, count, 1);
+ return rtlx_write(minor, buffer, count);
}
static const struct file_operations rtlx_fops = {
@@ -468,6 +487,7 @@ static int rtlx_module_init(void)
init_waitqueue_head(&channel_wqs[i].rt_queue);
init_waitqueue_head(&channel_wqs[i].lx_queue);
atomic_set(&channel_wqs[i].in_open, 0);
+ mutex_init(&channel_wqs[i].mutex);
dev = device_create(mt_class, NULL, MKDEV(major, i),
"%s%d", module_name, i);
diff --git a/arch/mips/kernel/signal-common.h b/arch/mips/kernel/signal-common.h
index fdbdbdc65b5..297dfcb9752 100644
--- a/arch/mips/kernel/signal-common.h
+++ b/arch/mips/kernel/signal-common.h
@@ -31,4 +31,7 @@ extern void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
*/
extern int install_sigtramp(unsigned int __user *tramp, unsigned int syscall);
+/* Check and clear pending FPU exceptions in saved CSR */
+extern int fpcsr_pending(unsigned int __user *fpcsr);
+
#endif /* __SIGNAL_COMMON_H */
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c
index f091786187a..8c3c5a5789b 100644
--- a/arch/mips/kernel/signal.c
+++ b/arch/mips/kernel/signal.c
@@ -82,6 +82,7 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{
int err = 0;
int i;
+ unsigned int used_math;
err |= __put_user(regs->cp0_epc, &sc->sc_pc);
@@ -104,26 +105,53 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
}
- err |= __put_user(!!used_math(), &sc->sc_used_math);
+ used_math = !!used_math();
+ err |= __put_user(used_math, &sc->sc_used_math);
- if (used_math()) {
+ if (used_math) {
/*
* Save FPU state to signal context. Signal handler
* will "inherit" current FPU state.
*/
- preempt_disable();
-
- if (!is_fpu_owner()) {
- own_fpu();
- restore_fp(current);
- }
+ own_fpu(1);
+ enable_fp_in_kernel();
err |= save_fp_context(sc);
-
- preempt_enable();
+ disable_fp_in_kernel();
}
return err;
}
+int fpcsr_pending(unsigned int __user *fpcsr)
+{
+ int err, sig = 0;
+ unsigned int csr, enabled;
+
+ err = __get_user(csr, fpcsr);
+ enabled = FPU_CSR_UNI_X | ((csr & FPU_CSR_ALL_E) << 5);
+ /*
+ * If the signal handler set some FPU exceptions, clear it and
+ * send SIGFPE.
+ */
+ if (csr & enabled) {
+ csr &= ~enabled;
+ err |= __put_user(csr, fpcsr);
+ sig = SIGFPE;
+ }
+ return err ?: sig;
+}
+
+static int
+check_and_restore_fp_context(struct sigcontext __user *sc)
+{
+ int err, sig;
+
+ err = sig = fpcsr_pending(&sc->sc_fpc_csr);
+ if (err > 0)
+ err = 0;
+ err |= restore_fp_context(sc);
+ return err ?: sig;
+}
+
int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{
unsigned int used_math;
@@ -157,19 +185,18 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
err |= __get_user(used_math, &sc->sc_used_math);
conditional_used_math(used_math);
- preempt_disable();
-
- if (used_math()) {
+ if (used_math) {
/* restore fpu context if we have used it before */
- own_fpu();
- err |= restore_fp_context(sc);
+ own_fpu(0);
+ enable_fp_in_kernel();
+ if (!err)
+ err = check_and_restore_fp_context(sc);
+ disable_fp_in_kernel();
} else {
/* signal handler may have used FPU. Give it up. */
- lose_fpu();
+ lose_fpu(0);
}
- preempt_enable();
-
return err;
}
@@ -332,6 +359,7 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs)
{
struct sigframe __user *frame;
sigset_t blocked;
+ int sig;
frame = (struct sigframe __user *) regs.regs[29];
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -345,8 +373,11 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs)
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- if (restore_sigcontext(&regs, &frame->sf_sc))
+ sig = restore_sigcontext(&regs, &frame->sf_sc);
+ if (sig < 0)
goto badframe;
+ else if (sig)
+ force_sig(sig, current);
/*
* Don't let your children do this ...
@@ -368,6 +399,7 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
struct rt_sigframe __user *frame;
sigset_t set;
stack_t st;
+ int sig;
frame = (struct rt_sigframe __user *) regs.regs[29];
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -381,8 +413,11 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- if (restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext))
+ sig = restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext);
+ if (sig < 0)
goto badframe;
+ else if (sig)
+ force_sig(sig, current);
if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st)))
goto badframe;
diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c
index 19bbef00195..151fd2f0893 100644
--- a/arch/mips/kernel/signal32.c
+++ b/arch/mips/kernel/signal32.c
@@ -181,6 +181,7 @@ static int setup_sigcontext32(struct pt_regs *regs,
{
int err = 0;
int i;
+ u32 used_math;
err |= __put_user(regs->cp0_epc, &sc->sc_pc);
@@ -200,26 +201,34 @@ static int setup_sigcontext32(struct pt_regs *regs,
err |= __put_user(mflo3(), &sc->sc_lo3);
}
- err |= __put_user(!!used_math(), &sc->sc_used_math);
+ used_math = !!used_math();
+ err |= __put_user(used_math, &sc->sc_used_math);
- if (used_math()) {
+ if (used_math) {
/*
* Save FPU state to signal context. Signal handler
* will "inherit" current FPU state.
*/
- preempt_disable();
-
- if (!is_fpu_owner()) {
- own_fpu();
- restore_fp(current);
- }
+ own_fpu(1);
+ enable_fp_in_kernel();
err |= save_fp_context32(sc);
-
- preempt_enable();
+ disable_fp_in_kernel();
}
return err;
}
+static int
+check_and_restore_fp_context32(struct sigcontext32 __user *sc)
+{
+ int err, sig;
+
+ err = sig = fpcsr_pending(&sc->sc_fpc_csr);
+ if (err > 0)
+ err = 0;
+ err |= restore_fp_context32(sc);
+ return err ?: sig;
+}
+
static int restore_sigcontext32(struct pt_regs *regs,
struct sigcontext32 __user *sc)
{
@@ -250,19 +259,18 @@ static int restore_sigcontext32(struct pt_regs *regs,
err |= __get_user(used_math, &sc->sc_used_math);
conditional_used_math(used_math);
- preempt_disable();
-
- if (used_math()) {
+ if (used_math) {
/* restore fpu context if we have used it before */
- own_fpu();
- err |= restore_fp_context32(sc);
+ own_fpu(0);
+ enable_fp_in_kernel();
+ if (!err)
+ err = check_and_restore_fp_context32(sc);
+ disable_fp_in_kernel();
} else {
/* signal handler may have used FPU. Give it up. */
- lose_fpu();
+ lose_fpu(0);
}
- preempt_enable();
-
return err;
}
@@ -508,6 +516,7 @@ asmlinkage void sys32_sigreturn(nabi_no_regargs struct pt_regs regs)
{
struct sigframe32 __user *frame;
sigset_t blocked;
+ int sig;
frame = (struct sigframe32 __user *) regs.regs[29];
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -521,8 +530,11 @@ asmlinkage void sys32_sigreturn(nabi_no_regargs struct pt_regs regs)
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- if (restore_sigcontext32(&regs, &frame->sf_sc))
+ sig = restore_sigcontext32(&regs, &frame->sf_sc);
+ if (sig < 0)
goto badframe;
+ else if (sig)
+ force_sig(sig, current);
/*
* Don't let your children do this ...
@@ -545,6 +557,7 @@ asmlinkage void sys32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
sigset_t set;
stack_t st;
s32 sp;
+ int sig;
frame = (struct rt_sigframe32 __user *) regs.regs[29];
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -558,8 +571,11 @@ asmlinkage void sys32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- if (restore_sigcontext32(&regs, &frame->rs_uc.uc_mcontext))
+ sig = restore_sigcontext32(&regs, &frame->rs_uc.uc_mcontext);
+ if (sig < 0)
goto badframe;
+ else if (sig)
+ force_sig(sig, current);
/* The ucontext contains a stack32_t, so we must convert! */
if (__get_user(sp, &frame->rs_uc.uc_stack.ss_sp))
diff --git a/arch/mips/kernel/signal_n32.c b/arch/mips/kernel/signal_n32.c
index ecf1f7ecaad..a9202fa9598 100644
--- a/arch/mips/kernel/signal_n32.c
+++ b/arch/mips/kernel/signal_n32.c
@@ -127,6 +127,7 @@ asmlinkage void sysn32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
sigset_t set;
stack_t st;
s32 sp;
+ int sig;
frame = (struct rt_sigframe_n32 __user *) regs.regs[29];
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -140,8 +141,11 @@ asmlinkage void sysn32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- if (restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext))
+ sig = restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext);
+ if (sig < 0)
goto badframe;
+ else if (sig)
+ force_sig(sig, current);
/* The ucontext contains a stack32_t, so we must convert! */
if (__get_user(sp, &frame->rs_uc.uc_stack.ss_sp))
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 18f56a9dbcf..7d76a85422b 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -610,16 +610,6 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
if (fcr31 & FPU_CSR_UNI_X) {
int sig;
- preempt_disable();
-
-#ifdef CONFIG_PREEMPT
- if (!is_fpu_owner()) {
- /* We might lose fpu before disabling preempt... */
- own_fpu();
- BUG_ON(!used_math());
- restore_fp(current);
- }
-#endif
/*
* Unimplemented operation exception. If we've got the full
* software emulator on-board, let's use it...
@@ -630,18 +620,12 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
* register operands before invoking the emulator, which seems
* a bit extreme for what should be an infrequent event.
*/
- save_fp(current);
/* Ensure 'resume' not overwrite saved fp context again. */
- lose_fpu();
-
- preempt_enable();
+ lose_fpu(1);
/* Run the emulator */
sig = fpu_emulator_cop1Handler (regs, &current->thread.fpu, 1);
- preempt_disable();
-
- own_fpu(); /* Using the FPU again. */
/*
* We can't allow the emulated instruction to leave any of
* the cause bit set in $fcr31.
@@ -649,9 +633,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
/* Restore the hardware register state */
- restore_fp(current);
-
- preempt_enable();
+ own_fpu(1); /* Using the FPU again. */
/* If something went wrong, signal */
if (sig)
@@ -775,12 +757,11 @@ asmlinkage void do_cpu(struct pt_regs *regs)
{
unsigned int cpid;
- die_if_kernel("do_cpu invoked from kernel context!", regs);
-
cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;
switch (cpid) {
case 0:
+ die_if_kernel("do_cpu invoked from kernel context!", regs);
if (!cpu_has_llsc)
if (!simulate_llsc(regs))
return;
@@ -791,21 +772,30 @@ asmlinkage void do_cpu(struct pt_regs *regs)
break;
case 1:
- preempt_disable();
-
- own_fpu();
- if (used_math()) { /* Using the FPU again. */
- restore_fp(current);
- } else { /* First time FPU user. */
+ if (!test_thread_flag(TIF_ALLOW_FP_IN_KERNEL))
+ die_if_kernel("do_cpu invoked from kernel context!",
+ regs);
+ if (used_math()) /* Using the FPU again. */
+ own_fpu(1);
+ else { /* First time FPU user. */
init_fpu();
set_used_math();
}
- if (cpu_has_fpu) {
- preempt_enable();
+ if (raw_cpu_has_fpu) {
+ if (test_thread_flag(TIF_ALLOW_FP_IN_KERNEL)) {
+ local_irq_disable();
+ if (cpu_has_fpu)
+ regs->cp0_status |= ST0_CU1;
+ /*
+ * We must return without enabling
+ * interrupts to ensure keep FPU
+ * ownership until resume.
+ */
+ return;
+ }
} else {
int sig;
- preempt_enable();
sig = fpu_emulator_cop1Handler(regs,
&current->thread.fpu, 0);
if (sig)
@@ -1259,26 +1249,26 @@ static inline void mips_srs_init(void)
/*
* This is used by native signal handling
*/
-asmlinkage int (*save_fp_context)(struct sigcontext *sc);
-asmlinkage int (*restore_fp_context)(struct sigcontext *sc);
+asmlinkage int (*save_fp_context)(struct sigcontext __user *sc);
+asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc);
-extern asmlinkage int _save_fp_context(struct sigcontext *sc);
-extern asmlinkage int _restore_fp_context(struct sigcontext *sc);
+extern asmlinkage int _save_fp_context(struct sigcontext __user *sc);
+extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc);
-extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc);
-extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc);
+extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc);
+extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc);
#ifdef CONFIG_SMP
-static int smp_save_fp_context(struct sigcontext *sc)
+static int smp_save_fp_context(struct sigcontext __user *sc)
{
- return cpu_has_fpu
+ return raw_cpu_has_fpu
? _save_fp_context(sc)
: fpu_emulator_save_context(sc);
}
-static int smp_restore_fp_context(struct sigcontext *sc)
+static int smp_restore_fp_context(struct sigcontext __user *sc)
{
- return cpu_has_fpu
+ return raw_cpu_has_fpu
? _restore_fp_context(sc)
: fpu_emulator_restore_context(sc);
}
@@ -1306,14 +1296,14 @@ static inline void signal_init(void)
/*
* This is used by 32-bit signal stuff on the 64-bit kernel
*/
-asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc);
-asmlinkage int (*restore_fp_context32)(struct sigcontext32 *sc);
+asmlinkage int (*save_fp_context32)(struct sigcontext32 __user *sc);
+asmlinkage int (*restore_fp_context32)(struct sigcontext32 __user *sc);
-extern asmlinkage int _save_fp_context32(struct sigcontext32 *sc);
-extern asmlinkage int _restore_fp_context32(struct sigcontext32 *sc);
+extern asmlinkage int _save_fp_context32(struct sigcontext32 __user *sc);
+extern asmlinkage int _restore_fp_context32(struct sigcontext32 __user *sc);
-extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 *sc);
-extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 *sc);
+extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 __user *sc);
+extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 __user *sc);
static inline void signal32_init(void)
{
diff --git a/arch/mips/math-emu/kernel_linkage.c b/arch/mips/math-emu/kernel_linkage.c
index 5b3390f6491..ed49ef01ac5 100644
--- a/arch/mips/math-emu/kernel_linkage.c
+++ b/arch/mips/math-emu/kernel_linkage.c
@@ -51,7 +51,7 @@ void fpu_emulator_init_fpu(void)
* with appropriate macros from uaccess.h
*/
-int fpu_emulator_save_context(struct sigcontext *sc)
+int fpu_emulator_save_context(struct sigcontext __user *sc)
{
int i;
int err = 0;
@@ -65,7 +65,7 @@ int fpu_emulator_save_context(struct sigcontext *sc)
return err;
}
-int fpu_emulator_restore_context(struct sigcontext *sc)
+int fpu_emulator_restore_context(struct sigcontext __user *sc)
{
int i;
int err = 0;
@@ -84,7 +84,7 @@ int fpu_emulator_restore_context(struct sigcontext *sc)
* This is the o32 version
*/
-int fpu_emulator_save_context32(struct sigcontext32 *sc)
+int fpu_emulator_save_context32(struct sigcontext32 __user *sc)
{
int i;
int err = 0;
@@ -98,7 +98,7 @@ int fpu_emulator_save_context32(struct sigcontext32 *sc)
return err;
}
-int fpu_emulator_restore_context32(struct sigcontext32 *sc)
+int fpu_emulator_restore_context32(struct sigcontext32 __user *sc)
{
int i;
int err = 0;