diff options
| author | Maxim Kuvyrkov <maxim@codesourcery.com> | 2009-12-07 00:24:27 -0800 | 
|---|---|---|
| committer | Geert Uytterhoeven <geert@linux-m68k.org> | 2010-02-27 18:31:19 +0100 | 
| commit | 9674cdc74d63f346870943ef966a034f8c71ee57 (patch) | |
| tree | c590640335983c5b1a7f5c9589478bf90fb68d03 /arch/m68k/kernel | |
| parent | f54bcdc2b81558a2b7e624cfeb4992422d9265f9 (diff) | |
m68k: Add NPTL support
This patch adds several syscalls, that provide necessary
functionality to support NPTL on m68k/ColdFire.
The syscalls are get_thread_area, set_thread_area, atomic_cmpxchg_32 and
atomic_barrier.
The cmpxchg syscall is required for ColdFire as it doesn't support 'cas'
instruction.
Also a ptrace call PTRACE_GET_THREAD_AREA is added to allow debugger to
inspect the TLS storage.
Signed-off-by: Maxim Kuvyrkov <maxim@codesourcery.com>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Diffstat (limited to 'arch/m68k/kernel')
| -rw-r--r-- | arch/m68k/kernel/entry.S | 4 | ||||
| -rw-r--r-- | arch/m68k/kernel/process.c | 4 | ||||
| -rw-r--r-- | arch/m68k/kernel/ptrace.c | 5 | ||||
| -rw-r--r-- | arch/m68k/kernel/sys_m68k.c | 81 | 
4 files changed, 94 insertions, 0 deletions
| diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 77fc7c16bf48..e136b8cbe9b9 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -761,4 +761,8 @@ sys_call_table:  	.long sys_pwritev		/* 330 */  	.long sys_rt_tgsigqueueinfo  	.long sys_perf_event_open +	.long sys_get_thread_area +	.long sys_set_thread_area +	.long sys_atomic_cmpxchg_32	/* 335 */ +	.long sys_atomic_barrier diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index 05296593e718..17c3f325255d 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -251,6 +251,10 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,  	p->thread.usp = usp;  	p->thread.ksp = (unsigned long)childstack; + +	if (clone_flags & CLONE_SETTLS) +		task_thread_info(p)->tp_value = regs->d5; +  	/*  	 * Must save the current SFC/DFC value, NOT the value when  	 * the parent was last descheduled - RGH  10-08-96 diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index 1fc217e5f06b..616e59752c29 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -245,6 +245,11 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)  			ret = -EFAULT;  		break; +	case PTRACE_GET_THREAD_AREA: +		ret = put_user(task_thread_info(child)->tp_value, +			       (unsigned long __user *)data); +		break; +  	default:  		ret = ptrace_request(child, request, addr, data);  		break; diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c index 218f441de667..e3ad2d671973 100644 --- a/arch/m68k/kernel/sys_m68k.c +++ b/arch/m68k/kernel/sys_m68k.c @@ -28,6 +28,11 @@  #include <asm/traps.h>  #include <asm/page.h>  #include <asm/unistd.h> +#include <linux/elf.h> +#include <asm/tlb.h> + +asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, +			     unsigned long error_code);  asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,  	unsigned long prot, unsigned long flags, @@ -595,3 +600,79 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[])  			: "d" (__a), "d" (__b), "d" (__c));  	return __res;  } + +asmlinkage unsigned long sys_get_thread_area(void) +{ +	return current_thread_info()->tp_value; +} + +asmlinkage int sys_set_thread_area(unsigned long tp) +{ +	current_thread_info()->tp_value = tp; +	return 0; +} + +/* This syscall gets its arguments in A0 (mem), D2 (oldval) and +   D1 (newval).  */ +asmlinkage int +sys_atomic_cmpxchg_32(unsigned long newval, int oldval, int d3, int d4, int d5, +		      unsigned long __user * mem) +{ +	/* This was borrowed from ARM's implementation.  */ +	for (;;) { +		struct mm_struct *mm = current->mm; +		pgd_t *pgd; +		pmd_t *pmd; +		pte_t *pte; +		spinlock_t *ptl; +		unsigned long mem_value; + +		down_read(&mm->mmap_sem); +		pgd = pgd_offset(mm, (unsigned long)mem); +		if (!pgd_present(*pgd)) +			goto bad_access; +		pmd = pmd_offset(pgd, (unsigned long)mem); +		if (!pmd_present(*pmd)) +			goto bad_access; +		pte = pte_offset_map_lock(mm, pmd, (unsigned long)mem, &ptl); +		if (!pte_present(*pte) || !pte_dirty(*pte) +		    || !pte_write(*pte)) { +			pte_unmap_unlock(pte, ptl); +			goto bad_access; +		} + +		mem_value = *mem; +		if (mem_value == oldval) +			*mem = newval; + +		pte_unmap_unlock(pte, ptl); +		up_read(&mm->mmap_sem); +		return mem_value; + +	      bad_access: +		up_read(&mm->mmap_sem); +		/* This is not necessarily a bad access, we can get here if +		   a memory we're trying to write to should be copied-on-write. +		   Make the kernel do the necessary page stuff, then re-iterate. +		   Simulate a write access fault to do that.  */ +		{ +			/* The first argument of the function corresponds to +			   D1, which is the first field of struct pt_regs.  */ +			struct pt_regs *fp = (struct pt_regs *)&newval; + +			/* '3' is an RMW flag.  */ +			if (do_page_fault(fp, (unsigned long)mem, 3)) +				/* If the do_page_fault() failed, we don't +				   have anything meaningful to return. +				   There should be a SIGSEGV pending for +				   the process.  */ +				return 0xdeadbeef; +		} +	} +} + +asmlinkage int sys_atomic_barrier(void) +{ +	/* no code needed for uniprocs */ +	return 0; +} | 
