linux/arch/parisc/kernel/syscall.S
<<
>>
Prefs
   1/* 
   2 * Linux/PA-RISC Project (http://www.parisc-linux.org/)
   3 * 
   4 * System call entry code / Linux gateway page
   5 * Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai>
   6 * Licensed under the GNU GPL.
   7 * thanks to Philipp Rumpf, Mike Shaver and various others
   8 * sorry about the wall, puffin..
   9 */
  10
  11/*
  12How does the Linux gateway page on PA-RISC work?
  13------------------------------------------------
  14The Linux gateway page on PA-RISC is "special".
  15It actually has PAGE_GATEWAY bits set (this is linux terminology; in parisc
  16terminology it's Execute, promote to PL0) in the page map.  So anything
  17executing on this page executes with kernel level privilege (there's more to it
  18than that: to have this happen, you also have to use a branch with a ,gate
  19completer to activate the privilege promotion).  The upshot is that everything
  20that runs on the gateway page runs at kernel privilege but with the current
  21user process address space (although you have access to kernel space via %sr2).
  22For the 0x100 syscall entry, we redo the space registers to point to the kernel
  23address space (preserving the user address space in %sr3), move to wide mode if
  24required, save the user registers and branch into the kernel syscall entry
  25point.  For all the other functions, we execute at kernel privilege but don't
  26flip address spaces. The basic upshot of this is that these code snippets are
  27executed atomically (because the kernel can't be pre-empted) and they may
  28perform architecturally forbidden (to PL3) operations (like setting control
  29registers).
  30*/
  31
  32
  33#include <asm/asm-offsets.h>
  34#include <asm/unistd.h>
  35#include <asm/errno.h>
  36#include <asm/page.h>
  37#include <asm/psw.h>
  38#include <asm/thread_info.h>
  39#include <asm/assembly.h>
  40#include <asm/processor.h>
  41#include <asm/cache.h>
  42
  43#include <linux/linkage.h>
  44
  45        /* We fill the empty parts of the gateway page with
  46         * something that will kill the kernel or a
  47         * userspace application.
  48         */
  49#define KILL_INSN       break   0,0
  50
  51        .level          LEVEL
  52
  53        .text
  54
  55        .import syscall_exit,code
  56        .import syscall_exit_rfi,code
  57
  58        /* Linux gateway page is aliased to virtual page 0 in the kernel
  59         * address space. Since it is a gateway page it cannot be
  60         * dereferenced, so null pointers will still fault. We start
  61         * the actual entry point at 0x100. We put break instructions
  62         * at the beginning of the page to trap null indirect function
  63         * pointers.
  64         */
  65
  66        .align PAGE_SIZE
  67ENTRY(linux_gateway_page)
  68
  69        /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */
  70        .rept 44
  71        KILL_INSN
  72        .endr
  73
  74        /* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */
  75        /* Light-weight-syscall entry must always be located at 0xb0 */
  76        /* WARNING: Keep this number updated with table size changes */
  77#define __NR_lws_entries (3)
  78
  79lws_entry:
  80        gate    lws_start, %r0          /* increase privilege */
  81        depi    3, 31, 2, %r31          /* Ensure we return into user mode. */
  82
  83        /* Fill from 0xb8 to 0xe0 */
  84        .rept 10
  85        KILL_INSN
  86        .endr
  87
  88        /* This function MUST be located at 0xe0 for glibc's threading 
  89        mechanism to work. DO NOT MOVE THIS CODE EVER! */
  90set_thread_pointer:
  91        gate    .+8, %r0                /* increase privilege */
  92        depi    3, 31, 2, %r31          /* Ensure we return into user mode. */
  93        be      0(%sr7,%r31)            /* return to user space */
  94        mtctl   %r26, %cr27             /* move arg0 to the control register */
  95
  96        /* Increase the chance of trapping if random jumps occur to this
  97        address, fill from 0xf0 to 0x100 */
  98        .rept 4
  99        KILL_INSN
 100        .endr
 101
 102/* This address must remain fixed at 0x100 for glibc's syscalls to work */
 103        .align LINUX_GATEWAY_ADDR
 104linux_gateway_entry:
 105        gate    .+8, %r0                        /* become privileged */
 106        mtsp    %r0,%sr4                        /* get kernel space into sr4 */
 107        mtsp    %r0,%sr5                        /* get kernel space into sr5 */
 108        mtsp    %r0,%sr6                        /* get kernel space into sr6 */
 109
 110#ifdef CONFIG_64BIT
 111        /* for now we can *always* set the W bit on entry to the syscall
 112         * since we don't support wide userland processes.  We could
 113         * also save the current SM other than in r0 and restore it on
 114         * exit from the syscall, and also use that value to know
 115         * whether to do narrow or wide syscalls. -PB
 116         */
 117        ssm     PSW_SM_W, %r1
 118        extrd,u %r1,PSW_W_BIT,1,%r1
 119        /* sp must be aligned on 4, so deposit the W bit setting into
 120         * the bottom of sp temporarily */
 121        or,ev   %r1,%r30,%r30
 122        b,n     1f
 123        /* The top halves of argument registers must be cleared on syscall
 124         * entry from narrow executable.
 125         */
 126        depdi   0, 31, 32, %r26
 127        depdi   0, 31, 32, %r25
 128        depdi   0, 31, 32, %r24
 129        depdi   0, 31, 32, %r23
 130        depdi   0, 31, 32, %r22
 131        depdi   0, 31, 32, %r21
 1321:      
 133#endif
 134
 135        /* We use a rsm/ssm pair to prevent sr3 from being clobbered
 136         * by external interrupts.
 137         */
 138        mfsp    %sr7,%r1                        /* save user sr7 */
 139        rsm     PSW_SM_I, %r0                   /* disable interrupts */
 140        mtsp    %r1,%sr3                        /* and store it in sr3 */
 141
 142        mfctl   %cr30,%r1
 143        xor     %r1,%r30,%r30                   /* ye olde xor trick */
 144        xor     %r1,%r30,%r1
 145        xor     %r1,%r30,%r30
 146        
 147        ldo     THREAD_SZ_ALGN+FRAME_SIZE(%r30),%r30  /* set up kernel stack */
 148
 149        /* N.B.: It is critical that we don't set sr7 to 0 until r30
 150         *       contains a valid kernel stack pointer. It is also
 151         *       critical that we don't start using the kernel stack
 152         *       until after sr7 has been set to 0.
 153         */
 154
 155        mtsp    %r0,%sr7                        /* get kernel space into sr7 */
 156        ssm     PSW_SM_I, %r0                   /* enable interrupts */
 157        STREGM  %r1,FRAME_SIZE(%r30)            /* save r1 (usp) here for now */
 158        mfctl   %cr30,%r1                       /* get task ptr in %r1 */
 159        LDREG   TI_TASK(%r1),%r1
 160
 161        /* Save some registers for sigcontext and potential task
 162           switch (see entry.S for the details of which ones are
 163           saved/restored).  TASK_PT_PSW is zeroed so we can see whether
 164           a process is on a syscall or not.  For an interrupt the real
 165           PSW value is stored.  This is needed for gdb and sys_ptrace. */
 166        STREG   %r0,  TASK_PT_PSW(%r1)
 167        STREG   %r2,  TASK_PT_GR2(%r1)          /* preserve rp */
 168        STREG   %r19, TASK_PT_GR19(%r1)
 169
 170        LDREGM  -FRAME_SIZE(%r30), %r2          /* get users sp back */
 171#ifdef CONFIG_64BIT
 172        extrd,u %r2,63,1,%r19                   /* W hidden in bottom bit */
 173#if 0
 174        xor     %r19,%r2,%r2                    /* clear bottom bit */
 175        depd,z  %r19,1,1,%r19
 176        std     %r19,TASK_PT_PSW(%r1)
 177#endif
 178#endif
 179        STREG   %r2,  TASK_PT_GR30(%r1)         /* ... and save it */
 180        
 181        STREG   %r20, TASK_PT_GR20(%r1)         /* Syscall number */
 182        STREG   %r21, TASK_PT_GR21(%r1)
 183        STREG   %r22, TASK_PT_GR22(%r1)
 184        STREG   %r23, TASK_PT_GR23(%r1)         /* 4th argument */
 185        STREG   %r24, TASK_PT_GR24(%r1)         /* 3rd argument */
 186        STREG   %r25, TASK_PT_GR25(%r1)         /* 2nd argument */
 187        STREG   %r26, TASK_PT_GR26(%r1)         /* 1st argument */
 188        STREG   %r27, TASK_PT_GR27(%r1)         /* user dp */
 189        STREG   %r28, TASK_PT_GR28(%r1)         /* return value 0 */
 190        STREG   %r0, TASK_PT_ORIG_R28(%r1)      /* don't prohibit restarts */
 191        STREG   %r29, TASK_PT_GR29(%r1)         /* return value 1 */
 192        STREG   %r31, TASK_PT_GR31(%r1)         /* preserve syscall return ptr */
 193        
 194        ldo     TASK_PT_FR0(%r1), %r27          /* save fpregs from the kernel */
 195        save_fp %r27                            /* or potential task switch  */
 196
 197        mfctl   %cr11, %r27                     /* i.e. SAR */
 198        STREG   %r27, TASK_PT_SAR(%r1)
 199
 200        loadgp
 201
 202#ifdef CONFIG_64BIT
 203        ldo     -16(%r30),%r29                  /* Reference param save area */
 204        copy    %r19,%r2                        /* W bit back to r2 */
 205#else
 206        /* no need to save these on stack in wide mode because the first 8
 207         * args are passed in registers */
 208        stw     %r22, -52(%r30)                 /* 5th argument */
 209        stw     %r21, -56(%r30)                 /* 6th argument */
 210#endif
 211
 212        /* Are we being ptraced? */
 213        mfctl   %cr30, %r1
 214        LDREG   TI_FLAGS(%r1),%r1
 215        ldi     _TIF_SYSCALL_TRACE_MASK, %r19
 216        and,COND(=) %r1, %r19, %r0
 217        b,n     .Ltracesys
 218        
 219        /* Note!  We cannot use the syscall table that is mapped
 220        nearby since the gateway page is mapped execute-only. */
 221
 222#ifdef CONFIG_64BIT
 223        ldil    L%sys_call_table, %r1
 224        or,=    %r2,%r2,%r2
 225        addil   L%(sys_call_table64-sys_call_table), %r1
 226        ldo     R%sys_call_table(%r1), %r19
 227        or,=    %r2,%r2,%r2
 228        ldo     R%sys_call_table64(%r1), %r19
 229#else
 230        ldil    L%sys_call_table, %r1
 231        ldo     R%sys_call_table(%r1), %r19
 232#endif  
 233        comiclr,>>      __NR_Linux_syscalls, %r20, %r0
 234        b,n     .Lsyscall_nosys
 235        
 236        LDREGX  %r20(%r19), %r19
 237
 238        /* If this is a sys_rt_sigreturn call, and the signal was received
 239         * when not in_syscall, then we want to return via syscall_exit_rfi,
 240         * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
 241         * trampoline code in signal.c).
 242         */
 243        ldi     __NR_rt_sigreturn,%r2
 244        comb,=  %r2,%r20,.Lrt_sigreturn
 245.Lin_syscall:
 246        ldil    L%syscall_exit,%r2
 247        be      0(%sr7,%r19)
 248        ldo     R%syscall_exit(%r2),%r2
 249.Lrt_sigreturn:
 250        comib,<> 0,%r25,.Lin_syscall
 251        ldil    L%syscall_exit_rfi,%r2
 252        be      0(%sr7,%r19)
 253        ldo     R%syscall_exit_rfi(%r2),%r2
 254
 255        /* Note!  Because we are not running where we were linked, any
 256        calls to functions external to this file must be indirect.  To
 257        be safe, we apply the opposite rule to functions within this
 258        file, with local labels given to them to ensure correctness. */
 259        
 260.Lsyscall_nosys:
 261syscall_nosys:
 262        ldil    L%syscall_exit,%r1
 263        be      R%syscall_exit(%sr7,%r1)
 264        ldo     -ENOSYS(%r0),%r28                  /* set errno */
 265
 266
 267/* Warning! This trace code is a virtual duplicate of the code above so be
 268 * sure to maintain both! */
 269.Ltracesys:
 270tracesys:
 271        /* Need to save more registers so the debugger can see where we
 272         * are.  This saves only the lower 8 bits of PSW, so that the C
 273         * bit is still clear on syscalls, and the D bit is set if this
 274         * full register save path has been executed.  We check the D
 275         * bit on syscall_return_rfi to determine which registers to
 276         * restore.  An interrupt results in a full PSW saved with the
 277         * C bit set, a non-straced syscall entry results in C and D clear
 278         * in the saved PSW.
 279         */
 280        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 281        LDREG   TI_TASK(%r1), %r1
 282        ssm     0,%r2
 283        STREG   %r2,TASK_PT_PSW(%r1)            /* Lower 8 bits only!! */
 284        mfsp    %sr0,%r2
 285        STREG   %r2,TASK_PT_SR0(%r1)
 286        mfsp    %sr1,%r2
 287        STREG   %r2,TASK_PT_SR1(%r1)
 288        mfsp    %sr2,%r2
 289        STREG   %r2,TASK_PT_SR2(%r1)
 290        mfsp    %sr3,%r2
 291        STREG   %r2,TASK_PT_SR3(%r1)
 292        STREG   %r2,TASK_PT_SR4(%r1)
 293        STREG   %r2,TASK_PT_SR5(%r1)
 294        STREG   %r2,TASK_PT_SR6(%r1)
 295        STREG   %r2,TASK_PT_SR7(%r1)
 296        STREG   %r2,TASK_PT_IASQ0(%r1)
 297        STREG   %r2,TASK_PT_IASQ1(%r1)
 298        LDREG   TASK_PT_GR31(%r1),%r2
 299        STREG   %r2,TASK_PT_IAOQ0(%r1)
 300        ldo     4(%r2),%r2
 301        STREG   %r2,TASK_PT_IAOQ1(%r1)
 302        ldo     TASK_REGS(%r1),%r2
 303        /* reg_save %r2 */
 304        STREG   %r3,PT_GR3(%r2)
 305        STREG   %r4,PT_GR4(%r2)
 306        STREG   %r5,PT_GR5(%r2)
 307        STREG   %r6,PT_GR6(%r2)
 308        STREG   %r7,PT_GR7(%r2)
 309        STREG   %r8,PT_GR8(%r2)
 310        STREG   %r9,PT_GR9(%r2)
 311        STREG   %r10,PT_GR10(%r2)
 312        STREG   %r11,PT_GR11(%r2)
 313        STREG   %r12,PT_GR12(%r2)
 314        STREG   %r13,PT_GR13(%r2)
 315        STREG   %r14,PT_GR14(%r2)
 316        STREG   %r15,PT_GR15(%r2)
 317        STREG   %r16,PT_GR16(%r2)
 318        STREG   %r17,PT_GR17(%r2)
 319        STREG   %r18,PT_GR18(%r2)
 320        /* Finished saving things for the debugger */
 321
 322        copy    %r2,%r26
 323        ldil    L%do_syscall_trace_enter,%r1
 324        ldil    L%tracesys_next,%r2
 325        be      R%do_syscall_trace_enter(%sr7,%r1)
 326        ldo     R%tracesys_next(%r2),%r2
 327        
 328tracesys_next:
 329        /* do_syscall_trace_enter either returned the syscallno, or -1L,
 330         *  so we skip restoring the PT_GR20 below, since we pulled it from
 331         *  task->thread.regs.gr[20] above.
 332         */
 333        copy    %ret0,%r20
 334        ldil    L%sys_call_table,%r1
 335        ldo     R%sys_call_table(%r1), %r19
 336
 337        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 338        LDREG   TI_TASK(%r1), %r1
 339        LDREG   TASK_PT_GR28(%r1), %r28         /* Restore return value */
 340        LDREG   TASK_PT_GR26(%r1), %r26         /* Restore the users args */
 341        LDREG   TASK_PT_GR25(%r1), %r25
 342        LDREG   TASK_PT_GR24(%r1), %r24
 343        LDREG   TASK_PT_GR23(%r1), %r23
 344        LDREG   TASK_PT_GR22(%r1), %r22
 345        LDREG   TASK_PT_GR21(%r1), %r21
 346#ifdef CONFIG_64BIT
 347        ldo     -16(%r30),%r29                  /* Reference param save area */
 348#else
 349        stw     %r22, -52(%r30)                 /* 5th argument */
 350        stw     %r21, -56(%r30)                 /* 6th argument */
 351#endif
 352
 353        cmpib,COND(=),n -1,%r20,tracesys_exit /* seccomp may have returned -1 */
 354        comiclr,>>      __NR_Linux_syscalls, %r20, %r0
 355        b,n     .Ltracesys_nosys
 356
 357        LDREGX  %r20(%r19), %r19
 358
 359        /* If this is a sys_rt_sigreturn call, and the signal was received
 360         * when not in_syscall, then we want to return via syscall_exit_rfi,
 361         * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
 362         * trampoline code in signal.c).
 363         */
 364        ldi     __NR_rt_sigreturn,%r2
 365        comb,=  %r2,%r20,.Ltrace_rt_sigreturn
 366.Ltrace_in_syscall:
 367        ldil    L%tracesys_exit,%r2
 368        be      0(%sr7,%r19)
 369        ldo     R%tracesys_exit(%r2),%r2
 370
 371.Ltracesys_nosys:
 372        ldo     -ENOSYS(%r0),%r28               /* set errno */
 373
 374        /* Do *not* call this function on the gateway page, because it
 375        makes a direct call to syscall_trace. */
 376        
 377tracesys_exit:
 378        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 379        LDREG   TI_TASK(%r1), %r1
 380#ifdef CONFIG_64BIT
 381        ldo     -16(%r30),%r29                  /* Reference param save area */
 382#endif
 383        ldo     TASK_REGS(%r1),%r26
 384        BL      do_syscall_trace_exit,%r2
 385        STREG   %r28,TASK_PT_GR28(%r1)          /* save return value now */
 386        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 387        LDREG   TI_TASK(%r1), %r1
 388        LDREG   TASK_PT_GR28(%r1), %r28         /* Restore return val. */
 389
 390        ldil    L%syscall_exit,%r1
 391        be,n    R%syscall_exit(%sr7,%r1)
 392
 393.Ltrace_rt_sigreturn:
 394        comib,<> 0,%r25,.Ltrace_in_syscall
 395        ldil    L%tracesys_sigexit,%r2
 396        be      0(%sr7,%r19)
 397        ldo     R%tracesys_sigexit(%r2),%r2
 398
 399tracesys_sigexit:
 400        ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
 401        LDREG   TI_TASK(%r1), %r1
 402#ifdef CONFIG_64BIT
 403        ldo     -16(%r30),%r29                  /* Reference param save area */
 404#endif
 405        BL      do_syscall_trace_exit,%r2
 406        ldo     TASK_REGS(%r1),%r26
 407
 408        ldil    L%syscall_exit_rfi,%r1
 409        be,n    R%syscall_exit_rfi(%sr7,%r1)
 410
 411
 412        /*********************************************************
 413                32/64-bit Light-Weight-Syscall ABI
 414
 415                * - Indicates a hint for userspace inline asm
 416                implementations.
 417
 418                Syscall number (caller-saves)
 419                - %r20
 420                * In asm clobber.
 421
 422                Argument registers (caller-saves)
 423                - %r26, %r25, %r24, %r23, %r22
 424                * In asm input.
 425
 426                Return registers (caller-saves)
 427                - %r28 (return), %r21 (errno)
 428                * In asm output.
 429
 430                Caller-saves registers
 431                - %r1, %r27, %r29
 432                - %r2 (return pointer)
 433                - %r31 (ble link register)
 434                * In asm clobber.
 435
 436                Callee-saves registers
 437                - %r3-%r18
 438                - %r30 (stack pointer)
 439                * Not in asm clobber.
 440
 441                If userspace is 32-bit:
 442                Callee-saves registers
 443                - %r19 (32-bit PIC register)
 444
 445                Differences from 32-bit calling convention:
 446                - Syscall number in %r20
 447                - Additional argument register %r22 (arg4)
 448                - Callee-saves %r19.
 449
 450                If userspace is 64-bit:
 451                Callee-saves registers
 452                - %r27 (64-bit PIC register)
 453
 454                Differences from 64-bit calling convention:
 455                - Syscall number in %r20
 456                - Additional argument register %r22 (arg4)
 457                - Callee-saves %r27.
 458
 459                Error codes returned by entry path:
 460
 461                ENOSYS - r20 was an invalid LWS number.
 462
 463        *********************************************************/
 464lws_start:
 465
 466#ifdef CONFIG_64BIT
 467        /* FIXME: If we are a 64-bit kernel just
 468         *        turn this on unconditionally.
 469         */
 470        ssm     PSW_SM_W, %r1
 471        extrd,u %r1,PSW_W_BIT,1,%r1
 472        /* sp must be aligned on 4, so deposit the W bit setting into
 473         * the bottom of sp temporarily */
 474        or,ev   %r1,%r30,%r30
 475
 476        /* Clip LWS number to a 32-bit value always */
 477        depdi   0, 31, 32, %r20
 478#endif  
 479
 480        /* Is the lws entry number valid? */
 481        comiclr,>>      __NR_lws_entries, %r20, %r0
 482        b,n     lws_exit_nosys
 483
 484        /* Load table start */
 485        ldil    L%lws_table, %r1
 486        ldo     R%lws_table(%r1), %r28  /* Scratch use of r28 */
 487        LDREGX  %r20(%sr2,r28), %r21    /* Scratch use of r21 */
 488
 489        /* Jump to lws, lws table pointers already relocated */
 490        be,n    0(%sr2,%r21)
 491
 492lws_exit_nosys:
 493        ldo     -ENOSYS(%r0),%r21                  /* set errno */
 494        /* Fall through: Return to userspace */
 495
 496lws_exit:
 497#ifdef CONFIG_64BIT
 498        /* decide whether to reset the wide mode bit
 499         *
 500         * For a syscall, the W bit is stored in the lowest bit
 501         * of sp.  Extract it and reset W if it is zero */
 502        extrd,u,*<>     %r30,63,1,%r1
 503        rsm     PSW_SM_W, %r0
 504        /* now reset the lowest bit of sp if it was set */
 505        xor     %r30,%r1,%r30
 506#endif
 507        be,n    0(%sr7, %r31)
 508
 509
 510        
 511        /***************************************************
 512                Implementing 32bit CAS as an atomic operation:
 513
 514                %r26 - Address to examine
 515                %r25 - Old value to check (old)
 516                %r24 - New value to set (new)
 517                %r28 - Return prev through this register.
 518                %r21 - Kernel error code
 519
 520                If debugging is DISabled:
 521
 522                %r21 has the following meanings:
 523
 524                EAGAIN - CAS is busy, ldcw failed, try again.
 525                EFAULT - Read or write failed.          
 526
 527                If debugging is enabled:
 528
 529                EDEADLOCK - CAS called recursively.
 530                EAGAIN && r28 == 1 - CAS is busy. Lock contended.
 531                EAGAIN && r28 == 2 - CAS is busy. ldcw failed.
 532                EFAULT - Read or write failed.
 533
 534                Scratch: r20, r28, r1
 535
 536        ****************************************************/
 537
 538        /* Do not enable LWS debugging */
 539#define ENABLE_LWS_DEBUG 0 
 540
 541        /* ELF64 Process entry path */
 542lws_compare_and_swap64:
 543#ifdef CONFIG_64BIT
 544        b,n     lws_compare_and_swap
 545#else
 546        /* If we are not a 64-bit kernel, then we don't
 547         * have 64-bit input registers, and calling
 548         * the 64-bit LWS CAS returns ENOSYS.
 549         */
 550        b,n     lws_exit_nosys
 551#endif
 552
 553        /* ELF32 Process entry path */
 554lws_compare_and_swap32:
 555#ifdef CONFIG_64BIT
 556        /* Clip all the input registers */
 557        depdi   0, 31, 32, %r26
 558        depdi   0, 31, 32, %r25
 559        depdi   0, 31, 32, %r24
 560#endif
 561
 562lws_compare_and_swap:
 563        /* Load start of lock table */
 564        ldil    L%lws_lock_start, %r20
 565        ldo     R%lws_lock_start(%r20), %r28
 566
 567        /* Extract four bits from r26 and hash lock (Bits 4-7) */
 568        extru  %r26, 27, 4, %r20
 569
 570        /* Find lock to use, the hash is either one of 0 to
 571           15, multiplied by 16 (keep it 16-byte aligned)
 572           and add to the lock table offset. */
 573        shlw    %r20, 4, %r20
 574        add     %r20, %r28, %r20
 575
 576# if ENABLE_LWS_DEBUG
 577        /*      
 578                DEBUG, check for deadlock! 
 579                If the thread register values are the same
 580                then we were the one that locked it last and
 581                this is a recurisve call that will deadlock.
 582                We *must* giveup this call and fail.
 583        */
 584        ldw     4(%sr2,%r20), %r28                      /* Load thread register */
 585        /* WARNING: If cr27 cycles to the same value we have problems */
 586        mfctl   %cr27, %r21                             /* Get current thread register */
 587        cmpb,<>,n       %r21, %r28, cas_lock            /* Called recursive? */
 588        b       lws_exit                                /* Return error! */
 589        ldo     -EDEADLOCK(%r0), %r21
 590cas_lock:
 591        cmpb,=,n        %r0, %r28, cas_nocontend        /* Is nobody using it? */
 592        ldo     1(%r0), %r28                            /* 1st case */
 593        b       lws_exit                                /* Contended... */
 594        ldo     -EAGAIN(%r0), %r21                      /* Spin in userspace */
 595cas_nocontend:
 596# endif
 597/* ENABLE_LWS_DEBUG */
 598
 599        rsm     PSW_SM_I, %r0                           /* Disable interrupts */
 600        /* COW breaks can cause contention on UP systems */
 601        LDCW    0(%sr2,%r20), %r28                      /* Try to acquire the lock */
 602        cmpb,<>,n       %r0, %r28, cas_action           /* Did we get it? */
 603cas_wouldblock:
 604        ldo     2(%r0), %r28                            /* 2nd case */
 605        ssm     PSW_SM_I, %r0
 606        b       lws_exit                                /* Contended... */
 607        ldo     -EAGAIN(%r0), %r21                      /* Spin in userspace */
 608
 609        /*
 610                prev = *addr;
 611                if ( prev == old )
 612                  *addr = new;
 613                return prev;
 614        */
 615
 616        /* NOTES:
 617                This all works becuse intr_do_signal
 618                and schedule both check the return iasq
 619                and see that we are on the kernel page
 620                so this process is never scheduled off
 621                or is ever sent any signal of any sort,
 622                thus it is wholly atomic from usrspaces
 623                perspective
 624        */
 625cas_action:
 626#if defined CONFIG_SMP && ENABLE_LWS_DEBUG
 627        /* DEBUG */
 628        mfctl   %cr27, %r1
 629        stw     %r1, 4(%sr2,%r20)
 630#endif
 631        /* The load and store could fail */
 6321:      ldw,ma  0(%r26), %r28
 633        sub,<>  %r28, %r25, %r0
 6342:      stw,ma  %r24, 0(%r26)
 635        /* Free lock */
 636        sync
 637        stw,ma  %r20, 0(%sr2,%r20)
 638#if ENABLE_LWS_DEBUG
 639        /* Clear thread register indicator */
 640        stw     %r0, 4(%sr2,%r20)
 641#endif
 642        /* Enable interrupts */
 643        ssm     PSW_SM_I, %r0
 644        /* Return to userspace, set no error */
 645        b       lws_exit
 646        copy    %r0, %r21
 647
 6483:              
 649        /* Error occurred on load or store */
 650        /* Free lock */
 651        sync
 652        stw     %r20, 0(%sr2,%r20)
 653#if ENABLE_LWS_DEBUG
 654        stw     %r0, 4(%sr2,%r20)
 655#endif
 656        ssm     PSW_SM_I, %r0
 657        b       lws_exit
 658        ldo     -EFAULT(%r0),%r21       /* set errno */
 659        nop
 660        nop
 661        nop
 662        nop
 663
 664        /* Two exception table entries, one for the load,
 665           the other for the store. Either return -EFAULT.
 666           Each of the entries must be relocated. */
 667        ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 3b-linux_gateway_page)
 668        ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 3b-linux_gateway_page)
 669
 670
 671        /***************************************************
 672                New CAS implementation which uses pointers and variable size
 673                information. The value pointed by old and new MUST NOT change
 674                while performing CAS. The lock only protect the value at %r26.
 675
 676                %r26 - Address to examine
 677                %r25 - Pointer to the value to check (old)
 678                %r24 - Pointer to the value to set (new)
 679                %r23 - Size of the variable (0/1/2/3 for 8/16/32/64 bit)
 680                %r28 - Return non-zero on failure
 681                %r21 - Kernel error code
 682
 683                %r21 has the following meanings:
 684
 685                EAGAIN - CAS is busy, ldcw failed, try again.
 686                EFAULT - Read or write failed.
 687
 688                Scratch: r20, r22, r28, r29, r1, fr4 (32bit for 64bit CAS only)
 689
 690        ****************************************************/
 691
 692        /* ELF32 Process entry path */
 693lws_compare_and_swap_2:
 694#ifdef CONFIG_64BIT
 695        /* Clip the input registers. We don't need to clip %r23 as we
 696           only use it for word operations */
 697        depdi   0, 31, 32, %r26
 698        depdi   0, 31, 32, %r25
 699        depdi   0, 31, 32, %r24
 700#endif
 701
 702        /* Check the validity of the size pointer */
 703        subi,>>= 3, %r23, %r0
 704        b,n     lws_exit_nosys
 705
 706        /* Jump to the functions which will load the old and new values into
 707           registers depending on the their size */
 708        shlw    %r23, 2, %r29
 709        blr     %r29, %r0
 710        nop
 711
 712        /* 8bit load */
 7134:      ldb     0(%r25), %r25
 714        b       cas2_lock_start
 7155:      ldb     0(%r24), %r24
 716        nop
 717        nop
 718        nop
 719        nop
 720        nop
 721
 722        /* 16bit load */
 7236:      ldh     0(%r25), %r25
 724        b       cas2_lock_start
 7257:      ldh     0(%r24), %r24
 726        nop
 727        nop
 728        nop
 729        nop
 730        nop
 731
 732        /* 32bit load */
 7338:      ldw     0(%r25), %r25
 734        b       cas2_lock_start
 7359:      ldw     0(%r24), %r24
 736        nop
 737        nop
 738        nop
 739        nop
 740        nop
 741
 742        /* 64bit load */
 743#ifdef CONFIG_64BIT
 74410:     ldd     0(%r25), %r25
 74511:     ldd     0(%r24), %r24
 746#else
 747        /* Load old value into r22/r23 - high/low */
 74810:     ldw     0(%r25), %r22
 74911:     ldw     4(%r25), %r23
 750        /* Load new value into fr4 for atomic store later */
 75112:     flddx   0(%r24), %fr4
 752#endif
 753
 754cas2_lock_start:
 755        /* Load start of lock table */
 756        ldil    L%lws_lock_start, %r20
 757        ldo     R%lws_lock_start(%r20), %r28
 758
 759        /* Extract four bits from r26 and hash lock (Bits 4-7) */
 760        extru  %r26, 27, 4, %r20
 761
 762        /* Find lock to use, the hash is either one of 0 to
 763           15, multiplied by 16 (keep it 16-byte aligned)
 764           and add to the lock table offset. */
 765        shlw    %r20, 4, %r20
 766        add     %r20, %r28, %r20
 767
 768        rsm     PSW_SM_I, %r0                   /* Disable interrupts */
 769        /* COW breaks can cause contention on UP systems */
 770        LDCW    0(%sr2,%r20), %r28              /* Try to acquire the lock */
 771        cmpb,<>,n       %r0, %r28, cas2_action  /* Did we get it? */
 772cas2_wouldblock:
 773        ldo     2(%r0), %r28                    /* 2nd case */
 774        ssm     PSW_SM_I, %r0
 775        b       lws_exit                        /* Contended... */
 776        ldo     -EAGAIN(%r0), %r21              /* Spin in userspace */
 777
 778        /*
 779                prev = *addr;
 780                if ( prev == old )
 781                  *addr = new;
 782                return prev;
 783        */
 784
 785        /* NOTES:
 786                This all works becuse intr_do_signal
 787                and schedule both check the return iasq
 788                and see that we are on the kernel page
 789                so this process is never scheduled off
 790                or is ever sent any signal of any sort,
 791                thus it is wholly atomic from usrspaces
 792                perspective
 793        */
 794cas2_action:
 795        /* Jump to the correct function */
 796        blr     %r29, %r0
 797        /* Set %r28 as non-zero for now */
 798        ldo     1(%r0),%r28
 799
 800        /* 8bit CAS */
 80113:     ldb,ma  0(%r26), %r29
 802        sub,=   %r29, %r25, %r0
 803        b,n     cas2_end
 80414:     stb,ma  %r24, 0(%r26)
 805        b       cas2_end
 806        copy    %r0, %r28
 807        nop
 808        nop
 809
 810        /* 16bit CAS */
 81115:     ldh,ma  0(%r26), %r29
 812        sub,=   %r29, %r25, %r0
 813        b,n     cas2_end
 81416:     sth,ma  %r24, 0(%r26)
 815        b       cas2_end
 816        copy    %r0, %r28
 817        nop
 818        nop
 819
 820        /* 32bit CAS */
 82117:     ldw,ma  0(%r26), %r29
 822        sub,=   %r29, %r25, %r0
 823        b,n     cas2_end
 82418:     stw,ma  %r24, 0(%r26)
 825        b       cas2_end
 826        copy    %r0, %r28
 827        nop
 828        nop
 829
 830        /* 64bit CAS */
 831#ifdef CONFIG_64BIT
 83219:     ldd,ma  0(%r26), %r29
 833        sub,*=  %r29, %r25, %r0
 834        b,n     cas2_end
 83520:     std,ma  %r24, 0(%r26)
 836        copy    %r0, %r28
 837#else
 838        /* Compare first word */
 83919:     ldw     0(%r26), %r29
 840        sub,=   %r29, %r22, %r0
 841        b,n     cas2_end
 842        /* Compare second word */
 84320:     ldw     4(%r26), %r29
 844        sub,=   %r29, %r23, %r0
 845        b,n     cas2_end
 846        /* Perform the store */
 84721:     fstdx   %fr4, 0(%r26)
 848        copy    %r0, %r28
 849#endif
 850
 851cas2_end:
 852        /* Free lock */
 853        sync
 854        stw,ma  %r20, 0(%sr2,%r20)
 855        /* Enable interrupts */
 856        ssm     PSW_SM_I, %r0
 857        /* Return to userspace, set no error */
 858        b       lws_exit
 859        copy    %r0, %r21
 860
 86122:
 862        /* Error occurred on load or store */
 863        /* Free lock */
 864        sync
 865        stw     %r20, 0(%sr2,%r20)
 866        ssm     PSW_SM_I, %r0
 867        ldo     1(%r0),%r28
 868        b       lws_exit
 869        ldo     -EFAULT(%r0),%r21       /* set errno */
 870        nop
 871        nop
 872        nop
 873
 874        /* Exception table entries, for the load and store, return EFAULT.
 875           Each of the entries must be relocated. */
 876        ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 22b-linux_gateway_page)
 877        ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 22b-linux_gateway_page)
 878        ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 22b-linux_gateway_page)
 879        ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 22b-linux_gateway_page)
 880        ASM_EXCEPTIONTABLE_ENTRY(8b-linux_gateway_page, 22b-linux_gateway_page)
 881        ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 22b-linux_gateway_page)
 882        ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 22b-linux_gateway_page)
 883        ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 22b-linux_gateway_page)
 884        ASM_EXCEPTIONTABLE_ENTRY(13b-linux_gateway_page, 22b-linux_gateway_page)
 885        ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 22b-linux_gateway_page)
 886        ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 22b-linux_gateway_page)
 887        ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 22b-linux_gateway_page)
 888        ASM_EXCEPTIONTABLE_ENTRY(17b-linux_gateway_page, 22b-linux_gateway_page)
 889        ASM_EXCEPTIONTABLE_ENTRY(18b-linux_gateway_page, 22b-linux_gateway_page)
 890        ASM_EXCEPTIONTABLE_ENTRY(19b-linux_gateway_page, 22b-linux_gateway_page)
 891        ASM_EXCEPTIONTABLE_ENTRY(20b-linux_gateway_page, 22b-linux_gateway_page)
 892#ifndef CONFIG_64BIT
 893        ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 22b-linux_gateway_page)
 894        ASM_EXCEPTIONTABLE_ENTRY(21b-linux_gateway_page, 22b-linux_gateway_page)
 895#endif
 896
 897        /* Make sure nothing else is placed on this page */
 898        .align PAGE_SIZE
 899END(linux_gateway_page)
 900ENTRY(end_linux_gateway_page)
 901
 902        /* Relocate symbols assuming linux_gateway_page is mapped
 903           to virtual address 0x0 */
 904
 905#define LWS_ENTRY(_name_) ASM_ULONG_INSN (lws_##_name_ - linux_gateway_page)
 906
 907        .section .rodata,"a"
 908
 909        .align 8
 910        /* Light-weight-syscall table */
 911        /* Start of lws table. */
 912ENTRY(lws_table)
 913        LWS_ENTRY(compare_and_swap32)           /* 0 - ELF32 Atomic 32bit CAS */
 914        LWS_ENTRY(compare_and_swap64)           /* 1 - ELF64 Atomic 32bit CAS */
 915        LWS_ENTRY(compare_and_swap_2)           /* 2 - ELF32 Atomic 64bit CAS */
 916END(lws_table)
 917        /* End of lws table */
 918
 919        .align 8
 920ENTRY(sys_call_table)
 921        .export sys_call_table,data
 922#include "syscall_table.S"
 923END(sys_call_table)
 924
 925#ifdef CONFIG_64BIT
 926        .align 8
 927ENTRY(sys_call_table64)
 928#define SYSCALL_TABLE_64BIT
 929#include "syscall_table.S"
 930END(sys_call_table64)
 931#endif
 932
 933        /*
 934                All light-weight-syscall atomic operations 
 935                will use this set of locks 
 936
 937                NOTE: The lws_lock_start symbol must be
 938                at least 16-byte aligned for safe use
 939                with ldcw.
 940        */
 941        .section .data
 942        .align  L1_CACHE_BYTES
 943ENTRY(lws_lock_start)
 944        /* lws locks */
 945        .rept 16
 946        /* Keep locks aligned at 16-bytes */
 947        .word 1
 948        .word 0 
 949        .word 0
 950        .word 0
 951        .endr
 952END(lws_lock_start)
 953        .previous
 954
 955.end
 956
 957
 958