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:
Родитель
f54bcdc2b8
Коммит
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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче