diff options
author | Maxim Kuvyrkov <maxim@codesourcery.com> | 2009-12-07 03:24:27 -0500 |
---|---|---|
committer | Geert Uytterhoeven <geert@linux-m68k.org> | 2010-02-27 12:31:19 -0500 |
commit | 9674cdc74d63f346870943ef966a034f8c71ee57 (patch) | |
tree | c590640335983c5b1a7f5c9589478bf90fb68d03 /arch/m68k | |
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')
-rw-r--r-- | arch/m68k/include/asm/ptrace.h | 2 | ||||
-rw-r--r-- | arch/m68k/include/asm/thread_info_mm.h | 1 | ||||
-rw-r--r-- | arch/m68k/include/asm/unistd.h | 6 | ||||
-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 |
7 files changed, 102 insertions, 1 deletions
diff --git a/arch/m68k/include/asm/ptrace.h b/arch/m68k/include/asm/ptrace.h index ee4011c23281..21605c736f69 100644 --- a/arch/m68k/include/asm/ptrace.h +++ b/arch/m68k/include/asm/ptrace.h | |||
@@ -71,6 +71,8 @@ struct switch_stack { | |||
71 | #define PTRACE_GETFPREGS 14 | 71 | #define PTRACE_GETFPREGS 14 |
72 | #define PTRACE_SETFPREGS 15 | 72 | #define PTRACE_SETFPREGS 15 |
73 | 73 | ||
74 | #define PTRACE_GET_THREAD_AREA 25 | ||
75 | |||
74 | #define PTRACE_SINGLEBLOCK 33 /* resume execution until next branch */ | 76 | #define PTRACE_SINGLEBLOCK 33 /* resume execution until next branch */ |
75 | 77 | ||
76 | #ifdef __KERNEL__ | 78 | #ifdef __KERNEL__ |
diff --git a/arch/m68k/include/asm/thread_info_mm.h b/arch/m68k/include/asm/thread_info_mm.h index 167e518db41b..67266c683453 100644 --- a/arch/m68k/include/asm/thread_info_mm.h +++ b/arch/m68k/include/asm/thread_info_mm.h | |||
@@ -16,6 +16,7 @@ struct thread_info { | |||
16 | struct exec_domain *exec_domain; /* execution domain */ | 16 | struct exec_domain *exec_domain; /* execution domain */ |
17 | int preempt_count; /* 0 => preemptable, <0 => BUG */ | 17 | int preempt_count; /* 0 => preemptable, <0 => BUG */ |
18 | __u32 cpu; /* should always be 0 on m68k */ | 18 | __u32 cpu; /* should always be 0 on m68k */ |
19 | unsigned long tp_value; /* thread pointer */ | ||
19 | struct restart_block restart_block; | 20 | struct restart_block restart_block; |
20 | }; | 21 | }; |
21 | #endif /* __ASSEMBLY__ */ | 22 | #endif /* __ASSEMBLY__ */ |
diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h index 48b87f5ced50..d72a71dabecb 100644 --- a/arch/m68k/include/asm/unistd.h +++ b/arch/m68k/include/asm/unistd.h | |||
@@ -336,10 +336,14 @@ | |||
336 | #define __NR_pwritev 330 | 336 | #define __NR_pwritev 330 |
337 | #define __NR_rt_tgsigqueueinfo 331 | 337 | #define __NR_rt_tgsigqueueinfo 331 |
338 | #define __NR_perf_event_open 332 | 338 | #define __NR_perf_event_open 332 |
339 | #define __NR_get_thread_area 333 | ||
340 | #define __NR_set_thread_area 334 | ||
341 | #define __NR_atomic_cmpxchg_32 335 | ||
342 | #define __NR_atomic_barrier 336 | ||
339 | 343 | ||
340 | #ifdef __KERNEL__ | 344 | #ifdef __KERNEL__ |
341 | 345 | ||
342 | #define NR_syscalls 333 | 346 | #define NR_syscalls 337 |
343 | 347 | ||
344 | #define __ARCH_WANT_IPC_PARSE_VERSION | 348 | #define __ARCH_WANT_IPC_PARSE_VERSION |
345 | #define __ARCH_WANT_OLD_READDIR | 349 | #define __ARCH_WANT_OLD_READDIR |
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: | |||
761 | .long sys_pwritev /* 330 */ | 761 | .long sys_pwritev /* 330 */ |
762 | .long sys_rt_tgsigqueueinfo | 762 | .long sys_rt_tgsigqueueinfo |
763 | .long sys_perf_event_open | 763 | .long sys_perf_event_open |
764 | .long sys_get_thread_area | ||
765 | .long sys_set_thread_area | ||
766 | .long sys_atomic_cmpxchg_32 /* 335 */ | ||
767 | .long sys_atomic_barrier | ||
764 | 768 | ||
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, | |||
251 | 251 | ||
252 | p->thread.usp = usp; | 252 | p->thread.usp = usp; |
253 | p->thread.ksp = (unsigned long)childstack; | 253 | p->thread.ksp = (unsigned long)childstack; |
254 | |||
255 | if (clone_flags & CLONE_SETTLS) | ||
256 | task_thread_info(p)->tp_value = regs->d5; | ||
257 | |||
254 | /* | 258 | /* |
255 | * Must save the current SFC/DFC value, NOT the value when | 259 | * Must save the current SFC/DFC value, NOT the value when |
256 | * the parent was last descheduled - RGH 10-08-96 | 260 | * 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) | |||
245 | ret = -EFAULT; | 245 | ret = -EFAULT; |
246 | break; | 246 | break; |
247 | 247 | ||
248 | case PTRACE_GET_THREAD_AREA: | ||
249 | ret = put_user(task_thread_info(child)->tp_value, | ||
250 | (unsigned long __user *)data); | ||
251 | break; | ||
252 | |||
248 | default: | 253 | default: |
249 | ret = ptrace_request(child, request, addr, data); | 254 | ret = ptrace_request(child, request, addr, data); |
250 | break; | 255 | 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 @@ | |||
28 | #include <asm/traps.h> | 28 | #include <asm/traps.h> |
29 | #include <asm/page.h> | 29 | #include <asm/page.h> |
30 | #include <asm/unistd.h> | 30 | #include <asm/unistd.h> |
31 | #include <linux/elf.h> | ||
32 | #include <asm/tlb.h> | ||
33 | |||
34 | asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, | ||
35 | unsigned long error_code); | ||
31 | 36 | ||
32 | asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, | 37 | asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, |
33 | unsigned long prot, unsigned long flags, | 38 | unsigned long prot, unsigned long flags, |
@@ -595,3 +600,79 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[]) | |||
595 | : "d" (__a), "d" (__b), "d" (__c)); | 600 | : "d" (__a), "d" (__b), "d" (__c)); |
596 | return __res; | 601 | return __res; |
597 | } | 602 | } |
603 | |||
604 | asmlinkage unsigned long sys_get_thread_area(void) | ||
605 | { | ||
606 | return current_thread_info()->tp_value; | ||
607 | } | ||
608 | |||
609 | asmlinkage int sys_set_thread_area(unsigned long tp) | ||
610 | { | ||
611 | current_thread_info()->tp_value = tp; | ||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | /* This syscall gets its arguments in A0 (mem), D2 (oldval) and | ||
616 | D1 (newval). */ | ||
617 | asmlinkage int | ||
618 | sys_atomic_cmpxchg_32(unsigned long newval, int oldval, int d3, int d4, int d5, | ||
619 | unsigned long __user * mem) | ||
620 | { | ||
621 | /* This was borrowed from ARM's implementation. */ | ||
622 | for (;;) { | ||
623 | struct mm_struct *mm = current->mm; | ||
624 | pgd_t *pgd; | ||
625 | pmd_t *pmd; | ||
626 | pte_t *pte; | ||
627 | spinlock_t *ptl; | ||
628 | unsigned long mem_value; | ||
629 | |||
630 | down_read(&mm->mmap_sem); | ||
631 | pgd = pgd_offset(mm, (unsigned long)mem); | ||
632 | if (!pgd_present(*pgd)) | ||
633 | goto bad_access; | ||
634 | pmd = pmd_offset(pgd, (unsigned long)mem); | ||
635 | if (!pmd_present(*pmd)) | ||
636 | goto bad_access; | ||
637 | pte = pte_offset_map_lock(mm, pmd, (unsigned long)mem, &ptl); | ||
638 | if (!pte_present(*pte) || !pte_dirty(*pte) | ||
639 | || !pte_write(*pte)) { | ||
640 | pte_unmap_unlock(pte, ptl); | ||
641 | goto bad_access; | ||
642 | } | ||
643 | |||
644 | mem_value = *mem; | ||
645 | if (mem_value == oldval) | ||
646 | *mem = newval; | ||
647 | |||
648 | pte_unmap_unlock(pte, ptl); | ||
649 | up_read(&mm->mmap_sem); | ||
650 | return mem_value; | ||
651 | |||
652 | bad_access: | ||
653 | up_read(&mm->mmap_sem); | ||
654 | /* This is not necessarily a bad access, we can get here if | ||
655 | a memory we're trying to write to should be copied-on-write. | ||
656 | Make the kernel do the necessary page stuff, then re-iterate. | ||
657 | Simulate a write access fault to do that. */ | ||
658 | { | ||
659 | /* The first argument of the function corresponds to | ||
660 | D1, which is the first field of struct pt_regs. */ | ||
661 | struct pt_regs *fp = (struct pt_regs *)&newval; | ||
662 | |||
663 | /* '3' is an RMW flag. */ | ||
664 | if (do_page_fault(fp, (unsigned long)mem, 3)) | ||
665 | /* If the do_page_fault() failed, we don't | ||
666 | have anything meaningful to return. | ||
667 | There should be a SIGSEGV pending for | ||
668 | the process. */ | ||
669 | return 0xdeadbeef; | ||
670 | } | ||
671 | } | ||
672 | } | ||
673 | |||
674 | asmlinkage int sys_atomic_barrier(void) | ||
675 | { | ||
676 | /* no code needed for uniprocs */ | ||
677 | return 0; | ||
678 | } | ||