linux/arch/parisc/kernel/syscall.S
<<
>>
Prefs
   1/* 
   2 * Linux/PA-RISC Project (http://www.parisc-linux.org/)
   3 * 
   4 * System call entry code Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai>
   5 * Licensed under the GNU GPL.
   6 * thanks to Philipp Rumpf, Mike Shaver and various others
   7 * sorry about the wall, puffin..
   8 */
   9
  10#include <asm/asm-offsets.h>
  11#include <asm/unistd.h>
  12#include <asm/errno.h>
  13#include <asm/page.h>
  14#include <asm/psw.h>
  15#include <asm/thread_info.h>
  16#include <asm/assembly.h>
  17#include <asm/processor.h>
  18
  19#include <linux/linkage.h>
  20
  21        /* We fill the empty parts of the gateway page with
  22         * something that will kill the kernel or a
  23         * userspace application.
  24         */
  25#define KILL_INSN       break   0,0
  26
  27        .level          LEVEL
  28
  29        .text
  30
  31        .import syscall_exit,code
  32        .import syscall_exit_rfi,code
  33
  34        /* Linux gateway page is aliased to virtual page 0 in the kernel
  35         * address space. Since it is a gateway page it cannot be
  36         * dereferenced, so null pointers will still fault. We start
  37         * the actual entry point at 0x100. We put break instructions
  38         * at the beginning of the page to trap null indirect function
  39         * pointers.
  40         */
  41
  42        .align PAGE_SIZE
  43ENTRY(linux_gateway_page)
  44
  45        /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */
  46        .rept 44
  47        KILL_INSN
  48        .endr
  49
  50        /* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */
  51        /* Light-weight-syscall entry must always be located at 0xb0 */
  52        /* WARNING: Keep this number updated with table size changes */
  53#define __NR_lws_entries (2)
  54
  55lws_entry:
  56        gate    lws_start, %r0          /* increase privilege */
  57        depi    3, 31, 2, %r31          /* Ensure we return into user mode. */
  58
  59        /* Fill from 0xb8 to 0xe0 */
  60        .rept 10
  61        KILL_INSN
  62        .endr
  63
  64        /* This function MUST be located at 0xe0 for glibc's threading 
  65        mechanism to work. DO NOT MOVE THIS CODE EVER! */
  66set_thread_pointer:
  67        gate    .+8, %r0                /* increase privilege */
  68        depi    3, 31, 2, %r31          /* Ensure we return into user mode. */
  69        be      0(%sr7,%r31)            /* return to user space */
  70        mtctl   %r26, %cr27             /* move arg0 to the control register */
  71
  72        /* Increase the chance of trapping if random jumps occur to this
  73        address, fill from 0xf0 to 0x100 */
  74        .rept 4
  75        KILL_INSN
  76        .endr
  77
  78/* This address must remain fixed at 0x100 for glibc's syscalls to work */
  79        .align 256
  80linux_gateway_entry:
  81        gate    .+8, %r0                        /* become privileged */
  82        mtsp    %r0,%sr4                        /* get kernel space into sr4 */
  83        mtsp    %r0,%sr5                        /* get kernel space into sr5 */
  84        mtsp    %r0,%sr6                        /* get kernel space into sr6 */
  85        mfsp    %sr7,%r1                        /* save user sr7 */
  86        mtsp    %r1,%sr3                        /* and store it in sr3 */
  87
  88#ifdef CONFIG_64BIT
  89        /* for now we can *always* set the W bit on entry to the syscall
  90         * since we don't support wide userland processes.  We could
  91         * also save the current SM other than in r0 and restore it on
  92         * exit from the syscall, and also use that value to know
  93         * whether to do narrow or wide syscalls. -PB
  94         */
  95        ssm     PSW_SM_W, %r1
  96        extrd,u %r1,PSW_W_BIT,1,%r1
  97        /* sp must be aligned on 4, so deposit the W bit setting into
  98         * the bottom of sp temporarily */
  99        or,ev   %r1,%r30,%r30
 100        b,n     1f
 101        /* The top halves of argument registers must be cleared on syscall
 102         * entry from narrow executable.
 103         */
 104        depdi   0, 31, 32, %r26
 105        depdi   0, 31, 32, %r25
 106        depdi   0, 31, 32, %r24
 107        depdi   0, 31, 32, %r23
 108        depdi   0, 31, 32, %r22
 109        depdi   0, 31, 32, %r21
 1101:      
 111#endif
 112        mfctl   %cr30,%r1
 113        xor     %r1,%r30,%r30                   /* ye olde xor trick */
 114        xor     %r1,%r30,%r1
 115        xor     %r1,%r30,%r30
 116        
 117        ldo     THREAD_SZ_ALGN+FRAME_SIZE(%r30),%r30  /* set up kernel stack */
 118
 119        /* N.B.: It is critical that we don't set sr7 to 0 until r30
 120         *       contains a valid kernel stack pointer. It is also
 121         *       critical that we don't start using the kernel stack
 122         *       until after sr7 has been set to 0.
 123         */
 124
 125        mtsp    %r0,%sr7                        /* get kernel space into sr7 */
 126        STREGM  %r1,FRAME_SIZE(%r30)            /* save r1 (usp) here for now */
 127        mfctl   %cr30,%r1                       /* get task ptr in %r1 */
 128        LDREG   TI_TASK(%r1),%r1
 129
 130        /* Save some registers for sigcontext and potential task
 131           switch (see entry.S for the details of which ones are
 132           saved/restored).  TASK_PT_PSW is zeroed so we can see whether
 133           a process is on a syscall or not.  For an interrupt the real
 134           PSW value is stored.  This is needed for gdb and sys_ptrace. */
 135        STREG   %r0,  TASK_PT_PSW(%r1)
 136        STREG   %r2,  TASK_PT_GR2(%r1)          /* preserve rp */
 137        STREG   %r19, TASK_PT_GR19(%r1)
 138
 139        LDREGM  -FRAME_SIZE(%r30), %r2          /* get users sp back */
 140#ifdef CONFIG_64BIT
 141        extrd,u %r2,63,1,%r19                   /* W hidden in bottom bit */
 142#if 0
 143        xor     %r19,%r2,%r2                    /* clear bottom bit */
 144        depd,z  %r19,1,1,%r19
 145        std     %r19,TASK_PT_PSW(%r1)
 146#endif
 147#endif
 148        STREG   %r2,  TASK_PT_GR30(%r1)         /* ... and save it */
 149        
 150        STREG   %r20, TASK_PT_GR20(%r1)         /* Syscall number */
 151        STREG   %r21, TASK_PT_GR21(%r1)
 152        STREG   %r22, TASK_PT_GR22(%r1)
 153        STREG   %r23, TASK_PT_GR23(%r1)         /* 4th argument */
 154        STREG   %r24, TASK_PT_GR24(%r1)         /* 3rd argument */
 155        STREG   %r25, TASK_PT_GR25(%r1)         /* 2nd argument */
 156        STREG   %r26, TASK_PT_GR26(%r1)         /* 1st argument */
 157        STREG   %r27, TASK_PT_GR27(%r1)         /* user dp */
 158        STREG   %r28, TASK_PT_GR28(%r1)         /* return value 0 */
 159        STREG   %r28, TASK_PT_ORIG_R28(%r1)     /* return value 0 (saved for signals) */
 160        STREG   %r29, TASK_PT_GR29(%r1)         /* return value 1 */
 161        STREG   %r31, TASK_PT_GR31(%r1)         /* preserve syscall return ptr */
 162        
 163        ldo     TASK_PT_FR0(%r1), %r27          /* save fpregs from the kernel */
 164        save_fp %r27                            /* or potential task switch  */
 165
 166        mfctl   %cr11, %r27                     /* i.e. SAR */
 167        STREG   %r27, TASK_PT_SAR(%r1)
 168
 169        loadgp
 170
 171#ifdef CONFIG_64BIT
 172        ldo     -16(%r30),%r29                  /* Reference param save area */
 173        copy    %r19,%r2                        /* W bit back to r2 */
 174#else
 175        /* no need to save these on stack in wide mode because the first 8
 176         * args are passed in registers */
 177        stw     %r22, -52(%r30)                 /* 5th argument */
 178        stw     %r21, -56(%r30)                 /* 6th argument */
 179#endif
 180
 181        /* Are we being ptraced? */
 182        mfctl   %cr30, %r1
 183        LDREG   TI_TASK(%r1),%r1
 184        ldw     TASK_PTRACE(%r1), %r1
 185        bb,<,n  %r1,31,.Ltracesys
 186        
 187        /* Note!  We cannot use the syscall table that is mapped
 188        nearby since the gateway page is mapped execute-only. */
 189
 190#ifdef CONFIG_64BIT
 191        ldil    L%sys_call_table, %r1
 192        or,=    %r2,%r2,%r2
 193        addil   L%(sys_call_table64-sys_call_table), %r1
 194        ldo     R%sys_call_table(%r1), %r19
 195        or,=    %r2,%r2,%r2
 196        ldo     R%sys_call_table64(%r1), %r19
 197#else
 198        ldil    L%sys_call_table, %r1
 199        ldo     R%sys_call_table(%r1), %r19
 200#endif  
 201        comiclr,>>      __NR_Linux_syscalls, %r20, %r0
 202        b,n     .Lsyscall_nosys
 203        
 204        LDREGX  %r20(%r19), %r19
 205
 206        /* If this is a sys_rt_sigreturn call, and the signal was received
 207         * when not in_syscall, then we want to return via syscall_exit_rfi,
 208         * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
 209         * trampoline code in signal.c).
 210         */
 211        ldi     __NR_rt_sigreturn,%r2
 212        comb,=  %r2,%r20,.Lrt_sigreturn
 213.Lin_syscall:
 214        ldil    L%syscall_exit,%r2
 215        be      0(%sr7,%r19)
 216        ldo     R%syscall_exit(%r2),%r2
 217.Lrt_sigreturn:
 218        comib,<> 0,%r25,.Lin_syscall
 219        ldil    L%syscall_exit_rfi,%r2
 220        be      0(%sr7,%r19)
 221        ldo     R%syscall_exit_rfi(%r2),%r2
 222
 223        /* Note!  Because we are not running where we were linked, any
 224        calls to functions external to this file must be indirect.  To
 225        be safe, we apply the opposite rule to functions within this
 226        file, with local labels given to them to ensure correctness. */
 227        
 228.Lsyscall_nosys:
 229syscall_nosys:
 230        ldil    L%syscall_exit,%r1
 231        be      R%syscall_exit(%sr7,%r1)
 232        ldo     -ENOSYS(%r0),%r28                  /* set errno */
 233
 234
 235/* Warning! This trace code is a virtual duplicate of the code above so be
 236 * sure to maintain both! */
 237.Ltracesys:
 238tracesys:
 239        /* Need to save more registers so the debugger can see where we
 240         * are.  This saves only the lower 8 bits of PSW, so that the C
 241         * bit is still clear on syscalls, and the D bit is set if this
 242         * full register save path has been executed.  We check the D
 243         * bit on syscall_return_rfi to determine which registers to
 244         * restore.  An interrupt results in a full PSW saved with the
 245         * C bit set, a non-straced syscall entry results in C and D clear
 246         * in the saved PSW.
 247         */
 248        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 249        LDREG   TI_TASK(%r1), %r1
 250        ssm     0,%r2
 251        STREG   %r2,TASK_PT_PSW(%r1)            /* Lower 8 bits only!! */
 252        mfsp    %sr0,%r2
 253        STREG   %r2,TASK_PT_SR0(%r1)
 254        mfsp    %sr1,%r2
 255        STREG   %r2,TASK_PT_SR1(%r1)
 256        mfsp    %sr2,%r2
 257        STREG   %r2,TASK_PT_SR2(%r1)
 258        mfsp    %sr3,%r2
 259        STREG   %r2,TASK_PT_SR3(%r1)
 260        STREG   %r2,TASK_PT_SR4(%r1)
 261        STREG   %r2,TASK_PT_SR5(%r1)
 262        STREG   %r2,TASK_PT_SR6(%r1)
 263        STREG   %r2,TASK_PT_SR7(%r1)
 264        STREG   %r2,TASK_PT_IASQ0(%r1)
 265        STREG   %r2,TASK_PT_IASQ1(%r1)
 266        LDREG   TASK_PT_GR31(%r1),%r2
 267        STREG   %r2,TASK_PT_IAOQ0(%r1)
 268        ldo     4(%r2),%r2
 269        STREG   %r2,TASK_PT_IAOQ1(%r1)
 270        ldo     TASK_REGS(%r1),%r2
 271        /* reg_save %r2 */
 272        STREG   %r3,PT_GR3(%r2)
 273        STREG   %r4,PT_GR4(%r2)
 274        STREG   %r5,PT_GR5(%r2)
 275        STREG   %r6,PT_GR6(%r2)
 276        STREG   %r7,PT_GR7(%r2)
 277        STREG   %r8,PT_GR8(%r2)
 278        STREG   %r9,PT_GR9(%r2)
 279        STREG   %r10,PT_GR10(%r2)
 280        STREG   %r11,PT_GR11(%r2)
 281        STREG   %r12,PT_GR12(%r2)
 282        STREG   %r13,PT_GR13(%r2)
 283        STREG   %r14,PT_GR14(%r2)
 284        STREG   %r15,PT_GR15(%r2)
 285        STREG   %r16,PT_GR16(%r2)
 286        STREG   %r17,PT_GR17(%r2)
 287        STREG   %r18,PT_GR18(%r2)
 288        /* Finished saving things for the debugger */
 289
 290        copy    %r2,%r26
 291        ldil    L%do_syscall_trace_enter,%r1
 292        ldil    L%tracesys_next,%r2
 293        be      R%do_syscall_trace_enter(%sr7,%r1)
 294        ldo     R%tracesys_next(%r2),%r2
 295        
 296tracesys_next:
 297        /* do_syscall_trace_enter either returned the syscallno, or -1L,
 298         *  so we skip restoring the PT_GR20 below, since we pulled it from
 299         *  task->thread.regs.gr[20] above.
 300         */
 301        copy    %ret0,%r20
 302        ldil    L%sys_call_table,%r1
 303        ldo     R%sys_call_table(%r1), %r19
 304
 305        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 306        LDREG   TI_TASK(%r1), %r1
 307        LDREG   TASK_PT_GR26(%r1), %r26         /* Restore the users args */
 308        LDREG   TASK_PT_GR25(%r1), %r25
 309        LDREG   TASK_PT_GR24(%r1), %r24
 310        LDREG   TASK_PT_GR23(%r1), %r23
 311#ifdef CONFIG_64BIT
 312        LDREG   TASK_PT_GR22(%r1), %r22
 313        LDREG   TASK_PT_GR21(%r1), %r21
 314        ldo     -16(%r30),%r29                  /* Reference param save area */
 315#endif
 316
 317        comiclr,>>=     __NR_Linux_syscalls, %r20, %r0
 318        b,n     .Lsyscall_nosys
 319
 320        LDREGX  %r20(%r19), %r19
 321
 322        /* If this is a sys_rt_sigreturn call, and the signal was received
 323         * when not in_syscall, then we want to return via syscall_exit_rfi,
 324         * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
 325         * trampoline code in signal.c).
 326         */
 327        ldi     __NR_rt_sigreturn,%r2
 328        comb,=  %r2,%r20,.Ltrace_rt_sigreturn
 329.Ltrace_in_syscall:
 330        ldil    L%tracesys_exit,%r2
 331        be      0(%sr7,%r19)
 332        ldo     R%tracesys_exit(%r2),%r2
 333
 334        /* Do *not* call this function on the gateway page, because it
 335        makes a direct call to syscall_trace. */
 336        
 337tracesys_exit:
 338        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 339        LDREG   TI_TASK(%r1), %r1
 340#ifdef CONFIG_64BIT
 341        ldo     -16(%r30),%r29                  /* Reference param save area */
 342#endif
 343        ldo     TASK_REGS(%r1),%r26
 344        bl      do_syscall_trace_exit,%r2
 345        STREG   %r28,TASK_PT_GR28(%r1)          /* save return value now */
 346        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 347        LDREG   TI_TASK(%r1), %r1
 348        LDREG   TASK_PT_GR28(%r1), %r28         /* Restore return val. */
 349
 350        ldil    L%syscall_exit,%r1
 351        be,n    R%syscall_exit(%sr7,%r1)
 352
 353.Ltrace_rt_sigreturn:
 354        comib,<> 0,%r25,.Ltrace_in_syscall
 355        ldil    L%tracesys_sigexit,%r2
 356        be      0(%sr7,%r19)
 357        ldo     R%tracesys_sigexit(%r2),%r2
 358
 359tracesys_sigexit:
 360        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 361        LDREG   TI_TASK(%r1), %r1
 362#ifdef CONFIG_64BIT
 363        ldo     -16(%r30),%r29                  /* Reference param save area */
 364#endif
 365        bl      do_syscall_trace_exit,%r2
 366        ldo     TASK_REGS(%r1),%r26
 367
 368        ldil    L%syscall_exit_rfi,%r1
 369        be,n    R%syscall_exit_rfi(%sr7,%r1)
 370
 371
 372        /*********************************************************
 373                32/64-bit Light-Weight-Syscall ABI
 374
 375                * - Indicates a hint for userspace inline asm
 376                implementations.
 377
 378                Syscall number (caller-saves)
 379                - %r20
 380                * In asm clobber.
 381
 382                Argument registers (caller-saves)
 383                - %r26, %r25, %r24, %r23, %r22
 384                * In asm input.
 385
 386                Return registers (caller-saves)
 387                - %r28 (return), %r21 (errno)
 388                * In asm output.
 389
 390                Caller-saves registers
 391                - %r1, %r27, %r29
 392                - %r2 (return pointer)
 393                - %r31 (ble link register)
 394                * In asm clobber.
 395
 396                Callee-saves registers
 397                - %r3-%r18
 398                - %r30 (stack pointer)
 399                * Not in asm clobber.
 400
 401                If userspace is 32-bit:
 402                Callee-saves registers
 403                - %r19 (32-bit PIC register)
 404
 405                Differences from 32-bit calling convention:
 406                - Syscall number in %r20
 407                - Additional argument register %r22 (arg4)
 408                - Callee-saves %r19.
 409
 410                If userspace is 64-bit:
 411                Callee-saves registers
 412                - %r27 (64-bit PIC register)
 413
 414                Differences from 64-bit calling convention:
 415                - Syscall number in %r20
 416                - Additional argument register %r22 (arg4)
 417                - Callee-saves %r27.
 418
 419                Error codes returned by entry path:
 420
 421                ENOSYS - r20 was an invalid LWS number.
 422
 423        *********************************************************/
 424lws_start:
 425
 426#ifdef CONFIG_64BIT
 427        /* FIXME: If we are a 64-bit kernel just
 428         *        turn this on unconditionally.
 429         */
 430        ssm     PSW_SM_W, %r1
 431        extrd,u %r1,PSW_W_BIT,1,%r1
 432        /* sp must be aligned on 4, so deposit the W bit setting into
 433         * the bottom of sp temporarily */
 434        or,ev   %r1,%r30,%r30
 435
 436        /* Clip LWS number to a 32-bit value always */
 437        depdi   0, 31, 32, %r20
 438#endif  
 439
 440        /* Is the lws entry number valid? */
 441        comiclr,>>      __NR_lws_entries, %r20, %r0
 442        b,n     lws_exit_nosys
 443
 444        /* WARNING: Trashing sr2 and sr3 */
 445        mfsp    %sr7,%r1                        /* get userspace into sr3 */
 446        mtsp    %r1,%sr3
 447        mtsp    %r0,%sr2                        /* get kernel space into sr2 */
 448
 449        /* Load table start */
 450        ldil    L%lws_table, %r1
 451        ldo     R%lws_table(%r1), %r28  /* Scratch use of r28 */
 452        LDREGX  %r20(%sr2,r28), %r21    /* Scratch use of r21 */
 453
 454        /* Jump to lws, lws table pointers already relocated */
 455        be,n    0(%sr2,%r21)
 456
 457lws_exit_nosys:
 458        ldo     -ENOSYS(%r0),%r21                  /* set errno */
 459        /* Fall through: Return to userspace */
 460
 461lws_exit:
 462#ifdef CONFIG_64BIT
 463        /* decide whether to reset the wide mode bit
 464         *
 465         * For a syscall, the W bit is stored in the lowest bit
 466         * of sp.  Extract it and reset W if it is zero */
 467        extrd,u,*<>     %r30,63,1,%r1
 468        rsm     PSW_SM_W, %r0
 469        /* now reset the lowest bit of sp if it was set */
 470        xor     %r30,%r1,%r30
 471#endif
 472        be,n    0(%sr7, %r31)
 473
 474
 475        
 476        /***************************************************
 477                Implementing CAS as an atomic operation:
 478
 479                %r26 - Address to examine
 480                %r25 - Old value to check (old)
 481                %r24 - New value to set (new)
 482                %r28 - Return prev through this register.
 483                %r21 - Kernel error code
 484
 485                If debugging is DISabled:
 486
 487                %r21 has the following meanings:
 488
 489                EAGAIN - CAS is busy, ldcw failed, try again.
 490                EFAULT - Read or write failed.          
 491
 492                If debugging is enabled:
 493
 494                EDEADLOCK - CAS called recursively.
 495                EAGAIN && r28 == 1 - CAS is busy. Lock contended.
 496                EAGAIN && r28 == 2 - CAS is busy. ldcw failed.
 497                EFAULT - Read or write failed.
 498
 499                Scratch: r20, r28, r1
 500
 501        ****************************************************/
 502
 503        /* Do not enable LWS debugging */
 504#define ENABLE_LWS_DEBUG 0 
 505
 506        /* ELF64 Process entry path */
 507lws_compare_and_swap64:
 508#ifdef CONFIG_64BIT
 509        b,n     lws_compare_and_swap
 510#else
 511        /* If we are not a 64-bit kernel, then we don't
 512         * have 64-bit input registers, and calling
 513         * the 64-bit LWS CAS returns ENOSYS.
 514         */
 515        b,n     lws_exit_nosys
 516#endif
 517
 518        /* ELF32 Process entry path */
 519lws_compare_and_swap32:
 520#ifdef CONFIG_64BIT
 521        /* Clip all the input registers */
 522        depdi   0, 31, 32, %r26
 523        depdi   0, 31, 32, %r25
 524        depdi   0, 31, 32, %r24
 525#endif
 526
 527lws_compare_and_swap:
 528        /* Load start of lock table */
 529        ldil    L%lws_lock_start, %r20
 530        ldo     R%lws_lock_start(%r20), %r28
 531
 532        /* Extract four bits from r26 and hash lock (Bits 4-7) */
 533        extru  %r26, 27, 4, %r20
 534
 535        /* Find lock to use, the hash is either one of 0 to
 536           15, multiplied by 16 (keep it 16-byte aligned)
 537           and add to the lock table offset. */
 538        shlw    %r20, 4, %r20
 539        add     %r20, %r28, %r20
 540
 541# if ENABLE_LWS_DEBUG
 542        /*      
 543                DEBUG, check for deadlock! 
 544                If the thread register values are the same
 545                then we were the one that locked it last and
 546                this is a recurisve call that will deadlock.
 547                We *must* giveup this call and fail.
 548        */
 549        ldw     4(%sr2,%r20), %r28                      /* Load thread register */
 550        /* WARNING: If cr27 cycles to the same value we have problems */
 551        mfctl   %cr27, %r21                             /* Get current thread register */
 552        cmpb,<>,n       %r21, %r28, cas_lock            /* Called recursive? */
 553        b       lws_exit                                /* Return error! */
 554        ldo     -EDEADLOCK(%r0), %r21
 555cas_lock:
 556        cmpb,=,n        %r0, %r28, cas_nocontend        /* Is nobody using it? */
 557        ldo     1(%r0), %r28                            /* 1st case */
 558        b       lws_exit                                /* Contended... */
 559        ldo     -EAGAIN(%r0), %r21                      /* Spin in userspace */
 560cas_nocontend:
 561# endif
 562/* ENABLE_LWS_DEBUG */
 563
 564        LDCW    0(%sr2,%r20), %r28                      /* Try to acquire the lock */
 565        cmpb,<>,n       %r0, %r28, cas_action           /* Did we get it? */
 566cas_wouldblock:
 567        ldo     2(%r0), %r28                            /* 2nd case */
 568        b       lws_exit                                /* Contended... */
 569        ldo     -EAGAIN(%r0), %r21                      /* Spin in userspace */
 570
 571        /*
 572                prev = *addr;
 573                if ( prev == old )
 574                  *addr = new;
 575                return prev;
 576        */
 577
 578        /* NOTES:
 579                This all works becuse intr_do_signal
 580                and schedule both check the return iasq
 581                and see that we are on the kernel page
 582                so this process is never scheduled off
 583                or is ever sent any signal of any sort,
 584                thus it is wholly atomic from usrspaces
 585                perspective
 586        */
 587cas_action:
 588#if defined CONFIG_SMP && ENABLE_LWS_DEBUG
 589        /* DEBUG */
 590        mfctl   %cr27, %r1
 591        stw     %r1, 4(%sr2,%r20)
 592#endif
 593        /* The load and store could fail */
 5941:      ldw     0(%sr3,%r26), %r28
 595        sub,<>  %r28, %r25, %r0
 5962:      stw     %r24, 0(%sr3,%r26)
 597        /* Free lock */
 598        stw     %r20, 0(%sr2,%r20)
 599#if ENABLE_LWS_DEBUG
 600        /* Clear thread register indicator */
 601        stw     %r0, 4(%sr2,%r20)
 602#endif
 603        /* Return to userspace, set no error */
 604        b       lws_exit
 605        copy    %r0, %r21
 606
 6073:              
 608        /* Error occured on load or store */
 609        /* Free lock */
 610        stw     %r20, 0(%sr2,%r20)
 611#if ENABLE_LWS_DEBUG
 612        stw     %r0, 4(%sr2,%r20)
 613#endif
 614        b       lws_exit
 615        ldo     -EFAULT(%r0),%r21       /* set errno */
 616        nop
 617        nop
 618        nop
 619        nop
 620
 621        /* Two exception table entries, one for the load,
 622           the other for the store. Either return -EFAULT.
 623           Each of the entries must be relocated. */
 624        .section __ex_table,"aw"
 625        ASM_ULONG_INSN (1b - linux_gateway_page), (3b - linux_gateway_page)
 626        ASM_ULONG_INSN (2b - linux_gateway_page), (3b - linux_gateway_page)
 627        .previous
 628
 629
 630        /* Make sure nothing else is placed on this page */
 631        .align PAGE_SIZE
 632END(linux_gateway_page)
 633ENTRY(end_linux_gateway_page)
 634
 635        /* Relocate symbols assuming linux_gateway_page is mapped
 636           to virtual address 0x0 */
 637
 638#define LWS_ENTRY(_name_) ASM_ULONG_INSN (lws_##_name_ - linux_gateway_page)
 639
 640        .section .rodata,"a"
 641
 642        .align PAGE_SIZE
 643        /* Light-weight-syscall table */
 644        /* Start of lws table. */
 645ENTRY(lws_table)
 646        LWS_ENTRY(compare_and_swap32)   /* 0 - ELF32 Atomic compare and swap */
 647        LWS_ENTRY(compare_and_swap64)   /* 1 - ELF64 Atomic compare and swap */
 648END(lws_table)
 649        /* End of lws table */
 650
 651        .align PAGE_SIZE
 652ENTRY(sys_call_table)
 653#include "syscall_table.S"
 654END(sys_call_table)
 655
 656#ifdef CONFIG_64BIT
 657        .align PAGE_SIZE
 658ENTRY(sys_call_table64)
 659#define SYSCALL_TABLE_64BIT
 660#include "syscall_table.S"
 661END(sys_call_table64)
 662#endif
 663
 664        /*
 665                All light-weight-syscall atomic operations 
 666                will use this set of locks 
 667
 668                NOTE: The lws_lock_start symbol must be
 669                at least 16-byte aligned for safe use
 670                with ldcw.
 671        */
 672        .section .data
 673        .align  PAGE_SIZE
 674ENTRY(lws_lock_start)
 675        /* lws locks */
 676        .rept 16
 677        /* Keep locks aligned at 16-bytes */
 678        .word 1
 679        .word 0 
 680        .word 0
 681        .word 0
 682        .endr
 683END(lws_lock_start)
 684        .previous
 685
 686.end
 687
 688
 689