11da177e4SLinus Torvalds/* 21da177e4SLinus Torvalds * Linux/PA-RISC Project (http://www.parisc-linux.org/) 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * System call entry code Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai> 51da177e4SLinus Torvalds * Licensed under the GNU GPL. 61da177e4SLinus Torvalds * thanks to Philipp Rumpf, Mike Shaver and various others 71da177e4SLinus Torvalds * sorry about the wall, puffin.. 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds 100013a854SSam Ravnborg#include <asm/asm-offsets.h> 111da177e4SLinus Torvalds#include <asm/unistd.h> 121da177e4SLinus Torvalds#include <asm/errno.h> 131c593571SSam Ravnborg#include <asm/page.h> 141da177e4SLinus Torvalds#include <asm/psw.h> 151da177e4SLinus Torvalds#include <asm/thread_info.h> 161da177e4SLinus Torvalds#include <asm/assembly.h> 171da177e4SLinus Torvalds#include <asm/processor.h> 186a45716aSHelge Deller#include <asm/cache.h> 191da177e4SLinus Torvalds 208e9e9844SHelge Deller#include <linux/linkage.h> 218e9e9844SHelge Deller 221da177e4SLinus Torvalds /* We fill the empty parts of the gateway page with 231da177e4SLinus Torvalds * something that will kill the kernel or a 241da177e4SLinus Torvalds * userspace application. 251da177e4SLinus Torvalds */ 261da177e4SLinus Torvalds#define KILL_INSN break 0,0 271da177e4SLinus Torvalds 280b3d643fSHelge Deller .level LEVEL 298e9e9844SHelge Deller 30dfcf753bSKyle McMartin .text 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds .import syscall_exit,code 331da177e4SLinus Torvalds .import syscall_exit_rfi,code 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds /* Linux gateway page is aliased to virtual page 0 in the kernel 361da177e4SLinus Torvalds * address space. Since it is a gateway page it cannot be 371da177e4SLinus Torvalds * dereferenced, so null pointers will still fault. We start 381da177e4SLinus Torvalds * the actual entry point at 0x100. We put break instructions 391da177e4SLinus Torvalds * at the beginning of the page to trap null indirect function 401da177e4SLinus Torvalds * pointers. 411da177e4SLinus Torvalds */ 421da177e4SLinus Torvalds 431c593571SSam Ravnborg .align PAGE_SIZE 448e9e9844SHelge DellerENTRY(linux_gateway_page) 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */ 471da177e4SLinus Torvalds .rept 44 481da177e4SLinus Torvalds KILL_INSN 491da177e4SLinus Torvalds .endr 501da177e4SLinus Torvalds 51f4c0346cSJohn David Anglin /* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */ 521da177e4SLinus Torvalds /* Light-weight-syscall entry must always be located at 0xb0 */ 531da177e4SLinus Torvalds /* WARNING: Keep this number updated with table size changes */ 541da177e4SLinus Torvalds#define __NR_lws_entries (2) 551da177e4SLinus Torvalds 561da177e4SLinus Torvaldslws_entry: 57f4c0346cSJohn David Anglin gate lws_start, %r0 /* increase privilege */ 58f4c0346cSJohn David Anglin depi 3, 31, 2, %r31 /* Ensure we return into user mode. */ 591da177e4SLinus Torvalds 60f4c0346cSJohn David Anglin /* Fill from 0xb8 to 0xe0 */ 61f4c0346cSJohn David Anglin .rept 10 621da177e4SLinus Torvalds KILL_INSN 631da177e4SLinus Torvalds .endr 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds /* This function MUST be located at 0xe0 for glibc's threading 661da177e4SLinus Torvalds mechanism to work. DO NOT MOVE THIS CODE EVER! */ 671da177e4SLinus Torvaldsset_thread_pointer: 681da177e4SLinus Torvalds gate .+8, %r0 /* increase privilege */ 691da177e4SLinus Torvalds depi 3, 31, 2, %r31 /* Ensure we return into user mode. */ 701da177e4SLinus Torvalds be 0(%sr7,%r31) /* return to user space */ 711da177e4SLinus Torvalds mtctl %r26, %cr27 /* move arg0 to the control register */ 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds /* Increase the chance of trapping if random jumps occur to this 741da177e4SLinus Torvalds address, fill from 0xf0 to 0x100 */ 751da177e4SLinus Torvalds .rept 4 761da177e4SLinus Torvalds KILL_INSN 771da177e4SLinus Torvalds .endr 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds/* This address must remain fixed at 0x100 for glibc's syscalls to work */ 801da177e4SLinus Torvalds .align 256 811da177e4SLinus Torvaldslinux_gateway_entry: 821da177e4SLinus Torvalds gate .+8, %r0 /* become privileged */ 831da177e4SLinus Torvalds mtsp %r0,%sr4 /* get kernel space into sr4 */ 841da177e4SLinus Torvalds mtsp %r0,%sr5 /* get kernel space into sr5 */ 851da177e4SLinus Torvalds mtsp %r0,%sr6 /* get kernel space into sr6 */ 861da177e4SLinus Torvalds mfsp %sr7,%r1 /* save user sr7 */ 871da177e4SLinus Torvalds mtsp %r1,%sr3 /* and store it in sr3 */ 881da177e4SLinus Torvalds 89413059f2SGrant Grundler#ifdef CONFIG_64BIT 901da177e4SLinus Torvalds /* for now we can *always* set the W bit on entry to the syscall 911da177e4SLinus Torvalds * since we don't support wide userland processes. We could 921da177e4SLinus Torvalds * also save the current SM other than in r0 and restore it on 931da177e4SLinus Torvalds * exit from the syscall, and also use that value to know 941da177e4SLinus Torvalds * whether to do narrow or wide syscalls. -PB 951da177e4SLinus Torvalds */ 961da177e4SLinus Torvalds ssm PSW_SM_W, %r1 971da177e4SLinus Torvalds extrd,u %r1,PSW_W_BIT,1,%r1 981da177e4SLinus Torvalds /* sp must be aligned on 4, so deposit the W bit setting into 991da177e4SLinus Torvalds * the bottom of sp temporarily */ 1001da177e4SLinus Torvalds or,ev %r1,%r30,%r30 1011da177e4SLinus Torvalds b,n 1f 1021da177e4SLinus Torvalds /* The top halves of argument registers must be cleared on syscall 1031da177e4SLinus Torvalds * entry from narrow executable. 1041da177e4SLinus Torvalds */ 1051da177e4SLinus Torvalds depdi 0, 31, 32, %r26 1061da177e4SLinus Torvalds depdi 0, 31, 32, %r25 1071da177e4SLinus Torvalds depdi 0, 31, 32, %r24 1081da177e4SLinus Torvalds depdi 0, 31, 32, %r23 1091da177e4SLinus Torvalds depdi 0, 31, 32, %r22 1101da177e4SLinus Torvalds depdi 0, 31, 32, %r21 1111da177e4SLinus Torvalds1: 1121da177e4SLinus Torvalds#endif 1131da177e4SLinus Torvalds mfctl %cr30,%r1 1141da177e4SLinus Torvalds xor %r1,%r30,%r30 /* ye olde xor trick */ 1151da177e4SLinus Torvalds xor %r1,%r30,%r1 1161da177e4SLinus Torvalds xor %r1,%r30,%r30 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds ldo THREAD_SZ_ALGN+FRAME_SIZE(%r30),%r30 /* set up kernel stack */ 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds /* N.B.: It is critical that we don't set sr7 to 0 until r30 1211da177e4SLinus Torvalds * contains a valid kernel stack pointer. It is also 1221da177e4SLinus Torvalds * critical that we don't start using the kernel stack 1231da177e4SLinus Torvalds * until after sr7 has been set to 0. 1241da177e4SLinus Torvalds */ 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds mtsp %r0,%sr7 /* get kernel space into sr7 */ 1271da177e4SLinus Torvalds STREGM %r1,FRAME_SIZE(%r30) /* save r1 (usp) here for now */ 1281da177e4SLinus Torvalds mfctl %cr30,%r1 /* get task ptr in %r1 */ 1291da177e4SLinus Torvalds LDREG TI_TASK(%r1),%r1 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds /* Save some registers for sigcontext and potential task 1321da177e4SLinus Torvalds switch (see entry.S for the details of which ones are 1331da177e4SLinus Torvalds saved/restored). TASK_PT_PSW is zeroed so we can see whether 1341da177e4SLinus Torvalds a process is on a syscall or not. For an interrupt the real 1351da177e4SLinus Torvalds PSW value is stored. This is needed for gdb and sys_ptrace. */ 1361da177e4SLinus Torvalds STREG %r0, TASK_PT_PSW(%r1) 1371da177e4SLinus Torvalds STREG %r2, TASK_PT_GR2(%r1) /* preserve rp */ 1381da177e4SLinus Torvalds STREG %r19, TASK_PT_GR19(%r1) 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds LDREGM -FRAME_SIZE(%r30), %r2 /* get users sp back */ 141413059f2SGrant Grundler#ifdef CONFIG_64BIT 1421da177e4SLinus Torvalds extrd,u %r2,63,1,%r19 /* W hidden in bottom bit */ 1431da177e4SLinus Torvalds#if 0 1441da177e4SLinus Torvalds xor %r19,%r2,%r2 /* clear bottom bit */ 1451da177e4SLinus Torvalds depd,z %r19,1,1,%r19 1461da177e4SLinus Torvalds std %r19,TASK_PT_PSW(%r1) 1471da177e4SLinus Torvalds#endif 1481da177e4SLinus Torvalds#endif 1491da177e4SLinus Torvalds STREG %r2, TASK_PT_GR30(%r1) /* ... and save it */ 1501da177e4SLinus Torvalds 151aa0eecb0SCarlos O'Donell STREG %r20, TASK_PT_GR20(%r1) /* Syscall number */ 1521da177e4SLinus Torvalds STREG %r21, TASK_PT_GR21(%r1) 1531da177e4SLinus Torvalds STREG %r22, TASK_PT_GR22(%r1) 1541da177e4SLinus Torvalds STREG %r23, TASK_PT_GR23(%r1) /* 4th argument */ 1551da177e4SLinus Torvalds STREG %r24, TASK_PT_GR24(%r1) /* 3rd argument */ 1561da177e4SLinus Torvalds STREG %r25, TASK_PT_GR25(%r1) /* 2nd argument */ 1571da177e4SLinus Torvalds STREG %r26, TASK_PT_GR26(%r1) /* 1st argument */ 1581da177e4SLinus Torvalds STREG %r27, TASK_PT_GR27(%r1) /* user dp */ 1591da177e4SLinus Torvalds STREG %r28, TASK_PT_GR28(%r1) /* return value 0 */ 16000df111eSAl Viro STREG %r0, TASK_PT_ORIG_R28(%r1) /* don't prohibit restarts */ 1611da177e4SLinus Torvalds STREG %r29, TASK_PT_GR29(%r1) /* return value 1 */ 1621da177e4SLinus Torvalds STREG %r31, TASK_PT_GR31(%r1) /* preserve syscall return ptr */ 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds ldo TASK_PT_FR0(%r1), %r27 /* save fpregs from the kernel */ 1651da177e4SLinus Torvalds save_fp %r27 /* or potential task switch */ 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds mfctl %cr11, %r27 /* i.e. SAR */ 1681da177e4SLinus Torvalds STREG %r27, TASK_PT_SAR(%r1) 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds loadgp 1711da177e4SLinus Torvalds 172413059f2SGrant Grundler#ifdef CONFIG_64BIT 1731da177e4SLinus Torvalds ldo -16(%r30),%r29 /* Reference param save area */ 1741da177e4SLinus Torvalds copy %r19,%r2 /* W bit back to r2 */ 1751da177e4SLinus Torvalds#else 1761da177e4SLinus Torvalds /* no need to save these on stack in wide mode because the first 8 1771da177e4SLinus Torvalds * args are passed in registers */ 1781da177e4SLinus Torvalds stw %r22, -52(%r30) /* 5th argument */ 1791da177e4SLinus Torvalds stw %r21, -56(%r30) /* 6th argument */ 1801da177e4SLinus Torvalds#endif 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds /* Are we being ptraced? */ 1831da177e4SLinus Torvalds mfctl %cr30, %r1 18464482bd8SAl Viro LDREG TI_FLAGS(%r1),%r1 18564482bd8SAl Viro ldi _TIF_SYSCALL_TRACE_MASK, %r19 18664482bd8SAl Viro and,COND(=) %r1, %r19, %r0 18764482bd8SAl Viro b,n .Ltracesys 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds /* Note! We cannot use the syscall table that is mapped 1901da177e4SLinus Torvalds nearby since the gateway page is mapped execute-only. */ 1911da177e4SLinus Torvalds 192413059f2SGrant Grundler#ifdef CONFIG_64BIT 1931da177e4SLinus Torvalds ldil L%sys_call_table, %r1 1941da177e4SLinus Torvalds or,= %r2,%r2,%r2 1951da177e4SLinus Torvalds addil L%(sys_call_table64-sys_call_table), %r1 1961da177e4SLinus Torvalds ldo R%sys_call_table(%r1), %r19 1971da177e4SLinus Torvalds or,= %r2,%r2,%r2 1981da177e4SLinus Torvalds ldo R%sys_call_table64(%r1), %r19 1991da177e4SLinus Torvalds#else 2001da177e4SLinus Torvalds ldil L%sys_call_table, %r1 2011da177e4SLinus Torvalds ldo R%sys_call_table(%r1), %r19 2021da177e4SLinus Torvalds#endif 2033bb457afSKyle McMartin comiclr,>> __NR_Linux_syscalls, %r20, %r0 2041da177e4SLinus Torvalds b,n .Lsyscall_nosys 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds LDREGX %r20(%r19), %r19 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds /* If this is a sys_rt_sigreturn call, and the signal was received 2091da177e4SLinus Torvalds * when not in_syscall, then we want to return via syscall_exit_rfi, 2101da177e4SLinus Torvalds * not syscall_exit. Signal no. in r20, in_syscall in r25 (see 2111da177e4SLinus Torvalds * trampoline code in signal.c). 2121da177e4SLinus Torvalds */ 2131da177e4SLinus Torvalds ldi __NR_rt_sigreturn,%r2 2141da177e4SLinus Torvalds comb,= %r2,%r20,.Lrt_sigreturn 2151da177e4SLinus Torvalds.Lin_syscall: 2161da177e4SLinus Torvalds ldil L%syscall_exit,%r2 2171da177e4SLinus Torvalds be 0(%sr7,%r19) 2181da177e4SLinus Torvalds ldo R%syscall_exit(%r2),%r2 2191da177e4SLinus Torvalds.Lrt_sigreturn: 2201da177e4SLinus Torvalds comib,<> 0,%r25,.Lin_syscall 2211da177e4SLinus Torvalds ldil L%syscall_exit_rfi,%r2 2221da177e4SLinus Torvalds be 0(%sr7,%r19) 2231da177e4SLinus Torvalds ldo R%syscall_exit_rfi(%r2),%r2 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds /* Note! Because we are not running where we were linked, any 2261da177e4SLinus Torvalds calls to functions external to this file must be indirect. To 2271da177e4SLinus Torvalds be safe, we apply the opposite rule to functions within this 2281da177e4SLinus Torvalds file, with local labels given to them to ensure correctness. */ 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds.Lsyscall_nosys: 2311da177e4SLinus Torvaldssyscall_nosys: 2321da177e4SLinus Torvalds ldil L%syscall_exit,%r1 2331da177e4SLinus Torvalds be R%syscall_exit(%sr7,%r1) 2341da177e4SLinus Torvalds ldo -ENOSYS(%r0),%r28 /* set errno */ 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds/* Warning! This trace code is a virtual duplicate of the code above so be 2381da177e4SLinus Torvalds * sure to maintain both! */ 2391da177e4SLinus Torvalds.Ltracesys: 2401da177e4SLinus Torvaldstracesys: 2411da177e4SLinus Torvalds /* Need to save more registers so the debugger can see where we 2421da177e4SLinus Torvalds * are. This saves only the lower 8 bits of PSW, so that the C 2431da177e4SLinus Torvalds * bit is still clear on syscalls, and the D bit is set if this 2441da177e4SLinus Torvalds * full register save path has been executed. We check the D 2451da177e4SLinus Torvalds * bit on syscall_return_rfi to determine which registers to 2461da177e4SLinus Torvalds * restore. An interrupt results in a full PSW saved with the 2471da177e4SLinus Torvalds * C bit set, a non-straced syscall entry results in C and D clear 2481da177e4SLinus Torvalds * in the saved PSW. 2491da177e4SLinus Torvalds */ 2501da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 2511da177e4SLinus Torvalds LDREG TI_TASK(%r1), %r1 2521da177e4SLinus Torvalds ssm 0,%r2 2531da177e4SLinus Torvalds STREG %r2,TASK_PT_PSW(%r1) /* Lower 8 bits only!! */ 2541da177e4SLinus Torvalds mfsp %sr0,%r2 2551da177e4SLinus Torvalds STREG %r2,TASK_PT_SR0(%r1) 2561da177e4SLinus Torvalds mfsp %sr1,%r2 2571da177e4SLinus Torvalds STREG %r2,TASK_PT_SR1(%r1) 2581da177e4SLinus Torvalds mfsp %sr2,%r2 2591da177e4SLinus Torvalds STREG %r2,TASK_PT_SR2(%r1) 2601da177e4SLinus Torvalds mfsp %sr3,%r2 2611da177e4SLinus Torvalds STREG %r2,TASK_PT_SR3(%r1) 2621da177e4SLinus Torvalds STREG %r2,TASK_PT_SR4(%r1) 2631da177e4SLinus Torvalds STREG %r2,TASK_PT_SR5(%r1) 2641da177e4SLinus Torvalds STREG %r2,TASK_PT_SR6(%r1) 2651da177e4SLinus Torvalds STREG %r2,TASK_PT_SR7(%r1) 2661da177e4SLinus Torvalds STREG %r2,TASK_PT_IASQ0(%r1) 2671da177e4SLinus Torvalds STREG %r2,TASK_PT_IASQ1(%r1) 2681da177e4SLinus Torvalds LDREG TASK_PT_GR31(%r1),%r2 2691da177e4SLinus Torvalds STREG %r2,TASK_PT_IAOQ0(%r1) 2701da177e4SLinus Torvalds ldo 4(%r2),%r2 2711da177e4SLinus Torvalds STREG %r2,TASK_PT_IAOQ1(%r1) 2721da177e4SLinus Torvalds ldo TASK_REGS(%r1),%r2 2731da177e4SLinus Torvalds /* reg_save %r2 */ 2741da177e4SLinus Torvalds STREG %r3,PT_GR3(%r2) 2751da177e4SLinus Torvalds STREG %r4,PT_GR4(%r2) 2761da177e4SLinus Torvalds STREG %r5,PT_GR5(%r2) 2771da177e4SLinus Torvalds STREG %r6,PT_GR6(%r2) 2781da177e4SLinus Torvalds STREG %r7,PT_GR7(%r2) 2791da177e4SLinus Torvalds STREG %r8,PT_GR8(%r2) 2801da177e4SLinus Torvalds STREG %r9,PT_GR9(%r2) 2811da177e4SLinus Torvalds STREG %r10,PT_GR10(%r2) 2821da177e4SLinus Torvalds STREG %r11,PT_GR11(%r2) 2831da177e4SLinus Torvalds STREG %r12,PT_GR12(%r2) 2841da177e4SLinus Torvalds STREG %r13,PT_GR13(%r2) 2851da177e4SLinus Torvalds STREG %r14,PT_GR14(%r2) 2861da177e4SLinus Torvalds STREG %r15,PT_GR15(%r2) 2871da177e4SLinus Torvalds STREG %r16,PT_GR16(%r2) 2881da177e4SLinus Torvalds STREG %r17,PT_GR17(%r2) 2891da177e4SLinus Torvalds STREG %r18,PT_GR18(%r2) 2901da177e4SLinus Torvalds /* Finished saving things for the debugger */ 2911da177e4SLinus Torvalds 2922798af1aSKyle McMartin copy %r2,%r26 2932798af1aSKyle McMartin ldil L%do_syscall_trace_enter,%r1 2941da177e4SLinus Torvalds ldil L%tracesys_next,%r2 2952798af1aSKyle McMartin be R%do_syscall_trace_enter(%sr7,%r1) 2961da177e4SLinus Torvalds ldo R%tracesys_next(%r2),%r2 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvaldstracesys_next: 2992798af1aSKyle McMartin /* do_syscall_trace_enter either returned the syscallno, or -1L, 3002798af1aSKyle McMartin * so we skip restoring the PT_GR20 below, since we pulled it from 3012798af1aSKyle McMartin * task->thread.regs.gr[20] above. 3022798af1aSKyle McMartin */ 3032798af1aSKyle McMartin copy %ret0,%r20 3041da177e4SLinus Torvalds ldil L%sys_call_table,%r1 3051da177e4SLinus Torvalds ldo R%sys_call_table(%r1), %r19 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 3081da177e4SLinus Torvalds LDREG TI_TASK(%r1), %r1 3091da177e4SLinus Torvalds LDREG TASK_PT_GR26(%r1), %r26 /* Restore the users args */ 3101da177e4SLinus Torvalds LDREG TASK_PT_GR25(%r1), %r25 3111da177e4SLinus Torvalds LDREG TASK_PT_GR24(%r1), %r24 3121da177e4SLinus Torvalds LDREG TASK_PT_GR23(%r1), %r23 3131da177e4SLinus Torvalds LDREG TASK_PT_GR22(%r1), %r22 3141da177e4SLinus Torvalds LDREG TASK_PT_GR21(%r1), %r21 31552ab532eSAl Viro#ifdef CONFIG_64BIT 3161da177e4SLinus Torvalds ldo -16(%r30),%r29 /* Reference param save area */ 31752ab532eSAl Viro#else 31852ab532eSAl Viro stw %r22, -52(%r30) /* 5th argument */ 31952ab532eSAl Viro stw %r21, -56(%r30) /* 6th argument */ 3201da177e4SLinus Torvalds#endif 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds comiclr,>>= __NR_Linux_syscalls, %r20, %r0 3231da177e4SLinus Torvalds b,n .Lsyscall_nosys 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds LDREGX %r20(%r19), %r19 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds /* If this is a sys_rt_sigreturn call, and the signal was received 3281da177e4SLinus Torvalds * when not in_syscall, then we want to return via syscall_exit_rfi, 3291da177e4SLinus Torvalds * not syscall_exit. Signal no. in r20, in_syscall in r25 (see 3301da177e4SLinus Torvalds * trampoline code in signal.c). 3311da177e4SLinus Torvalds */ 3321da177e4SLinus Torvalds ldi __NR_rt_sigreturn,%r2 3331da177e4SLinus Torvalds comb,= %r2,%r20,.Ltrace_rt_sigreturn 3341da177e4SLinus Torvalds.Ltrace_in_syscall: 3351da177e4SLinus Torvalds ldil L%tracesys_exit,%r2 3361da177e4SLinus Torvalds be 0(%sr7,%r19) 3371da177e4SLinus Torvalds ldo R%tracesys_exit(%r2),%r2 3381da177e4SLinus Torvalds 3391da177e4SLinus Torvalds /* Do *not* call this function on the gateway page, because it 3401da177e4SLinus Torvalds makes a direct call to syscall_trace. */ 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvaldstracesys_exit: 3431da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 3441da177e4SLinus Torvalds LDREG TI_TASK(%r1), %r1 345413059f2SGrant Grundler#ifdef CONFIG_64BIT 3461da177e4SLinus Torvalds ldo -16(%r30),%r29 /* Reference param save area */ 3471da177e4SLinus Torvalds#endif 3482798af1aSKyle McMartin ldo TASK_REGS(%r1),%r26 3492798af1aSKyle McMartin bl do_syscall_trace_exit,%r2 3501da177e4SLinus Torvalds STREG %r28,TASK_PT_GR28(%r1) /* save return value now */ 3511da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 3521da177e4SLinus Torvalds LDREG TI_TASK(%r1), %r1 3531da177e4SLinus Torvalds LDREG TASK_PT_GR28(%r1), %r28 /* Restore return val. */ 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds ldil L%syscall_exit,%r1 3561da177e4SLinus Torvalds be,n R%syscall_exit(%sr7,%r1) 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds.Ltrace_rt_sigreturn: 3591da177e4SLinus Torvalds comib,<> 0,%r25,.Ltrace_in_syscall 3601da177e4SLinus Torvalds ldil L%tracesys_sigexit,%r2 3611da177e4SLinus Torvalds be 0(%sr7,%r19) 3621da177e4SLinus Torvalds ldo R%tracesys_sigexit(%r2),%r2 3631da177e4SLinus Torvalds 3641da177e4SLinus Torvaldstracesys_sigexit: 3651da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 3665837d42fSKyle McMartin LDREG TI_TASK(%r1), %r1 367413059f2SGrant Grundler#ifdef CONFIG_64BIT 3681da177e4SLinus Torvalds ldo -16(%r30),%r29 /* Reference param save area */ 3691da177e4SLinus Torvalds#endif 3702798af1aSKyle McMartin bl do_syscall_trace_exit,%r2 3712798af1aSKyle McMartin ldo TASK_REGS(%r1),%r26 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds ldil L%syscall_exit_rfi,%r1 3741da177e4SLinus Torvalds be,n R%syscall_exit_rfi(%sr7,%r1) 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds 3771da177e4SLinus Torvalds /********************************************************* 378c84c3a69SHelge Deller 32/64-bit Light-Weight-Syscall ABI 3791da177e4SLinus Torvalds 380c84c3a69SHelge Deller * - Indicates a hint for userspace inline asm 381c84c3a69SHelge Deller implementations. 3821da177e4SLinus Torvalds 383c84c3a69SHelge Deller Syscall number (caller-saves) 384c84c3a69SHelge Deller - %r20 385c84c3a69SHelge Deller * In asm clobber. 3861da177e4SLinus Torvalds 387c84c3a69SHelge Deller Argument registers (caller-saves) 388c84c3a69SHelge Deller - %r26, %r25, %r24, %r23, %r22 389c84c3a69SHelge Deller * In asm input. 390c84c3a69SHelge Deller 391c84c3a69SHelge Deller Return registers (caller-saves) 392c84c3a69SHelge Deller - %r28 (return), %r21 (errno) 393c84c3a69SHelge Deller * In asm output. 394c84c3a69SHelge Deller 395c84c3a69SHelge Deller Caller-saves registers 396c84c3a69SHelge Deller - %r1, %r27, %r29 397c84c3a69SHelge Deller - %r2 (return pointer) 398c84c3a69SHelge Deller - %r31 (ble link register) 399c84c3a69SHelge Deller * In asm clobber. 400c84c3a69SHelge Deller 401c84c3a69SHelge Deller Callee-saves registers 402c84c3a69SHelge Deller - %r3-%r18 403c84c3a69SHelge Deller - %r30 (stack pointer) 404c84c3a69SHelge Deller * Not in asm clobber. 405c84c3a69SHelge Deller 406c84c3a69SHelge Deller If userspace is 32-bit: 407c84c3a69SHelge Deller Callee-saves registers 408c84c3a69SHelge Deller - %r19 (32-bit PIC register) 409c84c3a69SHelge Deller 410c84c3a69SHelge Deller Differences from 32-bit calling convention: 411c84c3a69SHelge Deller - Syscall number in %r20 412c84c3a69SHelge Deller - Additional argument register %r22 (arg4) 413c84c3a69SHelge Deller - Callee-saves %r19. 414c84c3a69SHelge Deller 415c84c3a69SHelge Deller If userspace is 64-bit: 416c84c3a69SHelge Deller Callee-saves registers 417c84c3a69SHelge Deller - %r27 (64-bit PIC register) 418c84c3a69SHelge Deller 419c84c3a69SHelge Deller Differences from 64-bit calling convention: 420c84c3a69SHelge Deller - Syscall number in %r20 421c84c3a69SHelge Deller - Additional argument register %r22 (arg4) 422c84c3a69SHelge Deller - Callee-saves %r27. 4231da177e4SLinus Torvalds 4241da177e4SLinus Torvalds Error codes returned by entry path: 4251da177e4SLinus Torvalds 4261da177e4SLinus Torvalds ENOSYS - r20 was an invalid LWS number. 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds *********************************************************/ 4291da177e4SLinus Torvaldslws_start: 4301da177e4SLinus Torvalds 431413059f2SGrant Grundler#ifdef CONFIG_64BIT 4321da177e4SLinus Torvalds /* FIXME: If we are a 64-bit kernel just 4331da177e4SLinus Torvalds * turn this on unconditionally. 4341da177e4SLinus Torvalds */ 4351da177e4SLinus Torvalds ssm PSW_SM_W, %r1 4361da177e4SLinus Torvalds extrd,u %r1,PSW_W_BIT,1,%r1 4371da177e4SLinus Torvalds /* sp must be aligned on 4, so deposit the W bit setting into 4381da177e4SLinus Torvalds * the bottom of sp temporarily */ 4391da177e4SLinus Torvalds or,ev %r1,%r30,%r30 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds /* Clip LWS number to a 32-bit value always */ 4421da177e4SLinus Torvalds depdi 0, 31, 32, %r20 4431da177e4SLinus Torvalds#endif 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds /* Is the lws entry number valid? */ 446f4c0346cSJohn David Anglin comiclr,>> __NR_lws_entries, %r20, %r0 4471da177e4SLinus Torvalds b,n lws_exit_nosys 4481da177e4SLinus Torvalds 4491da177e4SLinus Torvalds /* WARNING: Trashing sr2 and sr3 */ 4501da177e4SLinus Torvalds mfsp %sr7,%r1 /* get userspace into sr3 */ 4511da177e4SLinus Torvalds mtsp %r1,%sr3 4521da177e4SLinus Torvalds mtsp %r0,%sr2 /* get kernel space into sr2 */ 4531da177e4SLinus Torvalds 4541da177e4SLinus Torvalds /* Load table start */ 4551da177e4SLinus Torvalds ldil L%lws_table, %r1 4561da177e4SLinus Torvalds ldo R%lws_table(%r1), %r28 /* Scratch use of r28 */ 4571da177e4SLinus Torvalds LDREGX %r20(%sr2,r28), %r21 /* Scratch use of r21 */ 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds /* Jump to lws, lws table pointers already relocated */ 4601da177e4SLinus Torvalds be,n 0(%sr2,%r21) 4611da177e4SLinus Torvalds 4621da177e4SLinus Torvaldslws_exit_nosys: 4631da177e4SLinus Torvalds ldo -ENOSYS(%r0),%r21 /* set errno */ 4641da177e4SLinus Torvalds /* Fall through: Return to userspace */ 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvaldslws_exit: 467413059f2SGrant Grundler#ifdef CONFIG_64BIT 4681da177e4SLinus Torvalds /* decide whether to reset the wide mode bit 4691da177e4SLinus Torvalds * 4701da177e4SLinus Torvalds * For a syscall, the W bit is stored in the lowest bit 4711da177e4SLinus Torvalds * of sp. Extract it and reset W if it is zero */ 4721da177e4SLinus Torvalds extrd,u,*<> %r30,63,1,%r1 4731da177e4SLinus Torvalds rsm PSW_SM_W, %r0 4741da177e4SLinus Torvalds /* now reset the lowest bit of sp if it was set */ 4751da177e4SLinus Torvalds xor %r30,%r1,%r30 4761da177e4SLinus Torvalds#endif 477f4c0346cSJohn David Anglin be,n 0(%sr7, %r31) 4781da177e4SLinus Torvalds 4791da177e4SLinus Torvalds 4801da177e4SLinus Torvalds 4811da177e4SLinus Torvalds /*************************************************** 4821da177e4SLinus Torvalds Implementing CAS as an atomic operation: 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds %r26 - Address to examine 4851da177e4SLinus Torvalds %r25 - Old value to check (old) 4861da177e4SLinus Torvalds %r24 - New value to set (new) 4871da177e4SLinus Torvalds %r28 - Return prev through this register. 4881da177e4SLinus Torvalds %r21 - Kernel error code 4891da177e4SLinus Torvalds 4901da177e4SLinus Torvalds If debugging is DISabled: 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvalds %r21 has the following meanings: 4931da177e4SLinus Torvalds 4941da177e4SLinus Torvalds EAGAIN - CAS is busy, ldcw failed, try again. 4951da177e4SLinus Torvalds EFAULT - Read or write failed. 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds If debugging is enabled: 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds EDEADLOCK - CAS called recursively. 5001da177e4SLinus Torvalds EAGAIN && r28 == 1 - CAS is busy. Lock contended. 5011da177e4SLinus Torvalds EAGAIN && r28 == 2 - CAS is busy. ldcw failed. 5021da177e4SLinus Torvalds EFAULT - Read or write failed. 5031da177e4SLinus Torvalds 5041da177e4SLinus Torvalds Scratch: r20, r28, r1 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds ****************************************************/ 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds /* Do not enable LWS debugging */ 5091da177e4SLinus Torvalds#define ENABLE_LWS_DEBUG 0 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds /* ELF64 Process entry path */ 5121da177e4SLinus Torvaldslws_compare_and_swap64: 513413059f2SGrant Grundler#ifdef CONFIG_64BIT 5141da177e4SLinus Torvalds b,n lws_compare_and_swap 5151da177e4SLinus Torvalds#else 5161da177e4SLinus Torvalds /* If we are not a 64-bit kernel, then we don't 517c84c3a69SHelge Deller * have 64-bit input registers, and calling 518c84c3a69SHelge Deller * the 64-bit LWS CAS returns ENOSYS. 5191da177e4SLinus Torvalds */ 5201da177e4SLinus Torvalds b,n lws_exit_nosys 5211da177e4SLinus Torvalds#endif 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds /* ELF32 Process entry path */ 5241da177e4SLinus Torvaldslws_compare_and_swap32: 525413059f2SGrant Grundler#ifdef CONFIG_64BIT 5261da177e4SLinus Torvalds /* Clip all the input registers */ 5271da177e4SLinus Torvalds depdi 0, 31, 32, %r26 5281da177e4SLinus Torvalds depdi 0, 31, 32, %r25 5291da177e4SLinus Torvalds depdi 0, 31, 32, %r24 5301da177e4SLinus Torvalds#endif 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvaldslws_compare_and_swap: 5331da177e4SLinus Torvalds /* Load start of lock table */ 5341da177e4SLinus Torvalds ldil L%lws_lock_start, %r20 5351da177e4SLinus Torvalds ldo R%lws_lock_start(%r20), %r28 5361da177e4SLinus Torvalds 5371da177e4SLinus Torvalds /* Extract four bits from r26 and hash lock (Bits 4-7) */ 5381da177e4SLinus Torvalds extru %r26, 27, 4, %r20 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds /* Find lock to use, the hash is either one of 0 to 5411da177e4SLinus Torvalds 15, multiplied by 16 (keep it 16-byte aligned) 5421da177e4SLinus Torvalds and add to the lock table offset. */ 5431da177e4SLinus Torvalds shlw %r20, 4, %r20 5441da177e4SLinus Torvalds add %r20, %r28, %r20 5451da177e4SLinus Torvalds 546b5e8b733SAurelien Jarno# if ENABLE_LWS_DEBUG 5471da177e4SLinus Torvalds /* 5481da177e4SLinus Torvalds DEBUG, check for deadlock! 5491da177e4SLinus Torvalds If the thread register values are the same 5501da177e4SLinus Torvalds then we were the one that locked it last and 5511da177e4SLinus Torvalds this is a recurisve call that will deadlock. 5521da177e4SLinus Torvalds We *must* giveup this call and fail. 5531da177e4SLinus Torvalds */ 5541da177e4SLinus Torvalds ldw 4(%sr2,%r20), %r28 /* Load thread register */ 555aa0eecb0SCarlos O'Donell /* WARNING: If cr27 cycles to the same value we have problems */ 5561da177e4SLinus Torvalds mfctl %cr27, %r21 /* Get current thread register */ 5571da177e4SLinus Torvalds cmpb,<>,n %r21, %r28, cas_lock /* Called recursive? */ 5581da177e4SLinus Torvalds b lws_exit /* Return error! */ 5591da177e4SLinus Torvalds ldo -EDEADLOCK(%r0), %r21 5601da177e4SLinus Torvaldscas_lock: 5611da177e4SLinus Torvalds cmpb,=,n %r0, %r28, cas_nocontend /* Is nobody using it? */ 5621da177e4SLinus Torvalds ldo 1(%r0), %r28 /* 1st case */ 5631da177e4SLinus Torvalds b lws_exit /* Contended... */ 5641da177e4SLinus Torvalds ldo -EAGAIN(%r0), %r21 /* Spin in userspace */ 5651da177e4SLinus Torvaldscas_nocontend: 5661da177e4SLinus Torvalds# endif 5671da177e4SLinus Torvalds/* ENABLE_LWS_DEBUG */ 5681da177e4SLinus Torvalds 56964f49532SKyle McMartin LDCW 0(%sr2,%r20), %r28 /* Try to acquire the lock */ 5701da177e4SLinus Torvalds cmpb,<>,n %r0, %r28, cas_action /* Did we get it? */ 5711da177e4SLinus Torvaldscas_wouldblock: 5721da177e4SLinus Torvalds ldo 2(%r0), %r28 /* 2nd case */ 5731da177e4SLinus Torvalds b lws_exit /* Contended... */ 5741da177e4SLinus Torvalds ldo -EAGAIN(%r0), %r21 /* Spin in userspace */ 5751da177e4SLinus Torvalds 5761da177e4SLinus Torvalds /* 5771da177e4SLinus Torvalds prev = *addr; 5781da177e4SLinus Torvalds if ( prev == old ) 5791da177e4SLinus Torvalds *addr = new; 5801da177e4SLinus Torvalds return prev; 5811da177e4SLinus Torvalds */ 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds /* NOTES: 5841da177e4SLinus Torvalds This all works becuse intr_do_signal 5851da177e4SLinus Torvalds and schedule both check the return iasq 5861da177e4SLinus Torvalds and see that we are on the kernel page 5871da177e4SLinus Torvalds so this process is never scheduled off 5881da177e4SLinus Torvalds or is ever sent any signal of any sort, 5891da177e4SLinus Torvalds thus it is wholly atomic from usrspaces 5901da177e4SLinus Torvalds perspective 5911da177e4SLinus Torvalds */ 5921da177e4SLinus Torvaldscas_action: 593b5e8b733SAurelien Jarno#if defined CONFIG_SMP && ENABLE_LWS_DEBUG 5941da177e4SLinus Torvalds /* DEBUG */ 5951da177e4SLinus Torvalds mfctl %cr27, %r1 5961da177e4SLinus Torvalds stw %r1, 4(%sr2,%r20) 5971da177e4SLinus Torvalds#endif 5981da177e4SLinus Torvalds /* The load and store could fail */ 5991da177e4SLinus Torvalds1: ldw 0(%sr3,%r26), %r28 6001da177e4SLinus Torvalds sub,<> %r28, %r25, %r0 6011da177e4SLinus Torvalds2: stw %r24, 0(%sr3,%r26) 6021da177e4SLinus Torvalds /* Free lock */ 6031da177e4SLinus Torvalds stw %r20, 0(%sr2,%r20) 604b5e8b733SAurelien Jarno#if ENABLE_LWS_DEBUG 6051da177e4SLinus Torvalds /* Clear thread register indicator */ 6061da177e4SLinus Torvalds stw %r0, 4(%sr2,%r20) 6071da177e4SLinus Torvalds#endif 6081da177e4SLinus Torvalds /* Return to userspace, set no error */ 6091da177e4SLinus Torvalds b lws_exit 6101da177e4SLinus Torvalds copy %r0, %r21 6111da177e4SLinus Torvalds 6121da177e4SLinus Torvalds3: 61325985edcSLucas De Marchi /* Error occurred on load or store */ 6141da177e4SLinus Torvalds /* Free lock */ 6151da177e4SLinus Torvalds stw %r20, 0(%sr2,%r20) 616b5e8b733SAurelien Jarno#if ENABLE_LWS_DEBUG 6171da177e4SLinus Torvalds stw %r0, 4(%sr2,%r20) 6181da177e4SLinus Torvalds#endif 6191da177e4SLinus Torvalds b lws_exit 6201da177e4SLinus Torvalds ldo -EFAULT(%r0),%r21 /* set errno */ 6211da177e4SLinus Torvalds nop 6221da177e4SLinus Torvalds nop 6231da177e4SLinus Torvalds nop 6241da177e4SLinus Torvalds nop 6251da177e4SLinus Torvalds 6261da177e4SLinus Torvalds /* Two exception table entries, one for the load, 6271da177e4SLinus Torvalds the other for the store. Either return -EFAULT. 6281da177e4SLinus Torvalds Each of the entries must be relocated. */ 6291da177e4SLinus Torvalds .section __ex_table,"aw" 6300b3d643fSHelge Deller ASM_ULONG_INSN (1b - linux_gateway_page), (3b - linux_gateway_page) 6310b3d643fSHelge Deller ASM_ULONG_INSN (2b - linux_gateway_page), (3b - linux_gateway_page) 6321da177e4SLinus Torvalds .previous 6331da177e4SLinus Torvalds 6341da177e4SLinus Torvalds 6351da177e4SLinus Torvalds /* Make sure nothing else is placed on this page */ 6361c593571SSam Ravnborg .align PAGE_SIZE 6378e9e9844SHelge DellerEND(linux_gateway_page) 6388e9e9844SHelge DellerENTRY(end_linux_gateway_page) 6391da177e4SLinus Torvalds 6401da177e4SLinus Torvalds /* Relocate symbols assuming linux_gateway_page is mapped 6411da177e4SLinus Torvalds to virtual address 0x0 */ 6428e9e9844SHelge Deller 6430b3d643fSHelge Deller#define LWS_ENTRY(_name_) ASM_ULONG_INSN (lws_##_name_ - linux_gateway_page) 6441da177e4SLinus Torvalds 6451bcdd854SHelge Deller .section .rodata,"a" 6461bcdd854SHelge Deller 6476a45716aSHelge Deller .align 8 6481da177e4SLinus Torvalds /* Light-weight-syscall table */ 6491da177e4SLinus Torvalds /* Start of lws table. */ 6508e9e9844SHelge DellerENTRY(lws_table) 6511da177e4SLinus Torvalds LWS_ENTRY(compare_and_swap32) /* 0 - ELF32 Atomic compare and swap */ 6521da177e4SLinus Torvalds LWS_ENTRY(compare_and_swap64) /* 1 - ELF64 Atomic compare and swap */ 6538e9e9844SHelge DellerEND(lws_table) 6541da177e4SLinus Torvalds /* End of lws table */ 6551da177e4SLinus Torvalds 6566a45716aSHelge Deller .align 8 6578e9e9844SHelge DellerENTRY(sys_call_table) 6581da177e4SLinus Torvalds#include "syscall_table.S" 6598e9e9844SHelge DellerEND(sys_call_table) 6601da177e4SLinus Torvalds 661413059f2SGrant Grundler#ifdef CONFIG_64BIT 6626a45716aSHelge Deller .align 8 6638e9e9844SHelge DellerENTRY(sys_call_table64) 6641da177e4SLinus Torvalds#define SYSCALL_TABLE_64BIT 6651da177e4SLinus Torvalds#include "syscall_table.S" 6668e9e9844SHelge DellerEND(sys_call_table64) 6671da177e4SLinus Torvalds#endif 6681da177e4SLinus Torvalds 6691da177e4SLinus Torvalds /* 6701da177e4SLinus Torvalds All light-weight-syscall atomic operations 6711da177e4SLinus Torvalds will use this set of locks 672c84c3a69SHelge Deller 673c84c3a69SHelge Deller NOTE: The lws_lock_start symbol must be 674c84c3a69SHelge Deller at least 16-byte aligned for safe use 675c84c3a69SHelge Deller with ldcw. 6761da177e4SLinus Torvalds */ 677dfcf753bSKyle McMartin .section .data 6786a45716aSHelge Deller .align L1_CACHE_BYTES 6798e9e9844SHelge DellerENTRY(lws_lock_start) 6801da177e4SLinus Torvalds /* lws locks */ 6811da177e4SLinus Torvalds .rept 16 6821da177e4SLinus Torvalds /* Keep locks aligned at 16-bytes */ 6831da177e4SLinus Torvalds .word 1 6841da177e4SLinus Torvalds .word 0 6851da177e4SLinus Torvalds .word 0 6861da177e4SLinus Torvalds .word 0 6871da177e4SLinus Torvalds .endr 6888e9e9844SHelge DellerEND(lws_lock_start) 6891da177e4SLinus Torvalds .previous 6901da177e4SLinus Torvalds 6911da177e4SLinus Torvalds.end 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds 694