Merge branch 'x86-apic-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / arch / m68k / kernel / sys_m68k.c
index 218f441de667c6f5fb35de5d5caaefdeeb6f84fb..e3ad2d6719736fb123f062bea7ffeb4a946b5725 100644 (file)
 #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;
+}