fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / parisc / kernel / syscall.S
index 1c34cf7..a058004 100644 (file)
@@ -7,7 +7,7 @@
  * sorry about the wall, puffin..
  */
 
-#include <asm/offsets.h>
+#include <asm/asm-offsets.h>
 #include <asm/unistd.h>
 #include <asm/errno.h>
 #include <asm/psw.h>
 #include <asm/assembly.h>
 #include <asm/processor.h>
 
-#ifdef __LP64__
+       /* 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
+
+#ifdef CONFIG_64BIT
        .level          2.0w
 #else
        .level          1.1
 #endif
+
        .text
 
        .import syscall_exit,code
         * pointers.
         */
 
-       .align 4096
+       .align ASM_PAGE_SIZE
 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 */
@@ -62,7 +89,7 @@ linux_gateway_entry:
        mfsp    %sr7,%r1                        /* save user sr7 */
        mtsp    %r1,%sr3                        /* and store it in sr3 */
 
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
        /* for now we can *always* set the W bit on entry to the syscall
         * since we don't support wide userland processes.  We could
         * also save the current SM other than in r0 and restore it on
@@ -114,7 +141,7 @@ linux_gateway_entry:
        STREG   %r19, TASK_PT_GR19(%r1)
 
        LDREGM  -FRAME_SIZE(%r30), %r2          /* get users sp back */
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
        extrd,u %r2,63,1,%r19                   /* W hidden in bottom bit */
 #if 0
        xor     %r19,%r2,%r2                    /* clear bottom bit */
@@ -124,7 +151,7 @@ linux_gateway_entry:
 #endif
        STREG   %r2,  TASK_PT_GR30(%r1)         /* ... and save it */
        
-       STREG   %r20, TASK_PT_GR20(%r1)
+       STREG   %r20, TASK_PT_GR20(%r1)         /* Syscall number */
        STREG   %r21, TASK_PT_GR21(%r1)
        STREG   %r22, TASK_PT_GR22(%r1)
        STREG   %r23, TASK_PT_GR23(%r1)         /* 4th argument */
@@ -145,7 +172,7 @@ linux_gateway_entry:
 
        loadgp
 
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
        ldo     -16(%r30),%r29                  /* Reference param save area */
        copy    %r19,%r2                        /* W bit back to r2 */
 #else
@@ -157,13 +184,14 @@ 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
        
        /* Note!  We cannot use the syscall table that is mapped
        nearby since the gateway page is mapped execute-only. */
 
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
        ldil    L%sys_call_table, %r1
        or,=    %r2,%r2,%r2
        addil   L%(sys_call_table64-sys_call_table), %r1
@@ -177,11 +205,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
@@ -282,7 +307,7 @@ tracesys_next:
        LDREG   TASK_PT_GR25(%r1), %r25
        LDREG   TASK_PT_GR24(%r1), %r24
        LDREG   TASK_PT_GR23(%r1), %r23
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
        LDREG   TASK_PT_GR22(%r1), %r22
        LDREG   TASK_PT_GR21(%r1), %r21
        ldo     -16(%r30),%r29                  /* Reference param save area */
@@ -291,11 +316,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
@@ -314,7 +336,7 @@ tracesys_next:
 tracesys_exit:
        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
        LDREG   TI_TASK(%r1), %r1
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
        ldo     -16(%r30),%r29                  /* Reference param save area */
 #endif
        bl      syscall_trace, %r2
@@ -335,7 +357,7 @@ tracesys_exit:
 tracesys_sigexit:
        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
        LDREG   0(%r1), %r1
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
        ldo     -16(%r30),%r29                  /* Reference param save area */
 #endif
        bl      syscall_trace, %r2
@@ -344,14 +366,297 @@ tracesys_sigexit:
        ldil    L%syscall_exit_rfi,%r1
        be,n    R%syscall_exit_rfi(%sr7,%r1)
 
-       .align 4096
+
+       /*********************************************************
+               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 CONFIG_64BIT
+       /* 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 CONFIG_64BIT
+       /* 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 CONFIG_64BIT
+       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 CONFIG_64BIT
+       /* 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 */
+       /* WARNING: If cr27 cycles to the same value we have problems */
+       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 CONFIG_64BIT
+       /* 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 CONFIG_64BIT
+       /* 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 ASM_PAGE_SIZE
+       .export end_linux_gateway_page
+end_linux_gateway_page:
+
+       /* Relocate symbols assuming linux_gateway_page is mapped
+          to virtual address 0x0 */
+#ifdef CONFIG_64BIT
+       /* 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
+
+       .section .rodata,"a"
+
+       .align ASM_PAGE_SIZE
+       /* 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 ASM_PAGE_SIZE
        .export sys_call_table
 .Lsys_call_table:
 sys_call_table:
 #include "syscall_table.S"
-.end
-#ifdef __LP64__
-       .align 4096
+
+#ifdef CONFIG_64BIT
+       .align ASM_PAGE_SIZE
        .export sys_call_table64
 .Lsys_call_table64:
 sys_call_table64:
@@ -359,10 +664,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: