11da177e4SLinus Torvalds/* 21da177e4SLinus Torvalds * Linux/PA-RISC Project (http://www.parisc-linux.org/) 31da177e4SLinus Torvalds * 4dde39798SHelge Deller * System call entry code / Linux gateway page 5dde39798SHelge Deller * Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai> 61da177e4SLinus Torvalds * Licensed under the GNU GPL. 71da177e4SLinus Torvalds * thanks to Philipp Rumpf, Mike Shaver and various others 81da177e4SLinus Torvalds * sorry about the wall, puffin.. 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds 11dde39798SHelge Deller/* 12dde39798SHelge DellerHow does the Linux gateway page on PA-RISC work? 13dde39798SHelge Deller------------------------------------------------ 14dde39798SHelge DellerThe Linux gateway page on PA-RISC is "special". 15dde39798SHelge DellerIt actually has PAGE_GATEWAY bits set (this is linux terminology; in parisc 16dde39798SHelge Dellerterminology it's Execute, promote to PL0) in the page map. So anything 17dde39798SHelge Dellerexecuting on this page executes with kernel level privilege (there's more to it 18dde39798SHelge Dellerthan that: to have this happen, you also have to use a branch with a ,gate 19dde39798SHelge Dellercompleter to activate the privilege promotion). The upshot is that everything 20dde39798SHelge Dellerthat runs on the gateway page runs at kernel privilege but with the current 21dde39798SHelge Delleruser process address space (although you have access to kernel space via %sr2). 22dde39798SHelge DellerFor the 0x100 syscall entry, we redo the space registers to point to the kernel 23dde39798SHelge Delleraddress space (preserving the user address space in %sr3), move to wide mode if 24dde39798SHelge Dellerrequired, save the user registers and branch into the kernel syscall entry 25dde39798SHelge Dellerpoint. For all the other functions, we execute at kernel privilege but don't 26dde39798SHelge Dellerflip address spaces. The basic upshot of this is that these code snippets are 27dde39798SHelge Dellerexecuted atomically (because the kernel can't be pre-empted) and they may 28dde39798SHelge Dellerperform architecturally forbidden (to PL3) operations (like setting control 29dde39798SHelge Dellerregisters). 30dde39798SHelge Deller*/ 31dde39798SHelge Deller 32dde39798SHelge Deller 330013a854SSam Ravnborg#include <asm/asm-offsets.h> 341da177e4SLinus Torvalds#include <asm/unistd.h> 351da177e4SLinus Torvalds#include <asm/errno.h> 361c593571SSam Ravnborg#include <asm/page.h> 371da177e4SLinus Torvalds#include <asm/psw.h> 381da177e4SLinus Torvalds#include <asm/thread_info.h> 391da177e4SLinus Torvalds#include <asm/assembly.h> 401da177e4SLinus Torvalds#include <asm/processor.h> 416a45716aSHelge Deller#include <asm/cache.h> 421da177e4SLinus Torvalds 438e9e9844SHelge Deller#include <linux/linkage.h> 448e9e9844SHelge Deller 451da177e4SLinus Torvalds /* We fill the empty parts of the gateway page with 461da177e4SLinus Torvalds * something that will kill the kernel or a 471da177e4SLinus Torvalds * userspace application. 481da177e4SLinus Torvalds */ 491da177e4SLinus Torvalds#define KILL_INSN break 0,0 501da177e4SLinus Torvalds 510b3d643fSHelge Deller .level LEVEL 528e9e9844SHelge Deller 53dfcf753bSKyle McMartin .text 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds .import syscall_exit,code 561da177e4SLinus Torvalds .import syscall_exit_rfi,code 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds /* Linux gateway page is aliased to virtual page 0 in the kernel 591da177e4SLinus Torvalds * address space. Since it is a gateway page it cannot be 601da177e4SLinus Torvalds * dereferenced, so null pointers will still fault. We start 611da177e4SLinus Torvalds * the actual entry point at 0x100. We put break instructions 621da177e4SLinus Torvalds * at the beginning of the page to trap null indirect function 631da177e4SLinus Torvalds * pointers. 641da177e4SLinus Torvalds */ 651da177e4SLinus Torvalds 661c593571SSam Ravnborg .align PAGE_SIZE 678e9e9844SHelge DellerENTRY(linux_gateway_page) 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */ 701da177e4SLinus Torvalds .rept 44 711da177e4SLinus Torvalds KILL_INSN 721da177e4SLinus Torvalds .endr 731da177e4SLinus Torvalds 74f4c0346cSJohn David Anglin /* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */ 751da177e4SLinus Torvalds /* Light-weight-syscall entry must always be located at 0xb0 */ 761da177e4SLinus Torvalds /* WARNING: Keep this number updated with table size changes */ 7789206491SGuy Martin#define __NR_lws_entries (3) 781da177e4SLinus Torvalds 791da177e4SLinus Torvaldslws_entry: 80f4c0346cSJohn David Anglin gate lws_start, %r0 /* increase privilege */ 81f4c0346cSJohn David Anglin depi 3, 31, 2, %r31 /* Ensure we return into user mode. */ 821da177e4SLinus Torvalds 83f4c0346cSJohn David Anglin /* Fill from 0xb8 to 0xe0 */ 84f4c0346cSJohn David Anglin .rept 10 851da177e4SLinus Torvalds KILL_INSN 861da177e4SLinus Torvalds .endr 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds /* This function MUST be located at 0xe0 for glibc's threading 891da177e4SLinus Torvalds mechanism to work. DO NOT MOVE THIS CODE EVER! */ 901da177e4SLinus Torvaldsset_thread_pointer: 911da177e4SLinus Torvalds gate .+8, %r0 /* increase privilege */ 921da177e4SLinus Torvalds depi 3, 31, 2, %r31 /* Ensure we return into user mode. */ 931da177e4SLinus Torvalds be 0(%sr7,%r31) /* return to user space */ 941da177e4SLinus Torvalds mtctl %r26, %cr27 /* move arg0 to the control register */ 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds /* Increase the chance of trapping if random jumps occur to this 971da177e4SLinus Torvalds address, fill from 0xf0 to 0x100 */ 981da177e4SLinus Torvalds .rept 4 991da177e4SLinus Torvalds KILL_INSN 1001da177e4SLinus Torvalds .endr 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds/* This address must remain fixed at 0x100 for glibc's syscalls to work */ 1031da177e4SLinus Torvalds .align 256 1041da177e4SLinus Torvaldslinux_gateway_entry: 1051da177e4SLinus Torvalds gate .+8, %r0 /* become privileged */ 1061da177e4SLinus Torvalds mtsp %r0,%sr4 /* get kernel space into sr4 */ 1071da177e4SLinus Torvalds mtsp %r0,%sr5 /* get kernel space into sr5 */ 1081da177e4SLinus Torvalds mtsp %r0,%sr6 /* get kernel space into sr6 */ 1091da177e4SLinus Torvalds mfsp %sr7,%r1 /* save user sr7 */ 1101da177e4SLinus Torvalds mtsp %r1,%sr3 /* and store it in sr3 */ 1111da177e4SLinus Torvalds 112413059f2SGrant Grundler#ifdef CONFIG_64BIT 1131da177e4SLinus Torvalds /* for now we can *always* set the W bit on entry to the syscall 1141da177e4SLinus Torvalds * since we don't support wide userland processes. We could 1151da177e4SLinus Torvalds * also save the current SM other than in r0 and restore it on 1161da177e4SLinus Torvalds * exit from the syscall, and also use that value to know 1171da177e4SLinus Torvalds * whether to do narrow or wide syscalls. -PB 1181da177e4SLinus Torvalds */ 1191da177e4SLinus Torvalds ssm PSW_SM_W, %r1 1201da177e4SLinus Torvalds extrd,u %r1,PSW_W_BIT,1,%r1 1211da177e4SLinus Torvalds /* sp must be aligned on 4, so deposit the W bit setting into 1221da177e4SLinus Torvalds * the bottom of sp temporarily */ 1231da177e4SLinus Torvalds or,ev %r1,%r30,%r30 1241da177e4SLinus Torvalds b,n 1f 1251da177e4SLinus Torvalds /* The top halves of argument registers must be cleared on syscall 1261da177e4SLinus Torvalds * entry from narrow executable. 1271da177e4SLinus Torvalds */ 1281da177e4SLinus Torvalds depdi 0, 31, 32, %r26 1291da177e4SLinus Torvalds depdi 0, 31, 32, %r25 1301da177e4SLinus Torvalds depdi 0, 31, 32, %r24 1311da177e4SLinus Torvalds depdi 0, 31, 32, %r23 1321da177e4SLinus Torvalds depdi 0, 31, 32, %r22 1331da177e4SLinus Torvalds depdi 0, 31, 32, %r21 1341da177e4SLinus Torvalds1: 1351da177e4SLinus Torvalds#endif 1361da177e4SLinus Torvalds mfctl %cr30,%r1 1371da177e4SLinus Torvalds xor %r1,%r30,%r30 /* ye olde xor trick */ 1381da177e4SLinus Torvalds xor %r1,%r30,%r1 1391da177e4SLinus Torvalds xor %r1,%r30,%r30 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds ldo THREAD_SZ_ALGN+FRAME_SIZE(%r30),%r30 /* set up kernel stack */ 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds /* N.B.: It is critical that we don't set sr7 to 0 until r30 1441da177e4SLinus Torvalds * contains a valid kernel stack pointer. It is also 1451da177e4SLinus Torvalds * critical that we don't start using the kernel stack 1461da177e4SLinus Torvalds * until after sr7 has been set to 0. 1471da177e4SLinus Torvalds */ 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds mtsp %r0,%sr7 /* get kernel space into sr7 */ 1501da177e4SLinus Torvalds STREGM %r1,FRAME_SIZE(%r30) /* save r1 (usp) here for now */ 1511da177e4SLinus Torvalds mfctl %cr30,%r1 /* get task ptr in %r1 */ 1521da177e4SLinus Torvalds LDREG TI_TASK(%r1),%r1 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds /* Save some registers for sigcontext and potential task 1551da177e4SLinus Torvalds switch (see entry.S for the details of which ones are 1561da177e4SLinus Torvalds saved/restored). TASK_PT_PSW is zeroed so we can see whether 1571da177e4SLinus Torvalds a process is on a syscall or not. For an interrupt the real 1581da177e4SLinus Torvalds PSW value is stored. This is needed for gdb and sys_ptrace. */ 1591da177e4SLinus Torvalds STREG %r0, TASK_PT_PSW(%r1) 1601da177e4SLinus Torvalds STREG %r2, TASK_PT_GR2(%r1) /* preserve rp */ 1611da177e4SLinus Torvalds STREG %r19, TASK_PT_GR19(%r1) 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds LDREGM -FRAME_SIZE(%r30), %r2 /* get users sp back */ 164413059f2SGrant Grundler#ifdef CONFIG_64BIT 1651da177e4SLinus Torvalds extrd,u %r2,63,1,%r19 /* W hidden in bottom bit */ 1661da177e4SLinus Torvalds#if 0 1671da177e4SLinus Torvalds xor %r19,%r2,%r2 /* clear bottom bit */ 1681da177e4SLinus Torvalds depd,z %r19,1,1,%r19 1691da177e4SLinus Torvalds std %r19,TASK_PT_PSW(%r1) 1701da177e4SLinus Torvalds#endif 1711da177e4SLinus Torvalds#endif 1721da177e4SLinus Torvalds STREG %r2, TASK_PT_GR30(%r1) /* ... and save it */ 1731da177e4SLinus Torvalds 174aa0eecb0SCarlos O'Donell STREG %r20, TASK_PT_GR20(%r1) /* Syscall number */ 1751da177e4SLinus Torvalds STREG %r21, TASK_PT_GR21(%r1) 1761da177e4SLinus Torvalds STREG %r22, TASK_PT_GR22(%r1) 1771da177e4SLinus Torvalds STREG %r23, TASK_PT_GR23(%r1) /* 4th argument */ 1781da177e4SLinus Torvalds STREG %r24, TASK_PT_GR24(%r1) /* 3rd argument */ 1791da177e4SLinus Torvalds STREG %r25, TASK_PT_GR25(%r1) /* 2nd argument */ 1801da177e4SLinus Torvalds STREG %r26, TASK_PT_GR26(%r1) /* 1st argument */ 1811da177e4SLinus Torvalds STREG %r27, TASK_PT_GR27(%r1) /* user dp */ 1821da177e4SLinus Torvalds STREG %r28, TASK_PT_GR28(%r1) /* return value 0 */ 18300df111eSAl Viro STREG %r0, TASK_PT_ORIG_R28(%r1) /* don't prohibit restarts */ 1841da177e4SLinus Torvalds STREG %r29, TASK_PT_GR29(%r1) /* return value 1 */ 1851da177e4SLinus Torvalds STREG %r31, TASK_PT_GR31(%r1) /* preserve syscall return ptr */ 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds ldo TASK_PT_FR0(%r1), %r27 /* save fpregs from the kernel */ 1881da177e4SLinus Torvalds save_fp %r27 /* or potential task switch */ 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds mfctl %cr11, %r27 /* i.e. SAR */ 1911da177e4SLinus Torvalds STREG %r27, TASK_PT_SAR(%r1) 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds loadgp 1941da177e4SLinus Torvalds 195413059f2SGrant Grundler#ifdef CONFIG_64BIT 1961da177e4SLinus Torvalds ldo -16(%r30),%r29 /* Reference param save area */ 1971da177e4SLinus Torvalds copy %r19,%r2 /* W bit back to r2 */ 1981da177e4SLinus Torvalds#else 1991da177e4SLinus Torvalds /* no need to save these on stack in wide mode because the first 8 2001da177e4SLinus Torvalds * args are passed in registers */ 2011da177e4SLinus Torvalds stw %r22, -52(%r30) /* 5th argument */ 2021da177e4SLinus Torvalds stw %r21, -56(%r30) /* 6th argument */ 2031da177e4SLinus Torvalds#endif 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds /* Are we being ptraced? */ 2061da177e4SLinus Torvalds mfctl %cr30, %r1 20764482bd8SAl Viro LDREG TI_FLAGS(%r1),%r1 20864482bd8SAl Viro ldi _TIF_SYSCALL_TRACE_MASK, %r19 20964482bd8SAl Viro and,COND(=) %r1, %r19, %r0 21064482bd8SAl Viro b,n .Ltracesys 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds /* Note! We cannot use the syscall table that is mapped 2131da177e4SLinus Torvalds nearby since the gateway page is mapped execute-only. */ 2141da177e4SLinus Torvalds 215413059f2SGrant Grundler#ifdef CONFIG_64BIT 2161da177e4SLinus Torvalds ldil L%sys_call_table, %r1 2171da177e4SLinus Torvalds or,= %r2,%r2,%r2 2181da177e4SLinus Torvalds addil L%(sys_call_table64-sys_call_table), %r1 2191da177e4SLinus Torvalds ldo R%sys_call_table(%r1), %r19 2201da177e4SLinus Torvalds or,= %r2,%r2,%r2 2211da177e4SLinus Torvalds ldo R%sys_call_table64(%r1), %r19 2221da177e4SLinus Torvalds#else 2231da177e4SLinus Torvalds ldil L%sys_call_table, %r1 2241da177e4SLinus Torvalds ldo R%sys_call_table(%r1), %r19 2251da177e4SLinus Torvalds#endif 2263bb457afSKyle McMartin comiclr,>> __NR_Linux_syscalls, %r20, %r0 2271da177e4SLinus Torvalds b,n .Lsyscall_nosys 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds LDREGX %r20(%r19), %r19 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds /* If this is a sys_rt_sigreturn call, and the signal was received 2321da177e4SLinus Torvalds * when not in_syscall, then we want to return via syscall_exit_rfi, 2331da177e4SLinus Torvalds * not syscall_exit. Signal no. in r20, in_syscall in r25 (see 2341da177e4SLinus Torvalds * trampoline code in signal.c). 2351da177e4SLinus Torvalds */ 2361da177e4SLinus Torvalds ldi __NR_rt_sigreturn,%r2 2371da177e4SLinus Torvalds comb,= %r2,%r20,.Lrt_sigreturn 2381da177e4SLinus Torvalds.Lin_syscall: 2391da177e4SLinus Torvalds ldil L%syscall_exit,%r2 2401da177e4SLinus Torvalds be 0(%sr7,%r19) 2411da177e4SLinus Torvalds ldo R%syscall_exit(%r2),%r2 2421da177e4SLinus Torvalds.Lrt_sigreturn: 2431da177e4SLinus Torvalds comib,<> 0,%r25,.Lin_syscall 2441da177e4SLinus Torvalds ldil L%syscall_exit_rfi,%r2 2451da177e4SLinus Torvalds be 0(%sr7,%r19) 2461da177e4SLinus Torvalds ldo R%syscall_exit_rfi(%r2),%r2 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds /* Note! Because we are not running where we were linked, any 2491da177e4SLinus Torvalds calls to functions external to this file must be indirect. To 2501da177e4SLinus Torvalds be safe, we apply the opposite rule to functions within this 2511da177e4SLinus Torvalds file, with local labels given to them to ensure correctness. */ 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds.Lsyscall_nosys: 2541da177e4SLinus Torvaldssyscall_nosys: 2551da177e4SLinus Torvalds ldil L%syscall_exit,%r1 2561da177e4SLinus Torvalds be R%syscall_exit(%sr7,%r1) 2571da177e4SLinus Torvalds ldo -ENOSYS(%r0),%r28 /* set errno */ 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds/* Warning! This trace code is a virtual duplicate of the code above so be 2611da177e4SLinus Torvalds * sure to maintain both! */ 2621da177e4SLinus Torvalds.Ltracesys: 2631da177e4SLinus Torvaldstracesys: 2641da177e4SLinus Torvalds /* Need to save more registers so the debugger can see where we 2651da177e4SLinus Torvalds * are. This saves only the lower 8 bits of PSW, so that the C 2661da177e4SLinus Torvalds * bit is still clear on syscalls, and the D bit is set if this 2671da177e4SLinus Torvalds * full register save path has been executed. We check the D 2681da177e4SLinus Torvalds * bit on syscall_return_rfi to determine which registers to 2691da177e4SLinus Torvalds * restore. An interrupt results in a full PSW saved with the 2701da177e4SLinus Torvalds * C bit set, a non-straced syscall entry results in C and D clear 2711da177e4SLinus Torvalds * in the saved PSW. 2721da177e4SLinus Torvalds */ 2731da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 2741da177e4SLinus Torvalds LDREG TI_TASK(%r1), %r1 2751da177e4SLinus Torvalds ssm 0,%r2 2761da177e4SLinus Torvalds STREG %r2,TASK_PT_PSW(%r1) /* Lower 8 bits only!! */ 2771da177e4SLinus Torvalds mfsp %sr0,%r2 2781da177e4SLinus Torvalds STREG %r2,TASK_PT_SR0(%r1) 2791da177e4SLinus Torvalds mfsp %sr1,%r2 2801da177e4SLinus Torvalds STREG %r2,TASK_PT_SR1(%r1) 2811da177e4SLinus Torvalds mfsp %sr2,%r2 2821da177e4SLinus Torvalds STREG %r2,TASK_PT_SR2(%r1) 2831da177e4SLinus Torvalds mfsp %sr3,%r2 2841da177e4SLinus Torvalds STREG %r2,TASK_PT_SR3(%r1) 2851da177e4SLinus Torvalds STREG %r2,TASK_PT_SR4(%r1) 2861da177e4SLinus Torvalds STREG %r2,TASK_PT_SR5(%r1) 2871da177e4SLinus Torvalds STREG %r2,TASK_PT_SR6(%r1) 2881da177e4SLinus Torvalds STREG %r2,TASK_PT_SR7(%r1) 2891da177e4SLinus Torvalds STREG %r2,TASK_PT_IASQ0(%r1) 2901da177e4SLinus Torvalds STREG %r2,TASK_PT_IASQ1(%r1) 2911da177e4SLinus Torvalds LDREG TASK_PT_GR31(%r1),%r2 2921da177e4SLinus Torvalds STREG %r2,TASK_PT_IAOQ0(%r1) 2931da177e4SLinus Torvalds ldo 4(%r2),%r2 2941da177e4SLinus Torvalds STREG %r2,TASK_PT_IAOQ1(%r1) 2951da177e4SLinus Torvalds ldo TASK_REGS(%r1),%r2 2961da177e4SLinus Torvalds /* reg_save %r2 */ 2971da177e4SLinus Torvalds STREG %r3,PT_GR3(%r2) 2981da177e4SLinus Torvalds STREG %r4,PT_GR4(%r2) 2991da177e4SLinus Torvalds STREG %r5,PT_GR5(%r2) 3001da177e4SLinus Torvalds STREG %r6,PT_GR6(%r2) 3011da177e4SLinus Torvalds STREG %r7,PT_GR7(%r2) 3021da177e4SLinus Torvalds STREG %r8,PT_GR8(%r2) 3031da177e4SLinus Torvalds STREG %r9,PT_GR9(%r2) 3041da177e4SLinus Torvalds STREG %r10,PT_GR10(%r2) 3051da177e4SLinus Torvalds STREG %r11,PT_GR11(%r2) 3061da177e4SLinus Torvalds STREG %r12,PT_GR12(%r2) 3071da177e4SLinus Torvalds STREG %r13,PT_GR13(%r2) 3081da177e4SLinus Torvalds STREG %r14,PT_GR14(%r2) 3091da177e4SLinus Torvalds STREG %r15,PT_GR15(%r2) 3101da177e4SLinus Torvalds STREG %r16,PT_GR16(%r2) 3111da177e4SLinus Torvalds STREG %r17,PT_GR17(%r2) 3121da177e4SLinus Torvalds STREG %r18,PT_GR18(%r2) 3131da177e4SLinus Torvalds /* Finished saving things for the debugger */ 3141da177e4SLinus Torvalds 3152798af1aSKyle McMartin copy %r2,%r26 3162798af1aSKyle McMartin ldil L%do_syscall_trace_enter,%r1 3171da177e4SLinus Torvalds ldil L%tracesys_next,%r2 3182798af1aSKyle McMartin be R%do_syscall_trace_enter(%sr7,%r1) 3191da177e4SLinus Torvalds ldo R%tracesys_next(%r2),%r2 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvaldstracesys_next: 3222798af1aSKyle McMartin /* do_syscall_trace_enter either returned the syscallno, or -1L, 3232798af1aSKyle McMartin * so we skip restoring the PT_GR20 below, since we pulled it from 3242798af1aSKyle McMartin * task->thread.regs.gr[20] above. 3252798af1aSKyle McMartin */ 3262798af1aSKyle McMartin copy %ret0,%r20 3271da177e4SLinus Torvalds ldil L%sys_call_table,%r1 3281da177e4SLinus Torvalds ldo R%sys_call_table(%r1), %r19 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 3311da177e4SLinus Torvalds LDREG TI_TASK(%r1), %r1 3321da177e4SLinus Torvalds LDREG TASK_PT_GR26(%r1), %r26 /* Restore the users args */ 3331da177e4SLinus Torvalds LDREG TASK_PT_GR25(%r1), %r25 3341da177e4SLinus Torvalds LDREG TASK_PT_GR24(%r1), %r24 3351da177e4SLinus Torvalds LDREG TASK_PT_GR23(%r1), %r23 3361da177e4SLinus Torvalds LDREG TASK_PT_GR22(%r1), %r22 3371da177e4SLinus Torvalds LDREG TASK_PT_GR21(%r1), %r21 33852ab532eSAl Viro#ifdef CONFIG_64BIT 3391da177e4SLinus Torvalds ldo -16(%r30),%r29 /* Reference param save area */ 34052ab532eSAl Viro#else 34152ab532eSAl Viro stw %r22, -52(%r30) /* 5th argument */ 34252ab532eSAl Viro stw %r21, -56(%r30) /* 6th argument */ 3431da177e4SLinus Torvalds#endif 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds comiclr,>>= __NR_Linux_syscalls, %r20, %r0 34698e8b6c9SHelge Deller b,n .Ltracesys_nosys 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds LDREGX %r20(%r19), %r19 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds /* If this is a sys_rt_sigreturn call, and the signal was received 3511da177e4SLinus Torvalds * when not in_syscall, then we want to return via syscall_exit_rfi, 3521da177e4SLinus Torvalds * not syscall_exit. Signal no. in r20, in_syscall in r25 (see 3531da177e4SLinus Torvalds * trampoline code in signal.c). 3541da177e4SLinus Torvalds */ 3551da177e4SLinus Torvalds ldi __NR_rt_sigreturn,%r2 3561da177e4SLinus Torvalds comb,= %r2,%r20,.Ltrace_rt_sigreturn 3571da177e4SLinus Torvalds.Ltrace_in_syscall: 3581da177e4SLinus Torvalds ldil L%tracesys_exit,%r2 3591da177e4SLinus Torvalds be 0(%sr7,%r19) 3601da177e4SLinus Torvalds ldo R%tracesys_exit(%r2),%r2 3611da177e4SLinus Torvalds 36298e8b6c9SHelge Deller.Ltracesys_nosys: 36398e8b6c9SHelge Deller ldo -ENOSYS(%r0),%r28 /* set errno */ 36498e8b6c9SHelge Deller 3651da177e4SLinus Torvalds /* Do *not* call this function on the gateway page, because it 3661da177e4SLinus Torvalds makes a direct call to syscall_trace. */ 3671da177e4SLinus Torvalds 3681da177e4SLinus Torvaldstracesys_exit: 3691da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 3701da177e4SLinus Torvalds LDREG TI_TASK(%r1), %r1 371413059f2SGrant Grundler#ifdef CONFIG_64BIT 3721da177e4SLinus Torvalds ldo -16(%r30),%r29 /* Reference param save area */ 3731da177e4SLinus Torvalds#endif 3742798af1aSKyle McMartin ldo TASK_REGS(%r1),%r26 375337685e5SHelge Deller BL do_syscall_trace_exit,%r2 3761da177e4SLinus Torvalds STREG %r28,TASK_PT_GR28(%r1) /* save return value now */ 3771da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 3781da177e4SLinus Torvalds LDREG TI_TASK(%r1), %r1 3791da177e4SLinus Torvalds LDREG TASK_PT_GR28(%r1), %r28 /* Restore return val. */ 3801da177e4SLinus Torvalds 3811da177e4SLinus Torvalds ldil L%syscall_exit,%r1 3821da177e4SLinus Torvalds be,n R%syscall_exit(%sr7,%r1) 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds.Ltrace_rt_sigreturn: 3851da177e4SLinus Torvalds comib,<> 0,%r25,.Ltrace_in_syscall 3861da177e4SLinus Torvalds ldil L%tracesys_sigexit,%r2 3871da177e4SLinus Torvalds be 0(%sr7,%r19) 3881da177e4SLinus Torvalds ldo R%tracesys_sigexit(%r2),%r2 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvaldstracesys_sigexit: 3911da177e4SLinus Torvalds ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ 3925837d42fSKyle McMartin LDREG TI_TASK(%r1), %r1 393413059f2SGrant Grundler#ifdef CONFIG_64BIT 3941da177e4SLinus Torvalds ldo -16(%r30),%r29 /* Reference param save area */ 3951da177e4SLinus Torvalds#endif 396337685e5SHelge Deller BL do_syscall_trace_exit,%r2 3972798af1aSKyle McMartin ldo TASK_REGS(%r1),%r26 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds ldil L%syscall_exit_rfi,%r1 4001da177e4SLinus Torvalds be,n R%syscall_exit_rfi(%sr7,%r1) 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds /********************************************************* 404c84c3a69SHelge Deller 32/64-bit Light-Weight-Syscall ABI 4051da177e4SLinus Torvalds 406c84c3a69SHelge Deller * - Indicates a hint for userspace inline asm 407c84c3a69SHelge Deller implementations. 4081da177e4SLinus Torvalds 409c84c3a69SHelge Deller Syscall number (caller-saves) 410c84c3a69SHelge Deller - %r20 411c84c3a69SHelge Deller * In asm clobber. 4121da177e4SLinus Torvalds 413c84c3a69SHelge Deller Argument registers (caller-saves) 414c84c3a69SHelge Deller - %r26, %r25, %r24, %r23, %r22 415c84c3a69SHelge Deller * In asm input. 416c84c3a69SHelge Deller 417c84c3a69SHelge Deller Return registers (caller-saves) 418c84c3a69SHelge Deller - %r28 (return), %r21 (errno) 419c84c3a69SHelge Deller * In asm output. 420c84c3a69SHelge Deller 421c84c3a69SHelge Deller Caller-saves registers 422c84c3a69SHelge Deller - %r1, %r27, %r29 423c84c3a69SHelge Deller - %r2 (return pointer) 424c84c3a69SHelge Deller - %r31 (ble link register) 425c84c3a69SHelge Deller * In asm clobber. 426c84c3a69SHelge Deller 427c84c3a69SHelge Deller Callee-saves registers 428c84c3a69SHelge Deller - %r3-%r18 429c84c3a69SHelge Deller - %r30 (stack pointer) 430c84c3a69SHelge Deller * Not in asm clobber. 431c84c3a69SHelge Deller 432c84c3a69SHelge Deller If userspace is 32-bit: 433c84c3a69SHelge Deller Callee-saves registers 434c84c3a69SHelge Deller - %r19 (32-bit PIC register) 435c84c3a69SHelge Deller 436c84c3a69SHelge Deller Differences from 32-bit calling convention: 437c84c3a69SHelge Deller - Syscall number in %r20 438c84c3a69SHelge Deller - Additional argument register %r22 (arg4) 439c84c3a69SHelge Deller - Callee-saves %r19. 440c84c3a69SHelge Deller 441c84c3a69SHelge Deller If userspace is 64-bit: 442c84c3a69SHelge Deller Callee-saves registers 443c84c3a69SHelge Deller - %r27 (64-bit PIC register) 444c84c3a69SHelge Deller 445c84c3a69SHelge Deller Differences from 64-bit calling convention: 446c84c3a69SHelge Deller - Syscall number in %r20 447c84c3a69SHelge Deller - Additional argument register %r22 (arg4) 448c84c3a69SHelge Deller - Callee-saves %r27. 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds Error codes returned by entry path: 4511da177e4SLinus Torvalds 4521da177e4SLinus Torvalds ENOSYS - r20 was an invalid LWS number. 4531da177e4SLinus Torvalds 4541da177e4SLinus Torvalds *********************************************************/ 4551da177e4SLinus Torvaldslws_start: 4561da177e4SLinus Torvalds 457413059f2SGrant Grundler#ifdef CONFIG_64BIT 4581da177e4SLinus Torvalds /* FIXME: If we are a 64-bit kernel just 4591da177e4SLinus Torvalds * turn this on unconditionally. 4601da177e4SLinus Torvalds */ 4611da177e4SLinus Torvalds ssm PSW_SM_W, %r1 4621da177e4SLinus Torvalds extrd,u %r1,PSW_W_BIT,1,%r1 4631da177e4SLinus Torvalds /* sp must be aligned on 4, so deposit the W bit setting into 4641da177e4SLinus Torvalds * the bottom of sp temporarily */ 4651da177e4SLinus Torvalds or,ev %r1,%r30,%r30 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds /* Clip LWS number to a 32-bit value always */ 4681da177e4SLinus Torvalds depdi 0, 31, 32, %r20 4691da177e4SLinus Torvalds#endif 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds /* Is the lws entry number valid? */ 472f4c0346cSJohn David Anglin comiclr,>> __NR_lws_entries, %r20, %r0 4731da177e4SLinus Torvalds b,n lws_exit_nosys 4741da177e4SLinus Torvalds 4751da177e4SLinus Torvalds /* WARNING: Trashing sr2 and sr3 */ 4761da177e4SLinus Torvalds mfsp %sr7,%r1 /* get userspace into sr3 */ 4771da177e4SLinus Torvalds mtsp %r1,%sr3 4781da177e4SLinus Torvalds mtsp %r0,%sr2 /* get kernel space into sr2 */ 4791da177e4SLinus Torvalds 4801da177e4SLinus Torvalds /* Load table start */ 4811da177e4SLinus Torvalds ldil L%lws_table, %r1 4821da177e4SLinus Torvalds ldo R%lws_table(%r1), %r28 /* Scratch use of r28 */ 4831da177e4SLinus Torvalds LDREGX %r20(%sr2,r28), %r21 /* Scratch use of r21 */ 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds /* Jump to lws, lws table pointers already relocated */ 4861da177e4SLinus Torvalds be,n 0(%sr2,%r21) 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvaldslws_exit_nosys: 4891da177e4SLinus Torvalds ldo -ENOSYS(%r0),%r21 /* set errno */ 4901da177e4SLinus Torvalds /* Fall through: Return to userspace */ 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvaldslws_exit: 493413059f2SGrant Grundler#ifdef CONFIG_64BIT 4941da177e4SLinus Torvalds /* decide whether to reset the wide mode bit 4951da177e4SLinus Torvalds * 4961da177e4SLinus Torvalds * For a syscall, the W bit is stored in the lowest bit 4971da177e4SLinus Torvalds * of sp. Extract it and reset W if it is zero */ 4981da177e4SLinus Torvalds extrd,u,*<> %r30,63,1,%r1 4991da177e4SLinus Torvalds rsm PSW_SM_W, %r0 5001da177e4SLinus Torvalds /* now reset the lowest bit of sp if it was set */ 5011da177e4SLinus Torvalds xor %r30,%r1,%r30 5021da177e4SLinus Torvalds#endif 503f4c0346cSJohn David Anglin be,n 0(%sr7, %r31) 5041da177e4SLinus Torvalds 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds 5071da177e4SLinus Torvalds /*************************************************** 50889206491SGuy Martin Implementing 32bit CAS as an atomic operation: 5091da177e4SLinus Torvalds 5101da177e4SLinus Torvalds %r26 - Address to examine 5111da177e4SLinus Torvalds %r25 - Old value to check (old) 5121da177e4SLinus Torvalds %r24 - New value to set (new) 5131da177e4SLinus Torvalds %r28 - Return prev through this register. 5141da177e4SLinus Torvalds %r21 - Kernel error code 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds If debugging is DISabled: 5171da177e4SLinus Torvalds 5181da177e4SLinus Torvalds %r21 has the following meanings: 5191da177e4SLinus Torvalds 5201da177e4SLinus Torvalds EAGAIN - CAS is busy, ldcw failed, try again. 5211da177e4SLinus Torvalds EFAULT - Read or write failed. 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds If debugging is enabled: 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds EDEADLOCK - CAS called recursively. 5261da177e4SLinus Torvalds EAGAIN && r28 == 1 - CAS is busy. Lock contended. 5271da177e4SLinus Torvalds EAGAIN && r28 == 2 - CAS is busy. ldcw failed. 5281da177e4SLinus Torvalds EFAULT - Read or write failed. 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds Scratch: r20, r28, r1 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds ****************************************************/ 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds /* Do not enable LWS debugging */ 5351da177e4SLinus Torvalds#define ENABLE_LWS_DEBUG 0 5361da177e4SLinus Torvalds 5371da177e4SLinus Torvalds /* ELF64 Process entry path */ 5381da177e4SLinus Torvaldslws_compare_and_swap64: 539413059f2SGrant Grundler#ifdef CONFIG_64BIT 5401da177e4SLinus Torvalds b,n lws_compare_and_swap 5411da177e4SLinus Torvalds#else 5421da177e4SLinus Torvalds /* If we are not a 64-bit kernel, then we don't 543c84c3a69SHelge Deller * have 64-bit input registers, and calling 544c84c3a69SHelge Deller * the 64-bit LWS CAS returns ENOSYS. 5451da177e4SLinus Torvalds */ 5461da177e4SLinus Torvalds b,n lws_exit_nosys 5471da177e4SLinus Torvalds#endif 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds /* ELF32 Process entry path */ 5501da177e4SLinus Torvaldslws_compare_and_swap32: 551413059f2SGrant Grundler#ifdef CONFIG_64BIT 5521da177e4SLinus Torvalds /* Clip all the input registers */ 5531da177e4SLinus Torvalds depdi 0, 31, 32, %r26 5541da177e4SLinus Torvalds depdi 0, 31, 32, %r25 5551da177e4SLinus Torvalds depdi 0, 31, 32, %r24 5561da177e4SLinus Torvalds#endif 5571da177e4SLinus Torvalds 5581da177e4SLinus Torvaldslws_compare_and_swap: 5591da177e4SLinus Torvalds /* Load start of lock table */ 5601da177e4SLinus Torvalds ldil L%lws_lock_start, %r20 5611da177e4SLinus Torvalds ldo R%lws_lock_start(%r20), %r28 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds /* Extract four bits from r26 and hash lock (Bits 4-7) */ 5641da177e4SLinus Torvalds extru %r26, 27, 4, %r20 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds /* Find lock to use, the hash is either one of 0 to 5671da177e4SLinus Torvalds 15, multiplied by 16 (keep it 16-byte aligned) 5681da177e4SLinus Torvalds and add to the lock table offset. */ 5691da177e4SLinus Torvalds shlw %r20, 4, %r20 5701da177e4SLinus Torvalds add %r20, %r28, %r20 5711da177e4SLinus Torvalds 572b5e8b733SAurelien Jarno# if ENABLE_LWS_DEBUG 5731da177e4SLinus Torvalds /* 5741da177e4SLinus Torvalds DEBUG, check for deadlock! 5751da177e4SLinus Torvalds If the thread register values are the same 5761da177e4SLinus Torvalds then we were the one that locked it last and 5771da177e4SLinus Torvalds this is a recurisve call that will deadlock. 5781da177e4SLinus Torvalds We *must* giveup this call and fail. 5791da177e4SLinus Torvalds */ 5801da177e4SLinus Torvalds ldw 4(%sr2,%r20), %r28 /* Load thread register */ 581aa0eecb0SCarlos O'Donell /* WARNING: If cr27 cycles to the same value we have problems */ 5821da177e4SLinus Torvalds mfctl %cr27, %r21 /* Get current thread register */ 5831da177e4SLinus Torvalds cmpb,<>,n %r21, %r28, cas_lock /* Called recursive? */ 5841da177e4SLinus Torvalds b lws_exit /* Return error! */ 5851da177e4SLinus Torvalds ldo -EDEADLOCK(%r0), %r21 5861da177e4SLinus Torvaldscas_lock: 5871da177e4SLinus Torvalds cmpb,=,n %r0, %r28, cas_nocontend /* Is nobody using it? */ 5881da177e4SLinus Torvalds ldo 1(%r0), %r28 /* 1st case */ 5891da177e4SLinus Torvalds b lws_exit /* Contended... */ 5901da177e4SLinus Torvalds ldo -EAGAIN(%r0), %r21 /* Spin in userspace */ 5911da177e4SLinus Torvaldscas_nocontend: 5921da177e4SLinus Torvalds# endif 5931da177e4SLinus Torvalds/* ENABLE_LWS_DEBUG */ 5941da177e4SLinus Torvalds 595c776cd89SJohn David Anglin rsm PSW_SM_I, %r0 /* Disable interrupts */ 596c776cd89SJohn David Anglin /* COW breaks can cause contention on UP systems */ 59764f49532SKyle McMartin LDCW 0(%sr2,%r20), %r28 /* Try to acquire the lock */ 5981da177e4SLinus Torvalds cmpb,<>,n %r0, %r28, cas_action /* Did we get it? */ 5991da177e4SLinus Torvaldscas_wouldblock: 6001da177e4SLinus Torvalds ldo 2(%r0), %r28 /* 2nd case */ 601c776cd89SJohn David Anglin ssm PSW_SM_I, %r0 6021da177e4SLinus Torvalds b lws_exit /* Contended... */ 6031da177e4SLinus Torvalds ldo -EAGAIN(%r0), %r21 /* Spin in userspace */ 6041da177e4SLinus Torvalds 6051da177e4SLinus Torvalds /* 6061da177e4SLinus Torvalds prev = *addr; 6071da177e4SLinus Torvalds if ( prev == old ) 6081da177e4SLinus Torvalds *addr = new; 6091da177e4SLinus Torvalds return prev; 6101da177e4SLinus Torvalds */ 6111da177e4SLinus Torvalds 6121da177e4SLinus Torvalds /* NOTES: 6131da177e4SLinus Torvalds This all works becuse intr_do_signal 6141da177e4SLinus Torvalds and schedule both check the return iasq 6151da177e4SLinus Torvalds and see that we are on the kernel page 6161da177e4SLinus Torvalds so this process is never scheduled off 6171da177e4SLinus Torvalds or is ever sent any signal of any sort, 6181da177e4SLinus Torvalds thus it is wholly atomic from usrspaces 6191da177e4SLinus Torvalds perspective 6201da177e4SLinus Torvalds */ 6211da177e4SLinus Torvaldscas_action: 622b5e8b733SAurelien Jarno#if defined CONFIG_SMP && ENABLE_LWS_DEBUG 6231da177e4SLinus Torvalds /* DEBUG */ 6241da177e4SLinus Torvalds mfctl %cr27, %r1 6251da177e4SLinus Torvalds stw %r1, 4(%sr2,%r20) 6261da177e4SLinus Torvalds#endif 6271da177e4SLinus Torvalds /* The load and store could fail */ 628c776cd89SJohn David Anglin1: ldw,ma 0(%sr3,%r26), %r28 6291da177e4SLinus Torvalds sub,<> %r28, %r25, %r0 630c776cd89SJohn David Anglin2: stw,ma %r24, 0(%sr3,%r26) 6311da177e4SLinus Torvalds /* Free lock */ 632c776cd89SJohn David Anglin stw,ma %r20, 0(%sr2,%r20) 633b5e8b733SAurelien Jarno#if ENABLE_LWS_DEBUG 6341da177e4SLinus Torvalds /* Clear thread register indicator */ 6351da177e4SLinus Torvalds stw %r0, 4(%sr2,%r20) 6361da177e4SLinus Torvalds#endif 637c776cd89SJohn David Anglin /* Enable interrupts */ 638c776cd89SJohn David Anglin ssm PSW_SM_I, %r0 6391da177e4SLinus Torvalds /* Return to userspace, set no error */ 6401da177e4SLinus Torvalds b lws_exit 6411da177e4SLinus Torvalds copy %r0, %r21 6421da177e4SLinus Torvalds 6431da177e4SLinus Torvalds3: 64425985edcSLucas De Marchi /* Error occurred on load or store */ 6451da177e4SLinus Torvalds /* Free lock */ 6461da177e4SLinus Torvalds stw %r20, 0(%sr2,%r20) 647b5e8b733SAurelien Jarno#if ENABLE_LWS_DEBUG 6481da177e4SLinus Torvalds stw %r0, 4(%sr2,%r20) 6491da177e4SLinus Torvalds#endif 650c776cd89SJohn David Anglin ssm PSW_SM_I, %r0 6511da177e4SLinus Torvalds b lws_exit 6521da177e4SLinus Torvalds ldo -EFAULT(%r0),%r21 /* set errno */ 6531da177e4SLinus Torvalds nop 6541da177e4SLinus Torvalds nop 6551da177e4SLinus Torvalds nop 6561da177e4SLinus Torvalds nop 6571da177e4SLinus Torvalds 6581da177e4SLinus Torvalds /* Two exception table entries, one for the load, 6591da177e4SLinus Torvalds the other for the store. Either return -EFAULT. 6601da177e4SLinus Torvalds Each of the entries must be relocated. */ 66161dbbaebSHelge Deller ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 3b-linux_gateway_page) 66261dbbaebSHelge Deller ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 3b-linux_gateway_page) 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds 66589206491SGuy Martin /*************************************************** 66689206491SGuy Martin New CAS implementation which uses pointers and variable size 66789206491SGuy Martin information. The value pointed by old and new MUST NOT change 66889206491SGuy Martin while performing CAS. The lock only protect the value at %r26. 66989206491SGuy Martin 67089206491SGuy Martin %r26 - Address to examine 67189206491SGuy Martin %r25 - Pointer to the value to check (old) 67289206491SGuy Martin %r24 - Pointer to the value to set (new) 67389206491SGuy Martin %r23 - Size of the variable (0/1/2/3 for 8/16/32/64 bit) 67489206491SGuy Martin %r28 - Return non-zero on failure 67589206491SGuy Martin %r21 - Kernel error code 67689206491SGuy Martin 67789206491SGuy Martin %r21 has the following meanings: 67889206491SGuy Martin 67989206491SGuy Martin EAGAIN - CAS is busy, ldcw failed, try again. 68089206491SGuy Martin EFAULT - Read or write failed. 68189206491SGuy Martin 68289206491SGuy Martin Scratch: r20, r22, r28, r29, r1, fr4 (32bit for 64bit CAS only) 68389206491SGuy Martin 68489206491SGuy Martin ****************************************************/ 68589206491SGuy Martin 68689206491SGuy Martin /* ELF32 Process entry path */ 68789206491SGuy Martinlws_compare_and_swap_2: 68889206491SGuy Martin#ifdef CONFIG_64BIT 68989206491SGuy Martin /* Clip the input registers */ 69089206491SGuy Martin depdi 0, 31, 32, %r26 69189206491SGuy Martin depdi 0, 31, 32, %r25 69289206491SGuy Martin depdi 0, 31, 32, %r24 69389206491SGuy Martin depdi 0, 31, 32, %r23 69489206491SGuy Martin#endif 69589206491SGuy Martin 69689206491SGuy Martin /* Check the validity of the size pointer */ 69789206491SGuy Martin subi,>>= 4, %r23, %r0 69889206491SGuy Martin b,n lws_exit_nosys 69989206491SGuy Martin 70089206491SGuy Martin /* Jump to the functions which will load the old and new values into 70189206491SGuy Martin registers depending on the their size */ 70289206491SGuy Martin shlw %r23, 2, %r29 70389206491SGuy Martin blr %r29, %r0 70489206491SGuy Martin nop 70589206491SGuy Martin 70689206491SGuy Martin /* 8bit load */ 70789206491SGuy Martin4: ldb 0(%sr3,%r25), %r25 70889206491SGuy Martin b cas2_lock_start 70989206491SGuy Martin5: ldb 0(%sr3,%r24), %r24 71089206491SGuy Martin nop 71189206491SGuy Martin nop 71289206491SGuy Martin nop 71389206491SGuy Martin nop 71489206491SGuy Martin nop 71589206491SGuy Martin 71689206491SGuy Martin /* 16bit load */ 71789206491SGuy Martin6: ldh 0(%sr3,%r25), %r25 71889206491SGuy Martin b cas2_lock_start 71989206491SGuy Martin7: ldh 0(%sr3,%r24), %r24 72089206491SGuy Martin nop 72189206491SGuy Martin nop 72289206491SGuy Martin nop 72389206491SGuy Martin nop 72489206491SGuy Martin nop 72589206491SGuy Martin 72689206491SGuy Martin /* 32bit load */ 72789206491SGuy Martin8: ldw 0(%sr3,%r25), %r25 72889206491SGuy Martin b cas2_lock_start 72989206491SGuy Martin9: ldw 0(%sr3,%r24), %r24 73089206491SGuy Martin nop 73189206491SGuy Martin nop 73289206491SGuy Martin nop 73389206491SGuy Martin nop 73489206491SGuy Martin nop 73589206491SGuy Martin 73689206491SGuy Martin /* 64bit load */ 73789206491SGuy Martin#ifdef CONFIG_64BIT 73889206491SGuy Martin10: ldd 0(%sr3,%r25), %r25 73989206491SGuy Martin11: ldd 0(%sr3,%r24), %r24 74089206491SGuy Martin#else 74189206491SGuy Martin /* Load new value into r22/r23 - high/low */ 74289206491SGuy Martin10: ldw 0(%sr3,%r25), %r22 74389206491SGuy Martin11: ldw 4(%sr3,%r25), %r23 74489206491SGuy Martin /* Load new value into fr4 for atomic store later */ 74589206491SGuy Martin12: flddx 0(%sr3,%r24), %fr4 74689206491SGuy Martin#endif 74789206491SGuy Martin 74889206491SGuy Martincas2_lock_start: 74989206491SGuy Martin /* Load start of lock table */ 75089206491SGuy Martin ldil L%lws_lock_start, %r20 75189206491SGuy Martin ldo R%lws_lock_start(%r20), %r28 75289206491SGuy Martin 75389206491SGuy Martin /* Extract four bits from r26 and hash lock (Bits 4-7) */ 75489206491SGuy Martin extru %r26, 27, 4, %r20 75589206491SGuy Martin 75689206491SGuy Martin /* Find lock to use, the hash is either one of 0 to 75789206491SGuy Martin 15, multiplied by 16 (keep it 16-byte aligned) 75889206491SGuy Martin and add to the lock table offset. */ 75989206491SGuy Martin shlw %r20, 4, %r20 76089206491SGuy Martin add %r20, %r28, %r20 76189206491SGuy Martin 76289206491SGuy Martin rsm PSW_SM_I, %r0 /* Disable interrupts */ 76389206491SGuy Martin /* COW breaks can cause contention on UP systems */ 76489206491SGuy Martin LDCW 0(%sr2,%r20), %r28 /* Try to acquire the lock */ 76589206491SGuy Martin cmpb,<>,n %r0, %r28, cas2_action /* Did we get it? */ 76689206491SGuy Martincas2_wouldblock: 76789206491SGuy Martin ldo 2(%r0), %r28 /* 2nd case */ 76889206491SGuy Martin ssm PSW_SM_I, %r0 76989206491SGuy Martin b lws_exit /* Contended... */ 77089206491SGuy Martin ldo -EAGAIN(%r0), %r21 /* Spin in userspace */ 77189206491SGuy Martin 77289206491SGuy Martin /* 77389206491SGuy Martin prev = *addr; 77489206491SGuy Martin if ( prev == old ) 77589206491SGuy Martin *addr = new; 77689206491SGuy Martin return prev; 77789206491SGuy Martin */ 77889206491SGuy Martin 77989206491SGuy Martin /* NOTES: 78089206491SGuy Martin This all works becuse intr_do_signal 78189206491SGuy Martin and schedule both check the return iasq 78289206491SGuy Martin and see that we are on the kernel page 78389206491SGuy Martin so this process is never scheduled off 78489206491SGuy Martin or is ever sent any signal of any sort, 78589206491SGuy Martin thus it is wholly atomic from usrspaces 78689206491SGuy Martin perspective 78789206491SGuy Martin */ 78889206491SGuy Martincas2_action: 78989206491SGuy Martin /* Jump to the correct function */ 79089206491SGuy Martin blr %r29, %r0 79189206491SGuy Martin /* Set %r28 as non-zero for now */ 79289206491SGuy Martin ldo 1(%r0),%r28 79389206491SGuy Martin 79489206491SGuy Martin /* 8bit CAS */ 79589206491SGuy Martin13: ldb,ma 0(%sr3,%r26), %r29 79689206491SGuy Martin sub,= %r29, %r25, %r0 79789206491SGuy Martin b,n cas2_end 79889206491SGuy Martin14: stb,ma %r24, 0(%sr3,%r26) 79989206491SGuy Martin b cas2_end 80089206491SGuy Martin copy %r0, %r28 80189206491SGuy Martin nop 80289206491SGuy Martin nop 80389206491SGuy Martin 80489206491SGuy Martin /* 16bit CAS */ 80589206491SGuy Martin15: ldh,ma 0(%sr3,%r26), %r29 80689206491SGuy Martin sub,= %r29, %r25, %r0 80789206491SGuy Martin b,n cas2_end 80889206491SGuy Martin16: sth,ma %r24, 0(%sr3,%r26) 80989206491SGuy Martin b cas2_end 81089206491SGuy Martin copy %r0, %r28 81189206491SGuy Martin nop 81289206491SGuy Martin nop 81389206491SGuy Martin 81489206491SGuy Martin /* 32bit CAS */ 81589206491SGuy Martin17: ldw,ma 0(%sr3,%r26), %r29 81689206491SGuy Martin sub,= %r29, %r25, %r0 81789206491SGuy Martin b,n cas2_end 81889206491SGuy Martin18: stw,ma %r24, 0(%sr3,%r26) 81989206491SGuy Martin b cas2_end 82089206491SGuy Martin copy %r0, %r28 82189206491SGuy Martin nop 82289206491SGuy Martin nop 82389206491SGuy Martin 82489206491SGuy Martin /* 64bit CAS */ 82589206491SGuy Martin#ifdef CONFIG_64BIT 82689206491SGuy Martin19: ldd,ma 0(%sr3,%r26), %r29 8271b59ddfcSJohn David Anglin sub,*= %r29, %r25, %r0 82889206491SGuy Martin b,n cas2_end 82989206491SGuy Martin20: std,ma %r24, 0(%sr3,%r26) 83089206491SGuy Martin copy %r0, %r28 83189206491SGuy Martin#else 83289206491SGuy Martin /* Compare first word */ 83389206491SGuy Martin19: ldw,ma 0(%sr3,%r26), %r29 83489206491SGuy Martin sub,= %r29, %r22, %r0 83589206491SGuy Martin b,n cas2_end 83689206491SGuy Martin /* Compare second word */ 83789206491SGuy Martin20: ldw,ma 4(%sr3,%r26), %r29 83889206491SGuy Martin sub,= %r29, %r23, %r0 83989206491SGuy Martin b,n cas2_end 84089206491SGuy Martin /* Perform the store */ 84189206491SGuy Martin21: fstdx %fr4, 0(%sr3,%r26) 84289206491SGuy Martin copy %r0, %r28 84389206491SGuy Martin#endif 84489206491SGuy Martin 84589206491SGuy Martincas2_end: 84689206491SGuy Martin /* Free lock */ 84789206491SGuy Martin stw,ma %r20, 0(%sr2,%r20) 84889206491SGuy Martin /* Enable interrupts */ 84989206491SGuy Martin ssm PSW_SM_I, %r0 85089206491SGuy Martin /* Return to userspace, set no error */ 85189206491SGuy Martin b lws_exit 85289206491SGuy Martin copy %r0, %r21 85389206491SGuy Martin 85489206491SGuy Martin22: 85589206491SGuy Martin /* Error occurred on load or store */ 85689206491SGuy Martin /* Free lock */ 85789206491SGuy Martin stw %r20, 0(%sr2,%r20) 85889206491SGuy Martin ssm PSW_SM_I, %r0 85989206491SGuy Martin ldo 1(%r0),%r28 86089206491SGuy Martin b lws_exit 86189206491SGuy Martin ldo -EFAULT(%r0),%r21 /* set errno */ 86289206491SGuy Martin nop 86389206491SGuy Martin nop 86489206491SGuy Martin nop 86589206491SGuy Martin 86689206491SGuy Martin /* Exception table entries, for the load and store, return EFAULT. 86789206491SGuy Martin Each of the entries must be relocated. */ 86889206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 22b-linux_gateway_page) 86989206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 22b-linux_gateway_page) 87089206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 22b-linux_gateway_page) 87189206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 22b-linux_gateway_page) 87289206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(8b-linux_gateway_page, 22b-linux_gateway_page) 87389206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 22b-linux_gateway_page) 87489206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 22b-linux_gateway_page) 87589206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 22b-linux_gateway_page) 87689206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(13b-linux_gateway_page, 22b-linux_gateway_page) 87789206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 22b-linux_gateway_page) 87889206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 22b-linux_gateway_page) 87989206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 22b-linux_gateway_page) 88089206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(17b-linux_gateway_page, 22b-linux_gateway_page) 88189206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(18b-linux_gateway_page, 22b-linux_gateway_page) 88289206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(19b-linux_gateway_page, 22b-linux_gateway_page) 88389206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(20b-linux_gateway_page, 22b-linux_gateway_page) 88489206491SGuy Martin#ifndef CONFIG_64BIT 88589206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 22b-linux_gateway_page) 88689206491SGuy Martin ASM_EXCEPTIONTABLE_ENTRY(21b-linux_gateway_page, 22b-linux_gateway_page) 88789206491SGuy Martin#endif 88889206491SGuy Martin 8891da177e4SLinus Torvalds /* Make sure nothing else is placed on this page */ 8901c593571SSam Ravnborg .align PAGE_SIZE 8918e9e9844SHelge DellerEND(linux_gateway_page) 8928e9e9844SHelge DellerENTRY(end_linux_gateway_page) 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds /* Relocate symbols assuming linux_gateway_page is mapped 8951da177e4SLinus Torvalds to virtual address 0x0 */ 8968e9e9844SHelge Deller 8970b3d643fSHelge Deller#define LWS_ENTRY(_name_) ASM_ULONG_INSN (lws_##_name_ - linux_gateway_page) 8981da177e4SLinus Torvalds 8991bcdd854SHelge Deller .section .rodata,"a" 9001bcdd854SHelge Deller 9016a45716aSHelge Deller .align 8 9021da177e4SLinus Torvalds /* Light-weight-syscall table */ 9031da177e4SLinus Torvalds /* Start of lws table. */ 9048e9e9844SHelge DellerENTRY(lws_table) 90589206491SGuy Martin LWS_ENTRY(compare_and_swap32) /* 0 - ELF32 Atomic 32bit CAS */ 90689206491SGuy Martin LWS_ENTRY(compare_and_swap64) /* 1 - ELF64 Atomic 32bit CAS */ 90789206491SGuy Martin LWS_ENTRY(compare_and_swap_2) /* 2 - ELF32 Atomic 64bit CAS */ 9088e9e9844SHelge DellerEND(lws_table) 9091da177e4SLinus Torvalds /* End of lws table */ 9101da177e4SLinus Torvalds 9116a45716aSHelge Deller .align 8 9128e9e9844SHelge DellerENTRY(sys_call_table) 9131da177e4SLinus Torvalds#include "syscall_table.S" 9148e9e9844SHelge DellerEND(sys_call_table) 9151da177e4SLinus Torvalds 916413059f2SGrant Grundler#ifdef CONFIG_64BIT 9176a45716aSHelge Deller .align 8 9188e9e9844SHelge DellerENTRY(sys_call_table64) 9191da177e4SLinus Torvalds#define SYSCALL_TABLE_64BIT 9201da177e4SLinus Torvalds#include "syscall_table.S" 9218e9e9844SHelge DellerEND(sys_call_table64) 9221da177e4SLinus Torvalds#endif 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds /* 9251da177e4SLinus Torvalds All light-weight-syscall atomic operations 9261da177e4SLinus Torvalds will use this set of locks 927c84c3a69SHelge Deller 928c84c3a69SHelge Deller NOTE: The lws_lock_start symbol must be 929c84c3a69SHelge Deller at least 16-byte aligned for safe use 930c84c3a69SHelge Deller with ldcw. 9311da177e4SLinus Torvalds */ 932dfcf753bSKyle McMartin .section .data 9336a45716aSHelge Deller .align L1_CACHE_BYTES 9348e9e9844SHelge DellerENTRY(lws_lock_start) 9351da177e4SLinus Torvalds /* lws locks */ 9361da177e4SLinus Torvalds .rept 16 9371da177e4SLinus Torvalds /* Keep locks aligned at 16-bytes */ 9381da177e4SLinus Torvalds .word 1 9391da177e4SLinus Torvalds .word 0 9401da177e4SLinus Torvalds .word 0 9411da177e4SLinus Torvalds .word 0 9421da177e4SLinus Torvalds .endr 9438e9e9844SHelge DellerEND(lws_lock_start) 9441da177e4SLinus Torvalds .previous 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds.end 9471da177e4SLinus Torvalds 9481da177e4SLinus Torvalds 949