diff options
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r-- | arch/mips/kernel/asm-offsets.c | 1 | ||||
-rw-r--r-- | arch/mips/kernel/linux32.c | 27 | ||||
-rw-r--r-- | arch/mips/kernel/process.c | 4 | ||||
-rw-r--r-- | arch/mips/kernel/ptrace.c | 5 | ||||
-rw-r--r-- | arch/mips/kernel/ptrace32.c | 5 | ||||
-rw-r--r-- | arch/mips/kernel/scall32-o32.S | 1 | ||||
-rw-r--r-- | arch/mips/kernel/scall64-64.S | 1 | ||||
-rw-r--r-- | arch/mips/kernel/scall64-n32.S | 1 | ||||
-rw-r--r-- | arch/mips/kernel/scall64-o32.S | 3 | ||||
-rw-r--r-- | arch/mips/kernel/syscall.c | 31 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 46 |
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, ®s, 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, ®s, 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: |