diff options
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/signal.c | 7 | ||||
| -rw-r--r-- | arch/m68k/kernel/sys_m68k.c | 81 | 
5 files changed, 101 insertions, 0 deletions
diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 77fc7c16bf4..e136b8cbe9b 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 05296593e71..17c3f325255 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 1fc217e5f06..616e59752c2 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/signal.c b/arch/m68k/kernel/signal.c index de2d05ddd86..4b387538706 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -897,10 +897,17 @@ static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info,  	/* Set up to return from userspace.  */  	err |= __put_user(frame->retcode, &frame->pretcode); +#ifdef __mcoldfire__ +	/* movel #__NR_rt_sigreturn,d0; trap #0 */ +	err |= __put_user(0x203c0000, (long __user *)(frame->retcode + 0)); +	err |= __put_user(0x00004e40 + (__NR_rt_sigreturn << 16), +			  (long __user *)(frame->retcode + 4)); +#else  	/* moveq #,d0; notb d0; trap #0 */  	err |= __put_user(0x70004600 + ((__NR_rt_sigreturn ^ 0xff) << 16),  			  (long __user *)(frame->retcode + 0));  	err |= __put_user(0x4e40, (short __user *)(frame->retcode + 4)); +#endif  	if (err)  		goto give_sigsegv; diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c index 218f441de66..e3ad2d67197 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; +}  |