Here's a kernel patch to enable NPTL support. This doesn't include Maciej's uber-fast rdhwr emulation; I believe we ought to include both the fast and slow paths, since the slow path will handle use of other destination registers. Changes: - Clone takes five arguments, not four. Um, this bit is gross. - New syscall sys_set_thread_area. Only glibc uses this. - Emulation of the rdhwr instruction. This version is only loosely based on the emulation on the malta branch; the major difference is that I fixed ll/sc/rdhwr emulation in branch delay slots. GCC 4.1 will generate rdhwr in branch delay slots in some conditions. - PTRACE_GET_THREAD_AREA support for GDB. How's it look? I've just finished hopefully final tests for the glibc bits, and will post them once this patch is resolved. [Credit goes to Manish Lachwani at MontaVista for writing the first draft of this patch, though he won't recognize most of this copy :-)] Index: linux/arch/mips/kernel/syscall.c =================================================================== --- linux.orig/arch/mips/kernel/syscall.c 2005-03-15 10:09:23.000000000 -0500 +++ linux/arch/mips/kernel/syscall.c 2005-03-15 10:30:09.000000000 -0500 @@ -176,14 +176,28 @@ _sys_clone(nabi_no_regargs struct pt_reg { 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_MIPS32 + /* 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 +259,16 @@ asmlinkage int sys_olduname(struct oldol 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; Index: linux/arch/mips/kernel/traps.c =================================================================== --- linux.orig/arch/mips/kernel/traps.c 2005-03-15 10:09:23.000000000 -0500 +++ linux/arch/mips/kernel/traps.c 2005-03-15 10:33:48.000000000 -0500 @@ -360,6 +360,10 @@ static inline int get_insn_opcode(struct #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 @@ -408,9 +412,10 @@ static inline void simulate_ll(struct pt preempt_enable(); + compute_return_epc(regs); + regs->regs[(opcode & RT) >> 16] = value; - compute_return_epc(regs); return; sig: @@ -446,9 +451,9 @@ static inline void simulate_sc(struct pt preempt_disable(); if (ll_bit == 0 || ll_task != current) { - regs->regs[reg] = 0; preempt_enable(); compute_return_epc(regs); + regs->regs[reg] = 0; return; } @@ -459,9 +464,8 @@ static inline void simulate_sc(struct pt goto sig; } - regs->regs[reg] = 1; - compute_return_epc(regs); + regs->regs[reg] = 1; return; sig: @@ -494,6 +498,37 @@ static inline int simulate_llsc(struct p 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; @@ -640,6 +675,9 @@ asmlinkage void do_ri(struct pt_regs *re if (!simulate_llsc(regs)) return; + if (!simulate_rdhwr(regs)) + return; + force_sig(SIGILL, current); } @@ -653,11 +691,13 @@ asmlinkage void do_cpu(struct pt_regs *r 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: Index: linux/arch/mips/kernel/process.c =================================================================== --- linux.orig/arch/mips/kernel/process.c 2005-03-15 10:09:23.000000000 -0500 +++ linux/arch/mips/kernel/process.c 2005-03-16 09:04:04.049856461 -0500 @@ -89,6 +89,7 @@ int copy_thread(int nr, unsigned long cl 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 cl 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; } Index: linux/include/asm-mips/inst.h =================================================================== --- linux.orig/include/asm-mips/inst.h 2005-03-15 10:09:23.000000000 -0500 +++ linux/include/asm-mips/inst.h 2005-03-15 10:09:29.000000000 -0500 @@ -28,7 +28,7 @@ enum major_op { sdl_op, sdr_op, swr_op, cache_op, ll_op, lwc1_op, lwc2_op, pref_op, lld_op, ldc1_op, ldc2_op, ld_op, - sc_op, swc1_op, swc2_op, major_3b_op, /* Opcode 0x3b is unused */ + sc_op, swc1_op, swc2_op, rdhwr_op, scd_op, sdc1_op, sdc2_op, sd_op }; Index: linux/arch/mips/kernel/ptrace.c =================================================================== --- linux.orig/arch/mips/kernel/ptrace.c 2005-03-15 10:09:23.000000000 -0500 +++ linux/arch/mips/kernel/ptrace.c 2005-03-15 10:09:29.000000000 -0500 @@ -287,6 +287,11 @@ asmlinkage int sys_ptrace(long request, 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; Index: linux/include/asm-mips/thread_info.h =================================================================== --- linux.orig/include/asm-mips/thread_info.h 2005-03-15 10:09:23.000000000 -0500 +++ linux/include/asm-mips/thread_info.h 2005-03-15 10:34:51.000000000 -0500 @@ -26,6 +26,7 @@ struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ + unsigned long tp_value; /* thread pointer */ __u32 cpu; /* current CPU */ __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ Index: linux/arch/mips/kernel/offset.c =================================================================== --- linux.orig/arch/mips/kernel/offset.c 2005-03-15 10:09:23.000000000 -0500 +++ linux/arch/mips/kernel/offset.c 2005-03-15 10:09:29.000000000 -0500 @@ -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); Index: linux/arch/mips/kernel/scall64-o32.S =================================================================== --- linux.orig/arch/mips/kernel/scall64-o32.S 2005-03-15 10:09:23.000000000 -0500 +++ linux/arch/mips/kernel/scall64-o32.S 2005-03-15 10:29:46.000000000 -0500 @@ -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 Index: linux/arch/mips/kernel/linux32.c =================================================================== --- linux.orig/arch/mips/kernel/linux32.c 2005-03-15 10:09:23.000000000 -0500 +++ linux/arch/mips/kernel/linux32.c 2005-03-15 10:29:02.000000000 -0500 @@ -1471,3 +1471,38 @@ sysn32_rt_sigtimedwait(const sigset_t __ } return sys_rt_sigtimedwait(uthese, uinfo, uts, sigsetsize); } + +save_static_function(sys32_clone); +__attribute_used__ noinline static int +_sys32_clone(unsigned long __dummy0, + unsigned long __dummy1, + unsigned long __dummy2, + unsigned long __dummy3, + unsigned long __dummy4, + unsigned long __dummy5, + unsigned long __dummy6, + unsigned long __dummy7, + 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)); +} Index: linux/arch/mips/kernel/ptrace32.c =================================================================== --- linux.orig/arch/mips/kernel/ptrace32.c 2005-03-15 10:09:23.000000000 -0500 +++ linux/arch/mips/kernel/ptrace32.c 2005-03-15 10:09:29.000000000 -0500 @@ -268,6 +268,11 @@ asmlinkage int sys32_ptrace(int request, 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; Index: linux/arch/mips/kernel/scall32-o32.S =================================================================== --- linux.orig/arch/mips/kernel/scall32-o32.S 2005-03-15 10:04:30.000000000 -0500 +++ linux/arch/mips/kernel/scall32-o32.S 2005-03-15 10:29:24.000000000 -0500 @@ -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 Index: linux/include/asm-mips/unistd.h =================================================================== --- linux.orig/include/asm-mips/unistd.h 2005-02-02 09:17:33.000000000 -0500 +++ linux/include/asm-mips/unistd.h 2005-03-15 10:26:02.000000000 -0500 @@ -303,16 +303,17 @@ #define __NR_add_key (__NR_Linux + 280) #define __NR_request_key (__NR_Linux + 281) #define __NR_keyctl (__NR_Linux + 282) +#define __NR_set_thread_area (__NR_Linux + 283) /* * Offset of the last Linux o32 flavoured syscall */ -#define __NR_Linux_syscalls 282 +#define __NR_Linux_syscalls 283 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ #define __NR_O32_Linux 4000 -#define __NR_O32_Linux_syscalls 282 +#define __NR_O32_Linux_syscalls 283 #if _MIPS_SIM == _MIPS_SIM_ABI64 @@ -562,16 +563,17 @@ #define __NR_add_key (__NR_Linux + 239) #define __NR_request_key (__NR_Linux + 240) #define __NR_keyctl (__NR_Linux + 241) +#define __NR_set_thread_area (__NR_Linux + 242) /* * Offset of the last Linux 64-bit flavoured syscall */ -#define __NR_Linux_syscalls 241 +#define __NR_Linux_syscalls 242 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ #define __NR_64_Linux 5000 -#define __NR_64_Linux_syscalls 241 +#define __NR_64_Linux_syscalls 242 #if _MIPS_SIM == _MIPS_SIM_NABI32 @@ -825,16 +827,17 @@ #define __NR_add_key (__NR_Linux + 243) #define __NR_request_key (__NR_Linux + 244) #define __NR_keyctl (__NR_Linux + 245) +#define __NR_set_thread_area (__NR_Linux + 246) /* * Offset of the last N32 flavoured syscall */ -#define __NR_Linux_syscalls 245 +#define __NR_Linux_syscalls 246 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ #define __NR_N32_Linux 6000 -#define __NR_N32_Linux_syscalls 245 +#define __NR_N32_Linux_syscalls 246 #ifndef __ASSEMBLY__ Index: linux/arch/mips/kernel/scall64-64.S =================================================================== --- linux.orig/arch/mips/kernel/scall64-64.S 2005-01-20 16:26:58.000000000 -0500 +++ linux/arch/mips/kernel/scall64-64.S 2005-03-15 10:29:48.000000000 -0500 @@ -449,3 +449,4 @@ sys_call_table: PTR sys_add_key PTR sys_request_key /* 5240 */ PTR sys_keyctl + PTR sys_set_thread_area Index: linux/arch/mips/kernel/scall64-n32.S =================================================================== --- linux.orig/arch/mips/kernel/scall64-n32.S 2005-03-15 10:09:21.000000000 -0500 +++ linux/arch/mips/kernel/scall64-n32.S 2005-03-16 09:04:03.388011517 -0500 @@ -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 -- Daniel Jacobowitz CodeSourcery, LLC