summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r--arch/mips/kernel/asm-offsets.c1
-rw-r--r--arch/mips/kernel/linux32.c27
-rw-r--r--arch/mips/kernel/process.c4
-rw-r--r--arch/mips/kernel/ptrace.c5
-rw-r--r--arch/mips/kernel/ptrace32.c5
-rw-r--r--arch/mips/kernel/scall32-o32.S1
-rw-r--r--arch/mips/kernel/scall64-64.S1
-rw-r--r--arch/mips/kernel/scall64-n32.S1
-rw-r--r--arch/mips/kernel/scall64-o32.S3
-rw-r--r--arch/mips/kernel/syscall.c31
-rw-r--r--arch/mips/kernel/traps.c46
11 files changed, 118 insertions, 7 deletions
diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c
index 2c11abb5a406..af69cdbdd50e 100644
--- a/arch/mips/kernel/asm-offsets.c
+++ b/arch/mips/kernel/asm-offsets.c
@@ -95,6 +95,7 @@ void output_thread_info_defines(void)
offset("#define TI_PRE_COUNT ", struct thread_info, preempt_count);
offset("#define TI_ADDR_LIMIT ", struct thread_info, addr_limit);
offset("#define TI_RESTART_BLOCK ", struct thread_info, restart_block);
+ offset("#define TI_TP_VALUE ", struct thread_info, tp_value);
constant("#define _THREAD_SIZE_ORDER ", THREAD_SIZE_ORDER);
constant("#define _THREAD_SIZE ", THREAD_SIZE);
constant("#define _THREAD_MASK ", THREAD_MASK);
diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c
index e8e886dd52d6..330cf84d21fe 100644
--- a/arch/mips/kernel/linux32.c
+++ b/arch/mips/kernel/linux32.c
@@ -1468,3 +1468,30 @@ sysn32_rt_sigtimedwait(const sigset_t __user *uthese,
}
return sys_rt_sigtimedwait(uthese, uinfo, uts, sigsetsize);
}
+
+save_static_function(sys32_clone);
+__attribute_used__ noinline static int
+_sys32_clone(nabi_no_regargs struct pt_regs regs)
+{
+ unsigned long clone_flags;
+ unsigned long newsp;
+ int __user *parent_tidptr, *child_tidptr;
+
+ clone_flags = regs.regs[4];
+ newsp = regs.regs[5];
+ if (!newsp)
+ newsp = regs.regs[29];
+ parent_tidptr = (int *) regs.regs[6];
+
+ /* Use __dummy4 instead of getting it off the stack, so that
+ syscall() works. */
+ child_tidptr = (int __user *) __dummy4;
+ return do_fork(clone_flags, newsp, &regs, 0,
+ parent_tidptr, child_tidptr);
+}
+
+extern asmlinkage void sys_set_thread_area(u32 addr);
+asmlinkage void sys32_set_thread_area(u32 addr)
+{
+ sys_set_thread_area(AA(addr));
+}
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 2b7a44deb856..368526af5f5e 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -89,6 +89,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
struct thread_info *ti = p->thread_info;
struct pt_regs *childregs;
long childksp;
+ p->set_child_tid = p->clear_child_tid = NULL;
childksp = (unsigned long)ti + THREAD_SIZE - 32;
@@ -134,6 +135,9 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
childregs->cp0_status &= ~(ST0_CU2|ST0_CU1);
clear_tsk_thread_flag(p, TIF_USEDFPU);
+ if (clone_flags & CLONE_SETTLS)
+ ti->tp_value = regs->regs[7];
+
return 0;
}
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 2c7fc7472fb2..649c90dee38e 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -289,6 +289,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
ret = ptrace_detach(child, data);
break;
+ case PTRACE_GET_THREAD_AREA:
+ ret = put_user(child->thread_info->tp_value,
+ (unsigned long __user *) data);
+ break;
+
default:
ret = ptrace_request(child, request, addr, data);
break;
diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c
index a8a72c9a1cca..eb446e525908 100644
--- a/arch/mips/kernel/ptrace32.c
+++ b/arch/mips/kernel/ptrace32.c
@@ -268,6 +268,11 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
wake_up_process(child);
break;
+ case PTRACE_GET_THREAD_AREA:
+ ret = put_user(child->thread_info->tp_value,
+ (unsigned int __user *) (unsigned long) data);
+ break;
+
case PTRACE_DETACH: /* detach a process that was attached. */
ret = ptrace_detach(child, data);
break;
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index 9c4bb917d476..6fa1112512c8 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -623,6 +623,7 @@ einval: li v0, -EINVAL
sys sys_add_key 5
sys sys_request_key 4
sys sys_keyctl 5
+ sys sys_set_thread_area 1
.endm
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index ffb22a2068bf..d11f99b0ae53 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -449,3 +449,4 @@ sys_call_table:
PTR sys_add_key
PTR sys_request_key /* 5240 */
PTR sys_keyctl
+ PTR sys_set_thread_area
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index d912218259e5..ce030412efbe 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -363,3 +363,4 @@ EXPORT(sysn32_call_table)
PTR sys_add_key
PTR sys_request_key
PTR sys_keyctl /* 6245 */
+ PTR sys_set_thread_area
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 1017176bdce9..f49182ea73f8 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -322,7 +322,7 @@ sys_call_table:
PTR sys32_ipc
PTR sys_fsync
PTR sys32_sigreturn
- PTR sys_clone /* 4120 */
+ PTR sys32_clone /* 4120 */
PTR sys_setdomainname
PTR sys32_newuname
PTR sys_ni_syscall /* sys_modify_ldt */
@@ -485,4 +485,5 @@ sys_call_table:
PTR sys_add_key /* 4280 */
PTR sys_request_key
PTR sys_keyctl
+ PTR sys_set_thread_area
.size sys_call_table,.-sys_call_table
diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
index 8fde242596f9..ee98eeb65e85 100644
--- a/arch/mips/kernel/syscall.c
+++ b/arch/mips/kernel/syscall.c
@@ -7,6 +7,7 @@
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
* Copyright (C) 2001 MIPS Technologies, Inc.
*/
+#include <linux/config.h>
#include <linux/a.out.h>
#include <linux/errno.h>
#include <linux/linkage.h>
@@ -176,14 +177,28 @@ _sys_clone(nabi_no_regargs struct pt_regs regs)
{
unsigned long clone_flags;
unsigned long newsp;
- int *parent_tidptr, *child_tidptr;
+ int __user *parent_tidptr, *child_tidptr;
clone_flags = regs.regs[4];
newsp = regs.regs[5];
if (!newsp)
newsp = regs.regs[29];
- parent_tidptr = (int *) regs.regs[6];
- child_tidptr = (int *) regs.regs[7];
+ parent_tidptr = (int __user *) regs.regs[6];
+#ifdef CONFIG_32BIT
+ /* We need to fetch the fifth argument off the stack. */
+ child_tidptr = NULL;
+ if (clone_flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) {
+ int __user *__user *usp = (int __user *__user *) regs.regs[29];
+ if (regs.regs[2] == __NR_syscall) {
+ if (get_user (child_tidptr, &usp[5]))
+ return -EFAULT;
+ }
+ else if (get_user (child_tidptr, &usp[4]))
+ return -EFAULT;
+ }
+#else
+ child_tidptr = (int __user *) regs.regs[8];
+#endif
return do_fork(clone_flags, newsp, &regs, 0,
parent_tidptr, child_tidptr);
}
@@ -245,6 +260,16 @@ asmlinkage int sys_olduname(struct oldold_utsname * name)
return error;
}
+void sys_set_thread_area(unsigned long addr)
+{
+ struct thread_info *ti = current->thread_info;
+
+ ti->tp_value = addr;
+
+ /* If some future MIPS implementation has this register in hardware,
+ * we will need to update it here (and in context switches). */
+}
+
asmlinkage int _sys_sysmips(int cmd, long arg1, int arg2, int arg3)
{
int tmp, len;
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 94d9141c04c1..15fed0202154 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -360,6 +360,10 @@ static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode)
#define OFFSET 0x0000ffff
#define LL 0xc0000000
#define SC 0xe0000000
+#define SPEC3 0x7c000000
+#define RD 0x0000f800
+#define FUNC 0x0000003f
+#define RDHWR 0x0000003b
/*
* The ll_bit is cleared by r*_switch.S
@@ -495,6 +499,37 @@ static inline int simulate_llsc(struct pt_regs *regs)
return -EFAULT; /* Strange things going on ... */
}
+/*
+ * Simulate trapping 'rdhwr' instructions to provide user accessible
+ * registers not implemented in hardware. The only current use of this
+ * is the thread area pointer.
+ */
+static inline int simulate_rdhwr(struct pt_regs *regs)
+{
+ struct thread_info *ti = current->thread_info;
+ unsigned int opcode;
+
+ if (unlikely(get_insn_opcode(regs, &opcode)))
+ return -EFAULT;
+
+ if (unlikely(compute_return_epc(regs)))
+ return -EFAULT;
+
+ if ((opcode & OPCODE) == SPEC3 && (opcode & FUNC) == RDHWR) {
+ int rd = (opcode & RD) >> 11;
+ int rt = (opcode & RT) >> 16;
+ switch (rd) {
+ case 29:
+ regs->regs[rt] = ti->tp_value;
+ break;
+ default:
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
asmlinkage void do_ov(struct pt_regs *regs)
{
siginfo_t info;
@@ -641,6 +676,9 @@ asmlinkage void do_ri(struct pt_regs *regs)
if (!simulate_llsc(regs))
return;
+ if (!simulate_rdhwr(regs))
+ return;
+
force_sig(SIGILL, current);
}
@@ -654,11 +692,13 @@ asmlinkage void do_cpu(struct pt_regs *regs)
switch (cpid) {
case 0:
- if (cpu_has_llsc)
- break;
+ if (!cpu_has_llsc)
+ if (!simulate_llsc(regs))
+ return;
- if (!simulate_llsc(regs))
+ if (!simulate_rdhwr(regs))
return;
+
break;
case 1: