vserver 1.9.5.x5
[linux-2.6.git] / arch / parisc / kernel / syscall.S
index 1c34cf7..32ea701 100644 (file)
 #include <asm/assembly.h>
 #include <asm/processor.h>
 
+       /* We fill the empty parts of the gateway page with
+        * something that will kill the kernel or a
+        * userspace application.
+        */
+#define KILL_INSN      break   0,0
+
+#include <linux/config.h> /* for CONFIG_SMP */
+
 #ifdef __LP64__
        .level          2.0w
 #else
        .level          1.1
 #endif
+
+#ifndef __LP64__
+       .macro fixup_branch,lbl
+       b           \lbl
+       .endm
+#else
+       .macro fixup_branch,lbl
+       ldil        L%\lbl, %r1
+       ldo         R%\lbl(%r1), %r1
+       bv,n        %r0(%r1)
+       .endm
+#endif
+
        .text
 
        .import syscall_exit,code
        .align 4096
 linux_gateway_page:
 
-       .rept 56
-       break   0,0
+        /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */
+       .rept 44
+       KILL_INSN
        .endr
 
+       /* ADDRESS 0xb0 to 0xb4, lws uses 1 insns for entry */
+       /* Light-weight-syscall entry must always be located at 0xb0 */
+       /* WARNING: Keep this number updated with table size changes */
+#define __NR_lws_entries (2)
+
+lws_entry:
+       /* Unconditional branch to lws_start, located on the 
+          same gateway page */
+       b,n     lws_start
+
+       /* Fill from 0xb4 to 0xe0 */
+       .rept 11
+       KILL_INSN
+       .endr
+
+       /* This function MUST be located at 0xe0 for glibc's threading 
+       mechanism to work. DO NOT MOVE THIS CODE EVER! */
 set_thread_pointer:
        gate    .+8, %r0                /* increase privilege */
        depi    3, 31, 2, %r31          /* Ensure we return into user mode. */
        be      0(%sr7,%r31)            /* return to user space */
        mtctl   %r26, %cr27             /* move arg0 to the control register */
 
+       /* Increase the chance of trapping if random jumps occur to this
+       address, fill from 0xf0 to 0x100 */
        .rept 4
-       break   0,0
+       KILL_INSN
        .endr
 
-/* This address must remain fixed, or user binaries go splat. */
+/* This address must remain fixed at 0x100 for glibc's syscalls to work */
        .align 256
 linux_gateway_entry:
        gate    .+8, %r0                        /* become privileged */
@@ -157,6 +198,7 @@ linux_gateway_entry:
 
        /* Are we being ptraced? */
        mfctl   %cr30, %r1
+       LDREG   TI_TASK(%r1),%r1
        LDREG   TASK_PTRACE(%r1), %r1
        bb,<,n  %r1,31,.Ltracesys
        
@@ -177,11 +219,8 @@ linux_gateway_entry:
        comiclr,>>=     __NR_Linux_syscalls, %r20, %r0
        b,n     .Lsyscall_nosys
        
-#ifdef __LP64__
-       ldd,s   %r20(%r19), %r19
-#else
-       ldwx,s  %r20(%r19), %r19
-#endif
+       LDREGX  %r20(%r19), %r19
+
        /* If this is a sys_rt_sigreturn call, and the signal was received
         * when not in_syscall, then we want to return via syscall_exit_rfi,
         * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
@@ -291,11 +330,8 @@ tracesys_next:
        comiclr,>>=     __NR_Linux_syscalls, %r20, %r0
        b,n     .Lsyscall_nosys
 
-#ifdef __LP64__
-       ldd,s   %r20(%r19), %r19
-#else
-       ldwx,s  %r20(%r19), %r19
-#endif
+       LDREGX  %r20(%r19), %r19
+
        /* If this is a sys_rt_sigreturn call, and the signal was received
         * when not in_syscall, then we want to return via syscall_exit_rfi,
         * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
@@ -344,12 +380,292 @@ tracesys_sigexit:
        ldil    L%syscall_exit_rfi,%r1
        be,n    R%syscall_exit_rfi(%sr7,%r1)
 
+
+       /*********************************************************
+               Light-weight-syscall code
+
+               r20 - lws number
+               r26,r25,r24,r23,r22 - Input registers
+               r28 - Function return register
+               r21 - Error code.
+
+               Scracth: Any of the above that aren't being
+               currently used, including r1. 
+
+               Return pointer: r31 (Not usable)
+
+               Error codes returned by entry path:
+
+               ENOSYS - r20 was an invalid LWS number.
+
+       *********************************************************/
+lws_start:
+       /* Gate and ensure we return to userspace */
+       gate    .+8, %r0
+       depi    3, 31, 2, %r31  /* Ensure we return to userspace */
+
+#ifdef __LP64__
+       /* FIXME: If we are a 64-bit kernel just
+        *        turn this on unconditionally.
+        */
+       ssm     PSW_SM_W, %r1
+       extrd,u %r1,PSW_W_BIT,1,%r1
+       /* sp must be aligned on 4, so deposit the W bit setting into
+        * the bottom of sp temporarily */
+       or,ev   %r1,%r30,%r30
+
+       /* Clip LWS number to a 32-bit value always */
+       depdi   0, 31, 32, %r20
+#endif 
+
+        /* Is the lws entry number valid? */
+       comiclr,>>=     __NR_lws_entries, %r20, %r0
+       b,n     lws_exit_nosys
+
+       /* WARNING: Trashing sr2 and sr3 */
+       mfsp    %sr7,%r1                        /* get userspace into sr3 */
+       mtsp    %r1,%sr3
+       mtsp    %r0,%sr2                        /* get kernel space into sr2 */
+
+       /* Load table start */
+       ldil    L%lws_table, %r1
+       ldo     R%lws_table(%r1), %r28  /* Scratch use of r28 */
+       LDREGX  %r20(%sr2,r28), %r21    /* Scratch use of r21 */
+
+       /* Jump to lws, lws table pointers already relocated */
+       be,n    0(%sr2,%r21)
+
+lws_exit_nosys:
+       ldo     -ENOSYS(%r0),%r21                  /* set errno */
+       /* Fall through: Return to userspace */
+
+lws_exit:
+#ifdef __LP64__
+       /* decide whether to reset the wide mode bit
+        *
+        * For a syscall, the W bit is stored in the lowest bit
+        * of sp.  Extract it and reset W if it is zero */
+       extrd,u,*<>     %r30,63,1,%r1
+       rsm     PSW_SM_W, %r0
+       /* now reset the lowest bit of sp if it was set */
+       xor     %r30,%r1,%r30
+#endif
+       be,n    0(%sr3, %r31)
+
+
+       
+       /***************************************************
+               Implementing CAS as an atomic operation:
+
+               %r26 - Address to examine
+               %r25 - Old value to check (old)
+               %r24 - New value to set (new)
+               %r28 - Return prev through this register.
+               %r21 - Kernel error code
+
+               If debugging is DISabled:
+
+               %r21 has the following meanings:
+
+               EAGAIN - CAS is busy, ldcw failed, try again.
+               EFAULT - Read or write failed.          
+
+               If debugging is enabled:
+
+               EDEADLOCK - CAS called recursively.
+               EAGAIN && r28 == 1 - CAS is busy. Lock contended.
+               EAGAIN && r28 == 2 - CAS is busy. ldcw failed.
+               EFAULT - Read or write failed.
+
+               Scratch: r20, r28, r1
+
+       ****************************************************/
+
+       /* Do not enable LWS debugging */
+#define ENABLE_LWS_DEBUG 0 
+
+       /* ELF64 Process entry path */
+lws_compare_and_swap64:
+#ifdef __LP64__
+       b,n     lws_compare_and_swap
+#else
+       /* If we are not a 64-bit kernel, then we don't
+        * implement having 64-bit input registers
+        */
+       b,n     lws_exit_nosys
+#endif
+
+       /* ELF32 Process entry path */
+lws_compare_and_swap32:
+#ifdef __LP64__
+       /* Clip all the input registers */
+       depdi   0, 31, 32, %r26
+       depdi   0, 31, 32, %r25
+       depdi   0, 31, 32, %r24
+#endif
+
+lws_compare_and_swap:
+#ifdef CONFIG_SMP
+       /* Load start of lock table */
+       ldil    L%lws_lock_start, %r20
+       ldo     R%lws_lock_start(%r20), %r28
+
+       /* Extract four bits from r26 and hash lock (Bits 4-7) */
+       extru  %r26, 27, 4, %r20
+
+       /* Find lock to use, the hash is either one of 0 to
+          15, multiplied by 16 (keep it 16-byte aligned)
+          and add to the lock table offset. */
+       shlw    %r20, 4, %r20
+       add     %r20, %r28, %r20
+
+# ifdef ENABLE_LWS_DEBUG
+       /*      
+               DEBUG, check for deadlock! 
+               If the thread register values are the same
+               then we were the one that locked it last and
+               this is a recurisve call that will deadlock.
+               We *must* giveup this call and fail.
+       */
+       ldw     4(%sr2,%r20), %r28                      /* Load thread register */
+       mfctl   %cr27, %r21                             /* Get current thread register */
+       cmpb,<>,n       %r21, %r28, cas_lock            /* Called recursive? */
+       b       lws_exit                                /* Return error! */
+       ldo     -EDEADLOCK(%r0), %r21
+cas_lock:
+       cmpb,=,n        %r0, %r28, cas_nocontend        /* Is nobody using it? */
+       ldo     1(%r0), %r28                            /* 1st case */
+       b       lws_exit                                /* Contended... */
+       ldo     -EAGAIN(%r0), %r21                      /* Spin in userspace */
+cas_nocontend:
+# endif
+/* ENABLE_LWS_DEBUG */
+
+       ldcw    0(%sr2,%r20), %r28                      /* Try to acquire the lock */
+       cmpb,<>,n       %r0, %r28, cas_action           /* Did we get it? */
+cas_wouldblock:
+       ldo     2(%r0), %r28                            /* 2nd case */
+       b       lws_exit                                /* Contended... */
+       ldo     -EAGAIN(%r0), %r21                      /* Spin in userspace */
+#endif
+/* CONFIG_SMP */
+
+       /*
+               prev = *addr;
+               if ( prev == old )
+                 *addr = new;
+               return prev;
+       */
+
+       /* NOTES:
+               This all works becuse intr_do_signal
+               and schedule both check the return iasq
+               and see that we are on the kernel page
+               so this process is never scheduled off
+               or is ever sent any signal of any sort,
+               thus it is wholly atomic from usrspaces
+               perspective
+       */
+cas_action:
+#if defined CONFIG_SMP && defined ENABLE_LWS_DEBUG
+       /* DEBUG */
+       mfctl   %cr27, %r1
+       stw     %r1, 4(%sr2,%r20)
+#endif
+       /* The load and store could fail */
+1:     ldw     0(%sr3,%r26), %r28
+       sub,<>  %r28, %r25, %r0
+2:     stw     %r24, 0(%sr3,%r26)
+#ifdef CONFIG_SMP
+       /* Free lock */
+       stw     %r20, 0(%sr2,%r20)
+# ifdef ENABLE_LWS_DEBUG
+       /* Clear thread register indicator */
+       stw     %r0, 4(%sr2,%r20)
+# endif
+#endif
+       /* Return to userspace, set no error */
+       b       lws_exit
+       copy    %r0, %r21
+
+3:             
+       /* Error occured on load or store */
+#ifdef CONFIG_SMP
+       /* Free lock */
+       stw     %r20, 0(%sr2,%r20)
+# ifdef ENABLE_LWS_DEBUG
+       stw     %r0, 4(%sr2,%r20)
+# endif
+#endif
+       b       lws_exit
+       ldo     -EFAULT(%r0),%r21       /* set errno */
+       nop
+       nop
+       nop
+       nop
+
+       /* Two exception table entries, one for the load,
+          the other for the store. Either return -EFAULT.
+          Each of the entries must be relocated. */
+       .section __ex_table,"aw"
+#ifdef __LP64__
+       /* Pad the address calculation */
+       .word   0,(2b - linux_gateway_page)
+       .word   0,(3b - linux_gateway_page)
+#else
+       .word   (2b - linux_gateway_page)
+       .word   (3b - linux_gateway_page)
+#endif
+       .previous
+
+       .section __ex_table,"aw"
+#ifdef __LP64__
+       /* Pad the address calculation */
+       .word   0,(1b - linux_gateway_page)
+       .word   0,(3b - linux_gateway_page)
+#else
+       .word   (1b - linux_gateway_page)
+       .word   (3b - linux_gateway_page)
+#endif
+       .previous
+
+end_compare_and_swap:
+
+       /* Make sure nothing else is placed on this page */
+       .align 4096
+       .export end_linux_gateway_page
+end_linux_gateway_page:
+
+       /* Relocate symbols assuming linux_gateway_page is mapped
+          to virtual address 0x0 */
+#ifdef __LP64__
+       /* FIXME: The code will always be on the gateay page
+                 and thus it will be on the first 4k, the
+                 assembler seems to think that the final
+                 subtraction result is only a word in
+                 length, so we pad the value.
+       */
+#define LWS_ENTRY(_name_) .word 0,(lws_##_name_ - linux_gateway_page)
+#else
+#define LWS_ENTRY(_name_) .word  (lws_##_name_ - linux_gateway_page)
+#endif
+
+       .align 4096
+       /* Light-weight-syscall table */
+       /* Start of lws table. */
+       .export lws_table
+.Llws_table:
+lws_table:
+       LWS_ENTRY(compare_and_swap32)   /* 0 - ELF32 Atomic compare and swap */
+       LWS_ENTRY(compare_and_swap64)   /* 1 - ELF64 Atomic compare and swap */
+       /* End of lws table */
+
        .align 4096
        .export sys_call_table
 .Lsys_call_table:
 sys_call_table:
 #include "syscall_table.S"
-.end
+
 #ifdef __LP64__
        .align 4096
        .export sys_call_table64
@@ -359,10 +675,29 @@ sys_call_table64:
 #include "syscall_table.S"
 #endif
 
+#ifdef CONFIG_SMP
+       /*
+               All light-weight-syscall atomic operations 
+               will use this set of locks 
+       */
+       .section .data
+       .align 4096
+       .export lws_lock_start
+.Llws_lock_start:
+lws_lock_start:
+       /* lws locks */
+       .align 16
+       .rept 16
+       /* Keep locks aligned at 16-bytes */
+       .word 1
+       .word 0 
+       .word 0
+       .word 0
+       .endr
+       .previous
+#endif
+/* CONFIG_SMP for lws_lock_start */
 
-       /* Make sure nothing else is placed on this page */
+.end
 
-       .align 4096
-       .export end_linux_gateway_page
-end_linux_gateway_page: