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>
This commit is contained in:
		
							parent
							
								
									f54bcdc2b8
								
							
						
					
					
						commit
						9674cdc74d
					
				| @ -71,6 +71,8 @@ struct switch_stack { | ||||
| #define PTRACE_GETFPREGS          14 | ||||
| #define PTRACE_SETFPREGS          15 | ||||
| 
 | ||||
| #define PTRACE_GET_THREAD_AREA    25 | ||||
| 
 | ||||
| #define PTRACE_SINGLEBLOCK	33	/* resume execution until next branch */ | ||||
| 
 | ||||
| #ifdef __KERNEL__ | ||||
|  | ||||
| @ -16,6 +16,7 @@ struct thread_info { | ||||
| 	struct exec_domain	*exec_domain;	/* execution domain */ | ||||
| 	int			preempt_count;	/* 0 => preemptable, <0 => BUG */ | ||||
| 	__u32 cpu; /* should always be 0 on m68k */ | ||||
| 	unsigned long		tp_value;	/* thread pointer */ | ||||
| 	struct restart_block    restart_block; | ||||
| }; | ||||
| #endif /* __ASSEMBLY__ */ | ||||
|  | ||||
| @ -336,10 +336,14 @@ | ||||
| #define __NR_pwritev		330 | ||||
| #define __NR_rt_tgsigqueueinfo	331 | ||||
| #define __NR_perf_event_open	332 | ||||
| #define __NR_get_thread_area	333 | ||||
| #define __NR_set_thread_area	334 | ||||
| #define __NR_atomic_cmpxchg_32	335 | ||||
| #define __NR_atomic_barrier	336 | ||||
| 
 | ||||
| #ifdef __KERNEL__ | ||||
| 
 | ||||
| #define NR_syscalls		333 | ||||
| #define NR_syscalls		337 | ||||
| 
 | ||||
| #define __ARCH_WANT_IPC_PARSE_VERSION | ||||
| #define __ARCH_WANT_OLD_READDIR | ||||
|  | ||||
| @ -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
 | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user