xref: /openbmc/linux/arch/parisc/kernel/syscall.S (revision 8e9e9844)
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>
131da177e4SLinus Torvalds#include <asm/psw.h>
141da177e4SLinus Torvalds#include <asm/thread_info.h>
151da177e4SLinus Torvalds#include <asm/assembly.h>
161da177e4SLinus Torvalds#include <asm/processor.h>
171da177e4SLinus Torvalds
188e9e9844SHelge Deller#include <linux/linkage.h>
198e9e9844SHelge Deller
201da177e4SLinus Torvalds	/* We fill the empty parts of the gateway page with
211da177e4SLinus Torvalds 	 * something that will kill the kernel or a
221da177e4SLinus Torvalds 	 * userspace application.
231da177e4SLinus Torvalds	 */
241da177e4SLinus Torvalds#define KILL_INSN	break	0,0
251da177e4SLinus Torvalds
26413059f2SGrant Grundler#ifdef CONFIG_64BIT
271da177e4SLinus Torvalds	.level          2.0w
281da177e4SLinus Torvalds#else
291da177e4SLinus Torvalds	.level		1.1
301da177e4SLinus Torvalds#endif
311da177e4SLinus Torvalds
328e9e9844SHelge Deller/* on 64bit pad to 64bit values */
338e9e9844SHelge Deller#ifdef CONFIG_64BIT
348e9e9844SHelge Deller#define ULONG_WORD(x)	.word 0, x
358e9e9844SHelge Deller#else
368e9e9844SHelge Deller#define ULONG_WORD(x)	.word x
378e9e9844SHelge Deller#endif
388e9e9844SHelge Deller
398e9e9844SHelge Deller
401da177e4SLinus Torvalds	.text
411da177e4SLinus Torvalds
421da177e4SLinus Torvalds	.import syscall_exit,code
431da177e4SLinus Torvalds	.import syscall_exit_rfi,code
441da177e4SLinus Torvalds
451da177e4SLinus Torvalds	/* Linux gateway page is aliased to virtual page 0 in the kernel
461da177e4SLinus Torvalds	 * address space. Since it is a gateway page it cannot be
471da177e4SLinus Torvalds	 * dereferenced, so null pointers will still fault. We start
481da177e4SLinus Torvalds	 * the actual entry point at 0x100. We put break instructions
491da177e4SLinus Torvalds	 * at the beginning of the page to trap null indirect function
501da177e4SLinus Torvalds	 * pointers.
511da177e4SLinus Torvalds	 */
521da177e4SLinus Torvalds
532fd83038SHelge Deller	.align ASM_PAGE_SIZE
548e9e9844SHelge DellerENTRY(linux_gateway_page)
551da177e4SLinus Torvalds
561da177e4SLinus Torvalds        /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */
571da177e4SLinus Torvalds	.rept 44
581da177e4SLinus Torvalds	KILL_INSN
591da177e4SLinus Torvalds	.endr
601da177e4SLinus Torvalds
611da177e4SLinus Torvalds	/* ADDRESS 0xb0 to 0xb4, lws uses 1 insns for entry */
621da177e4SLinus Torvalds	/* Light-weight-syscall entry must always be located at 0xb0 */
631da177e4SLinus Torvalds	/* WARNING: Keep this number updated with table size changes */
641da177e4SLinus Torvalds#define __NR_lws_entries (2)
651da177e4SLinus Torvalds
661da177e4SLinus Torvaldslws_entry:
671da177e4SLinus Torvalds	/* Unconditional branch to lws_start, located on the
681da177e4SLinus Torvalds	   same gateway page */
691da177e4SLinus Torvalds	b,n	lws_start
701da177e4SLinus Torvalds
711da177e4SLinus Torvalds	/* Fill from 0xb4 to 0xe0 */
721da177e4SLinus Torvalds	.rept 11
731da177e4SLinus Torvalds	KILL_INSN
741da177e4SLinus Torvalds	.endr
751da177e4SLinus Torvalds
761da177e4SLinus Torvalds	/* This function MUST be located at 0xe0 for glibc's threading
771da177e4SLinus Torvalds	mechanism to work. DO NOT MOVE THIS CODE EVER! */
781da177e4SLinus Torvaldsset_thread_pointer:
791da177e4SLinus Torvalds	gate	.+8, %r0		/* increase privilege */
801da177e4SLinus Torvalds	depi	3, 31, 2, %r31		/* Ensure we return into user mode. */
811da177e4SLinus Torvalds	be	0(%sr7,%r31)		/* return to user space */
821da177e4SLinus Torvalds	mtctl	%r26, %cr27		/* move arg0 to the control register */
831da177e4SLinus Torvalds
841da177e4SLinus Torvalds	/* Increase the chance of trapping if random jumps occur to this
851da177e4SLinus Torvalds	address, fill from 0xf0 to 0x100 */
861da177e4SLinus Torvalds	.rept 4
871da177e4SLinus Torvalds	KILL_INSN
881da177e4SLinus Torvalds	.endr
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds/* This address must remain fixed at 0x100 for glibc's syscalls to work */
911da177e4SLinus Torvalds	.align 256
921da177e4SLinus Torvaldslinux_gateway_entry:
931da177e4SLinus Torvalds	gate	.+8, %r0			/* become privileged */
941da177e4SLinus Torvalds	mtsp	%r0,%sr4			/* get kernel space into sr4 */
951da177e4SLinus Torvalds	mtsp	%r0,%sr5			/* get kernel space into sr5 */
961da177e4SLinus Torvalds	mtsp	%r0,%sr6			/* get kernel space into sr6 */
971da177e4SLinus Torvalds	mfsp    %sr7,%r1                        /* save user sr7 */
981da177e4SLinus Torvalds	mtsp    %r1,%sr3                        /* and store it in sr3 */
991da177e4SLinus Torvalds
100413059f2SGrant Grundler#ifdef CONFIG_64BIT
1011da177e4SLinus Torvalds	/* for now we can *always* set the W bit on entry to the syscall
1021da177e4SLinus Torvalds	 * since we don't support wide userland processes.  We could
1031da177e4SLinus Torvalds	 * also save the current SM other than in r0 and restore it on
1041da177e4SLinus Torvalds	 * exit from the syscall, and also use that value to know
1051da177e4SLinus Torvalds	 * whether to do narrow or wide syscalls. -PB
1061da177e4SLinus Torvalds	 */
1071da177e4SLinus Torvalds	ssm	PSW_SM_W, %r1
1081da177e4SLinus Torvalds	extrd,u	%r1,PSW_W_BIT,1,%r1
1091da177e4SLinus Torvalds	/* sp must be aligned on 4, so deposit the W bit setting into
1101da177e4SLinus Torvalds	 * the bottom of sp temporarily */
1111da177e4SLinus Torvalds	or,ev	%r1,%r30,%r30
1121da177e4SLinus Torvalds	b,n	1f
1131da177e4SLinus Torvalds	/* The top halves of argument registers must be cleared on syscall
1141da177e4SLinus Torvalds	 * entry from narrow executable.
1151da177e4SLinus Torvalds	 */
1161da177e4SLinus Torvalds	depdi	0, 31, 32, %r26
1171da177e4SLinus Torvalds	depdi	0, 31, 32, %r25
1181da177e4SLinus Torvalds	depdi	0, 31, 32, %r24
1191da177e4SLinus Torvalds	depdi	0, 31, 32, %r23
1201da177e4SLinus Torvalds	depdi	0, 31, 32, %r22
1211da177e4SLinus Torvalds	depdi	0, 31, 32, %r21
1221da177e4SLinus Torvalds1:
1231da177e4SLinus Torvalds#endif
1241da177e4SLinus Torvalds	mfctl   %cr30,%r1
1251da177e4SLinus Torvalds	xor     %r1,%r30,%r30                   /* ye olde xor trick */
1261da177e4SLinus Torvalds	xor     %r1,%r30,%r1
1271da177e4SLinus Torvalds	xor     %r1,%r30,%r30
1281da177e4SLinus Torvalds
1291da177e4SLinus Torvalds	ldo     THREAD_SZ_ALGN+FRAME_SIZE(%r30),%r30  /* set up kernel stack */
1301da177e4SLinus Torvalds
1311da177e4SLinus Torvalds	/* N.B.: It is critical that we don't set sr7 to 0 until r30
1321da177e4SLinus Torvalds	 *       contains a valid kernel stack pointer. It is also
1331da177e4SLinus Torvalds	 *       critical that we don't start using the kernel stack
1341da177e4SLinus Torvalds	 *       until after sr7 has been set to 0.
1351da177e4SLinus Torvalds	 */
1361da177e4SLinus Torvalds
1371da177e4SLinus Torvalds	mtsp	%r0,%sr7			/* get kernel space into sr7 */
1381da177e4SLinus Torvalds	STREGM	%r1,FRAME_SIZE(%r30)		/* save r1 (usp) here for now */
1391da177e4SLinus Torvalds	mfctl	%cr30,%r1			/* get task ptr in %r1 */
1401da177e4SLinus Torvalds	LDREG	TI_TASK(%r1),%r1
1411da177e4SLinus Torvalds
1421da177e4SLinus Torvalds	/* Save some registers for sigcontext and potential task
1431da177e4SLinus Torvalds	   switch (see entry.S for the details of which ones are
1441da177e4SLinus Torvalds	   saved/restored).  TASK_PT_PSW is zeroed so we can see whether
1451da177e4SLinus Torvalds	   a process is on a syscall or not.  For an interrupt the real
1461da177e4SLinus Torvalds	   PSW value is stored.  This is needed for gdb and sys_ptrace. */
1471da177e4SLinus Torvalds	STREG	%r0,  TASK_PT_PSW(%r1)
1481da177e4SLinus Torvalds	STREG	%r2,  TASK_PT_GR2(%r1)		/* preserve rp */
1491da177e4SLinus Torvalds	STREG	%r19, TASK_PT_GR19(%r1)
1501da177e4SLinus Torvalds
1511da177e4SLinus Torvalds	LDREGM	-FRAME_SIZE(%r30), %r2		/* get users sp back */
152413059f2SGrant Grundler#ifdef CONFIG_64BIT
1531da177e4SLinus Torvalds	extrd,u	%r2,63,1,%r19			/* W hidden in bottom bit */
1541da177e4SLinus Torvalds#if 0
1551da177e4SLinus Torvalds	xor	%r19,%r2,%r2			/* clear bottom bit */
1561da177e4SLinus Torvalds	depd,z	%r19,1,1,%r19
1571da177e4SLinus Torvalds	std	%r19,TASK_PT_PSW(%r1)
1581da177e4SLinus Torvalds#endif
1591da177e4SLinus Torvalds#endif
1601da177e4SLinus Torvalds	STREG	%r2,  TASK_PT_GR30(%r1)		/* ... and save it */
1611da177e4SLinus Torvalds
162aa0eecb0SCarlos O'Donell	STREG	%r20, TASK_PT_GR20(%r1)		/* Syscall number */
1631da177e4SLinus Torvalds	STREG	%r21, TASK_PT_GR21(%r1)
1641da177e4SLinus Torvalds	STREG	%r22, TASK_PT_GR22(%r1)
1651da177e4SLinus Torvalds	STREG	%r23, TASK_PT_GR23(%r1)		/* 4th argument */
1661da177e4SLinus Torvalds	STREG	%r24, TASK_PT_GR24(%r1)		/* 3rd argument */
1671da177e4SLinus Torvalds	STREG	%r25, TASK_PT_GR25(%r1)		/* 2nd argument */
1681da177e4SLinus Torvalds	STREG	%r26, TASK_PT_GR26(%r1)	 	/* 1st argument */
1691da177e4SLinus Torvalds	STREG	%r27, TASK_PT_GR27(%r1)		/* user dp */
1701da177e4SLinus Torvalds	STREG   %r28, TASK_PT_GR28(%r1)         /* return value 0 */
1711da177e4SLinus Torvalds	STREG   %r28, TASK_PT_ORIG_R28(%r1)     /* return value 0 (saved for signals) */
1721da177e4SLinus Torvalds	STREG	%r29, TASK_PT_GR29(%r1)		/* return value 1 */
1731da177e4SLinus Torvalds	STREG	%r31, TASK_PT_GR31(%r1)		/* preserve syscall return ptr */
1741da177e4SLinus Torvalds
1751da177e4SLinus Torvalds	ldo	TASK_PT_FR0(%r1), %r27		/* save fpregs from the kernel */
1761da177e4SLinus Torvalds	save_fp	%r27				/* or potential task switch  */
1771da177e4SLinus Torvalds
1781da177e4SLinus Torvalds	mfctl	%cr11, %r27			/* i.e. SAR */
1791da177e4SLinus Torvalds	STREG	%r27, TASK_PT_SAR(%r1)
1801da177e4SLinus Torvalds
1811da177e4SLinus Torvalds	loadgp
1821da177e4SLinus Torvalds
183413059f2SGrant Grundler#ifdef CONFIG_64BIT
1841da177e4SLinus Torvalds	ldo	-16(%r30),%r29			/* Reference param save area */
1851da177e4SLinus Torvalds	copy	%r19,%r2			/* W bit back to r2 */
1861da177e4SLinus Torvalds#else
1871da177e4SLinus Torvalds	/* no need to save these on stack in wide mode because the first 8
1881da177e4SLinus Torvalds	 * args are passed in registers */
1891da177e4SLinus Torvalds	stw     %r22, -52(%r30)                 /* 5th argument */
1901da177e4SLinus Torvalds	stw     %r21, -56(%r30)                 /* 6th argument */
1911da177e4SLinus Torvalds#endif
1921da177e4SLinus Torvalds
1931da177e4SLinus Torvalds	/* Are we being ptraced? */
1941da177e4SLinus Torvalds	mfctl	%cr30, %r1
1951da177e4SLinus Torvalds	LDREG	TI_TASK(%r1),%r1
1961da177e4SLinus Torvalds	LDREG	TASK_PTRACE(%r1), %r1
1971da177e4SLinus Torvalds	bb,<,n	%r1,31,.Ltracesys
1981da177e4SLinus Torvalds
1991da177e4SLinus Torvalds	/* Note!  We cannot use the syscall table that is mapped
2001da177e4SLinus Torvalds	nearby since the gateway page is mapped execute-only. */
2011da177e4SLinus Torvalds
202413059f2SGrant Grundler#ifdef CONFIG_64BIT
2031da177e4SLinus Torvalds	ldil	L%sys_call_table, %r1
2041da177e4SLinus Torvalds	or,=	%r2,%r2,%r2
2051da177e4SLinus Torvalds	addil	L%(sys_call_table64-sys_call_table), %r1
2061da177e4SLinus Torvalds	ldo	R%sys_call_table(%r1), %r19
2071da177e4SLinus Torvalds	or,=	%r2,%r2,%r2
2081da177e4SLinus Torvalds	ldo	R%sys_call_table64(%r1), %r19
2091da177e4SLinus Torvalds#else
2101da177e4SLinus Torvalds	ldil	L%sys_call_table, %r1
2111da177e4SLinus Torvalds	ldo     R%sys_call_table(%r1), %r19
2121da177e4SLinus Torvalds#endif
2131da177e4SLinus Torvalds	comiclr,>>=	__NR_Linux_syscalls, %r20, %r0
2141da177e4SLinus Torvalds	b,n	.Lsyscall_nosys
2151da177e4SLinus Torvalds
2161da177e4SLinus Torvalds	LDREGX  %r20(%r19), %r19
2171da177e4SLinus Torvalds
2181da177e4SLinus Torvalds	/* If this is a sys_rt_sigreturn call, and the signal was received
2191da177e4SLinus Torvalds	 * when not in_syscall, then we want to return via syscall_exit_rfi,
2201da177e4SLinus Torvalds	 * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
2211da177e4SLinus Torvalds	 * trampoline code in signal.c).
2221da177e4SLinus Torvalds	 */
2231da177e4SLinus Torvalds	ldi	__NR_rt_sigreturn,%r2
2241da177e4SLinus Torvalds	comb,=	%r2,%r20,.Lrt_sigreturn
2251da177e4SLinus Torvalds.Lin_syscall:
2261da177e4SLinus Torvalds	ldil	L%syscall_exit,%r2
2271da177e4SLinus Torvalds	be      0(%sr7,%r19)
2281da177e4SLinus Torvalds	ldo	R%syscall_exit(%r2),%r2
2291da177e4SLinus Torvalds.Lrt_sigreturn:
2301da177e4SLinus Torvalds	comib,<> 0,%r25,.Lin_syscall
2311da177e4SLinus Torvalds	ldil	L%syscall_exit_rfi,%r2
2321da177e4SLinus Torvalds	be      0(%sr7,%r19)
2331da177e4SLinus Torvalds	ldo	R%syscall_exit_rfi(%r2),%r2
2341da177e4SLinus Torvalds
2351da177e4SLinus Torvalds	/* Note!  Because we are not running where we were linked, any
2361da177e4SLinus Torvalds	calls to functions external to this file must be indirect.  To
2371da177e4SLinus Torvalds	be safe, we apply the opposite rule to functions within this
2381da177e4SLinus Torvalds	file, with local labels given to them to ensure correctness. */
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds.Lsyscall_nosys:
2411da177e4SLinus Torvaldssyscall_nosys:
2421da177e4SLinus Torvalds	ldil	L%syscall_exit,%r1
2431da177e4SLinus Torvalds	be	R%syscall_exit(%sr7,%r1)
2441da177e4SLinus Torvalds	ldo	-ENOSYS(%r0),%r28		   /* set errno */
2451da177e4SLinus Torvalds
2461da177e4SLinus Torvalds
2471da177e4SLinus Torvalds/* Warning! This trace code is a virtual duplicate of the code above so be
2481da177e4SLinus Torvalds * sure to maintain both! */
2491da177e4SLinus Torvalds.Ltracesys:
2501da177e4SLinus Torvaldstracesys:
2511da177e4SLinus Torvalds	/* Need to save more registers so the debugger can see where we
2521da177e4SLinus Torvalds	 * are.  This saves only the lower 8 bits of PSW, so that the C
2531da177e4SLinus Torvalds	 * bit is still clear on syscalls, and the D bit is set if this
2541da177e4SLinus Torvalds	 * full register save path has been executed.  We check the D
2551da177e4SLinus Torvalds	 * bit on syscall_return_rfi to determine which registers to
2561da177e4SLinus Torvalds	 * restore.  An interrupt results in a full PSW saved with the
2571da177e4SLinus Torvalds	 * C bit set, a non-straced syscall entry results in C and D clear
2581da177e4SLinus Torvalds	 * in the saved PSW.
2591da177e4SLinus Torvalds	 */
2601da177e4SLinus Torvalds	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
2611da177e4SLinus Torvalds	LDREG	TI_TASK(%r1), %r1
2621da177e4SLinus Torvalds	ssm	0,%r2
2631da177e4SLinus Torvalds	STREG	%r2,TASK_PT_PSW(%r1)		/* Lower 8 bits only!! */
2641da177e4SLinus Torvalds	mfsp	%sr0,%r2
2651da177e4SLinus Torvalds	STREG	%r2,TASK_PT_SR0(%r1)
2661da177e4SLinus Torvalds	mfsp	%sr1,%r2
2671da177e4SLinus Torvalds	STREG	%r2,TASK_PT_SR1(%r1)
2681da177e4SLinus Torvalds	mfsp	%sr2,%r2
2691da177e4SLinus Torvalds	STREG	%r2,TASK_PT_SR2(%r1)
2701da177e4SLinus Torvalds	mfsp	%sr3,%r2
2711da177e4SLinus Torvalds	STREG	%r2,TASK_PT_SR3(%r1)
2721da177e4SLinus Torvalds	STREG	%r2,TASK_PT_SR4(%r1)
2731da177e4SLinus Torvalds	STREG	%r2,TASK_PT_SR5(%r1)
2741da177e4SLinus Torvalds	STREG	%r2,TASK_PT_SR6(%r1)
2751da177e4SLinus Torvalds	STREG	%r2,TASK_PT_SR7(%r1)
2761da177e4SLinus Torvalds	STREG	%r2,TASK_PT_IASQ0(%r1)
2771da177e4SLinus Torvalds	STREG	%r2,TASK_PT_IASQ1(%r1)
2781da177e4SLinus Torvalds	LDREG	TASK_PT_GR31(%r1),%r2
2791da177e4SLinus Torvalds	STREG	%r2,TASK_PT_IAOQ0(%r1)
2801da177e4SLinus Torvalds	ldo	4(%r2),%r2
2811da177e4SLinus Torvalds	STREG	%r2,TASK_PT_IAOQ1(%r1)
2821da177e4SLinus Torvalds	ldo	TASK_REGS(%r1),%r2
2831da177e4SLinus Torvalds	/* reg_save %r2 */
2841da177e4SLinus Torvalds	STREG	%r3,PT_GR3(%r2)
2851da177e4SLinus Torvalds	STREG	%r4,PT_GR4(%r2)
2861da177e4SLinus Torvalds	STREG	%r5,PT_GR5(%r2)
2871da177e4SLinus Torvalds	STREG	%r6,PT_GR6(%r2)
2881da177e4SLinus Torvalds	STREG	%r7,PT_GR7(%r2)
2891da177e4SLinus Torvalds	STREG	%r8,PT_GR8(%r2)
2901da177e4SLinus Torvalds	STREG	%r9,PT_GR9(%r2)
2911da177e4SLinus Torvalds	STREG	%r10,PT_GR10(%r2)
2921da177e4SLinus Torvalds	STREG	%r11,PT_GR11(%r2)
2931da177e4SLinus Torvalds	STREG	%r12,PT_GR12(%r2)
2941da177e4SLinus Torvalds	STREG	%r13,PT_GR13(%r2)
2951da177e4SLinus Torvalds	STREG	%r14,PT_GR14(%r2)
2961da177e4SLinus Torvalds	STREG	%r15,PT_GR15(%r2)
2971da177e4SLinus Torvalds	STREG	%r16,PT_GR16(%r2)
2981da177e4SLinus Torvalds	STREG	%r17,PT_GR17(%r2)
2991da177e4SLinus Torvalds	STREG	%r18,PT_GR18(%r2)
3001da177e4SLinus Torvalds	/* Finished saving things for the debugger */
3011da177e4SLinus Torvalds
3021da177e4SLinus Torvalds	ldil	L%syscall_trace,%r1
3031da177e4SLinus Torvalds	ldil	L%tracesys_next,%r2
3041da177e4SLinus Torvalds	be	R%syscall_trace(%sr7,%r1)
3051da177e4SLinus Torvalds	ldo	R%tracesys_next(%r2),%r2
3061da177e4SLinus Torvalds
3071da177e4SLinus Torvaldstracesys_next:
3081da177e4SLinus Torvalds	ldil	L%sys_call_table,%r1
3091da177e4SLinus Torvalds	ldo     R%sys_call_table(%r1), %r19
3101da177e4SLinus Torvalds
3111da177e4SLinus Torvalds	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
3121da177e4SLinus Torvalds	LDREG	TI_TASK(%r1), %r1
3131da177e4SLinus Torvalds	LDREG   TASK_PT_GR20(%r1), %r20
3141da177e4SLinus Torvalds	LDREG   TASK_PT_GR26(%r1), %r26		/* Restore the users args */
3151da177e4SLinus Torvalds	LDREG   TASK_PT_GR25(%r1), %r25
3161da177e4SLinus Torvalds	LDREG   TASK_PT_GR24(%r1), %r24
3171da177e4SLinus Torvalds	LDREG   TASK_PT_GR23(%r1), %r23
318413059f2SGrant Grundler#ifdef CONFIG_64BIT
3191da177e4SLinus Torvalds	LDREG   TASK_PT_GR22(%r1), %r22
3201da177e4SLinus Torvalds	LDREG   TASK_PT_GR21(%r1), %r21
3211da177e4SLinus Torvalds	ldo	-16(%r30),%r29			/* Reference param save area */
3221da177e4SLinus Torvalds#endif
3231da177e4SLinus Torvalds
3241da177e4SLinus Torvalds	comiclr,>>=	__NR_Linux_syscalls, %r20, %r0
3251da177e4SLinus Torvalds	b,n	.Lsyscall_nosys
3261da177e4SLinus Torvalds
3271da177e4SLinus Torvalds	LDREGX  %r20(%r19), %r19
3281da177e4SLinus Torvalds
3291da177e4SLinus Torvalds	/* If this is a sys_rt_sigreturn call, and the signal was received
3301da177e4SLinus Torvalds	 * when not in_syscall, then we want to return via syscall_exit_rfi,
3311da177e4SLinus Torvalds	 * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
3321da177e4SLinus Torvalds	 * trampoline code in signal.c).
3331da177e4SLinus Torvalds	 */
3341da177e4SLinus Torvalds	ldi	__NR_rt_sigreturn,%r2
3351da177e4SLinus Torvalds	comb,=	%r2,%r20,.Ltrace_rt_sigreturn
3361da177e4SLinus Torvalds.Ltrace_in_syscall:
3371da177e4SLinus Torvalds	ldil	L%tracesys_exit,%r2
3381da177e4SLinus Torvalds	be      0(%sr7,%r19)
3391da177e4SLinus Torvalds	ldo	R%tracesys_exit(%r2),%r2
3401da177e4SLinus Torvalds
3411da177e4SLinus Torvalds	/* Do *not* call this function on the gateway page, because it
3421da177e4SLinus Torvalds	makes a direct call to syscall_trace. */
3431da177e4SLinus Torvalds
3441da177e4SLinus Torvaldstracesys_exit:
3451da177e4SLinus Torvalds	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
3461da177e4SLinus Torvalds	LDREG	TI_TASK(%r1), %r1
347413059f2SGrant Grundler#ifdef CONFIG_64BIT
3481da177e4SLinus Torvalds	ldo	-16(%r30),%r29			/* Reference param save area */
3491da177e4SLinus Torvalds#endif
3501da177e4SLinus Torvalds	bl	syscall_trace, %r2
3511da177e4SLinus Torvalds	STREG   %r28,TASK_PT_GR28(%r1)          /* save return value now */
3521da177e4SLinus Torvalds	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
3531da177e4SLinus Torvalds	LDREG	TI_TASK(%r1), %r1
3541da177e4SLinus Torvalds	LDREG   TASK_PT_GR28(%r1), %r28		/* Restore return val. */
3551da177e4SLinus Torvalds
3561da177e4SLinus Torvalds	ldil	L%syscall_exit,%r1
3571da177e4SLinus Torvalds	be,n	R%syscall_exit(%sr7,%r1)
3581da177e4SLinus Torvalds
3591da177e4SLinus Torvalds.Ltrace_rt_sigreturn:
3601da177e4SLinus Torvalds	comib,<> 0,%r25,.Ltrace_in_syscall
3611da177e4SLinus Torvalds	ldil	L%tracesys_sigexit,%r2
3621da177e4SLinus Torvalds	be      0(%sr7,%r19)
3631da177e4SLinus Torvalds	ldo	R%tracesys_sigexit(%r2),%r2
3641da177e4SLinus Torvalds
3651da177e4SLinus Torvaldstracesys_sigexit:
3661da177e4SLinus Torvalds	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
3671da177e4SLinus Torvalds	LDREG	0(%r1), %r1
368413059f2SGrant Grundler#ifdef CONFIG_64BIT
3691da177e4SLinus Torvalds	ldo	-16(%r30),%r29			/* Reference param save area */
3701da177e4SLinus Torvalds#endif
3711da177e4SLinus Torvalds	bl	syscall_trace, %r2
3721da177e4SLinus Torvalds	nop
3731da177e4SLinus Torvalds
3741da177e4SLinus Torvalds	ldil	L%syscall_exit_rfi,%r1
3751da177e4SLinus Torvalds	be,n	R%syscall_exit_rfi(%sr7,%r1)
3761da177e4SLinus Torvalds
3771da177e4SLinus Torvalds
3781da177e4SLinus Torvalds	/*********************************************************
3791da177e4SLinus Torvalds		Light-weight-syscall code
3801da177e4SLinus Torvalds
3811da177e4SLinus Torvalds		r20 - lws number
3821da177e4SLinus Torvalds		r26,r25,r24,r23,r22 - Input registers
3831da177e4SLinus Torvalds		r28 - Function return register
3841da177e4SLinus Torvalds		r21 - Error code.
3851da177e4SLinus Torvalds
3861da177e4SLinus Torvalds		Scracth: Any of the above that aren't being
3871da177e4SLinus Torvalds		currently used, including r1.
3881da177e4SLinus Torvalds
3891da177e4SLinus Torvalds		Return pointer: r31 (Not usable)
3901da177e4SLinus Torvalds
3911da177e4SLinus Torvalds		Error codes returned by entry path:
3921da177e4SLinus Torvalds
3931da177e4SLinus Torvalds		ENOSYS - r20 was an invalid LWS number.
3941da177e4SLinus Torvalds
3951da177e4SLinus Torvalds	*********************************************************/
3961da177e4SLinus Torvaldslws_start:
3971da177e4SLinus Torvalds	/* Gate and ensure we return to userspace */
3981da177e4SLinus Torvalds	gate	.+8, %r0
3991da177e4SLinus Torvalds	depi	3, 31, 2, %r31	/* Ensure we return to userspace */
4001da177e4SLinus Torvalds
401413059f2SGrant Grundler#ifdef CONFIG_64BIT
4021da177e4SLinus Torvalds	/* FIXME: If we are a 64-bit kernel just
4031da177e4SLinus Torvalds	 *        turn this on unconditionally.
4041da177e4SLinus Torvalds	 */
4051da177e4SLinus Torvalds	ssm	PSW_SM_W, %r1
4061da177e4SLinus Torvalds	extrd,u	%r1,PSW_W_BIT,1,%r1
4071da177e4SLinus Torvalds	/* sp must be aligned on 4, so deposit the W bit setting into
4081da177e4SLinus Torvalds	 * the bottom of sp temporarily */
4091da177e4SLinus Torvalds	or,ev	%r1,%r30,%r30
4101da177e4SLinus Torvalds
4111da177e4SLinus Torvalds	/* Clip LWS number to a 32-bit value always */
4121da177e4SLinus Torvalds	depdi	0, 31, 32, %r20
4131da177e4SLinus Torvalds#endif
4141da177e4SLinus Torvalds
4151da177e4SLinus Torvalds        /* Is the lws entry number valid? */
4161da177e4SLinus Torvalds	comiclr,>>=	__NR_lws_entries, %r20, %r0
4171da177e4SLinus Torvalds	b,n	lws_exit_nosys
4181da177e4SLinus Torvalds
4191da177e4SLinus Torvalds	/* WARNING: Trashing sr2 and sr3 */
4201da177e4SLinus Torvalds	mfsp	%sr7,%r1			/* get userspace into sr3 */
4211da177e4SLinus Torvalds	mtsp	%r1,%sr3
4221da177e4SLinus Torvalds	mtsp	%r0,%sr2			/* get kernel space into sr2 */
4231da177e4SLinus Torvalds
4241da177e4SLinus Torvalds	/* Load table start */
4251da177e4SLinus Torvalds	ldil	L%lws_table, %r1
4261da177e4SLinus Torvalds	ldo	R%lws_table(%r1), %r28	/* Scratch use of r28 */
4271da177e4SLinus Torvalds	LDREGX	%r20(%sr2,r28), %r21	/* Scratch use of r21 */
4281da177e4SLinus Torvalds
4291da177e4SLinus Torvalds	/* Jump to lws, lws table pointers already relocated */
4301da177e4SLinus Torvalds	be,n	0(%sr2,%r21)
4311da177e4SLinus Torvalds
4321da177e4SLinus Torvaldslws_exit_nosys:
4331da177e4SLinus Torvalds	ldo	-ENOSYS(%r0),%r21		   /* set errno */
4341da177e4SLinus Torvalds	/* Fall through: Return to userspace */
4351da177e4SLinus Torvalds
4361da177e4SLinus Torvaldslws_exit:
437413059f2SGrant Grundler#ifdef CONFIG_64BIT
4381da177e4SLinus Torvalds	/* decide whether to reset the wide mode bit
4391da177e4SLinus Torvalds	 *
4401da177e4SLinus Torvalds	 * For a syscall, the W bit is stored in the lowest bit
4411da177e4SLinus Torvalds	 * of sp.  Extract it and reset W if it is zero */
4421da177e4SLinus Torvalds	extrd,u,*<>	%r30,63,1,%r1
4431da177e4SLinus Torvalds	rsm	PSW_SM_W, %r0
4441da177e4SLinus Torvalds	/* now reset the lowest bit of sp if it was set */
4451da177e4SLinus Torvalds	xor	%r30,%r1,%r30
4461da177e4SLinus Torvalds#endif
4471da177e4SLinus Torvalds	be,n	0(%sr3, %r31)
4481da177e4SLinus Torvalds
4491da177e4SLinus Torvalds
4501da177e4SLinus Torvalds
4511da177e4SLinus Torvalds	/***************************************************
4521da177e4SLinus Torvalds		Implementing CAS as an atomic operation:
4531da177e4SLinus Torvalds
4541da177e4SLinus Torvalds		%r26 - Address to examine
4551da177e4SLinus Torvalds		%r25 - Old value to check (old)
4561da177e4SLinus Torvalds		%r24 - New value to set (new)
4571da177e4SLinus Torvalds		%r28 - Return prev through this register.
4581da177e4SLinus Torvalds		%r21 - Kernel error code
4591da177e4SLinus Torvalds
4601da177e4SLinus Torvalds		If debugging is DISabled:
4611da177e4SLinus Torvalds
4621da177e4SLinus Torvalds		%r21 has the following meanings:
4631da177e4SLinus Torvalds
4641da177e4SLinus Torvalds		EAGAIN - CAS is busy, ldcw failed, try again.
4651da177e4SLinus Torvalds		EFAULT - Read or write failed.
4661da177e4SLinus Torvalds
4671da177e4SLinus Torvalds		If debugging is enabled:
4681da177e4SLinus Torvalds
4691da177e4SLinus Torvalds		EDEADLOCK - CAS called recursively.
4701da177e4SLinus Torvalds		EAGAIN && r28 == 1 - CAS is busy. Lock contended.
4711da177e4SLinus Torvalds		EAGAIN && r28 == 2 - CAS is busy. ldcw failed.
4721da177e4SLinus Torvalds		EFAULT - Read or write failed.
4731da177e4SLinus Torvalds
4741da177e4SLinus Torvalds		Scratch: r20, r28, r1
4751da177e4SLinus Torvalds
4761da177e4SLinus Torvalds	****************************************************/
4771da177e4SLinus Torvalds
4781da177e4SLinus Torvalds	/* Do not enable LWS debugging */
4791da177e4SLinus Torvalds#define ENABLE_LWS_DEBUG 0
4801da177e4SLinus Torvalds
4811da177e4SLinus Torvalds	/* ELF64 Process entry path */
4821da177e4SLinus Torvaldslws_compare_and_swap64:
483413059f2SGrant Grundler#ifdef CONFIG_64BIT
4841da177e4SLinus Torvalds	b,n	lws_compare_and_swap
4851da177e4SLinus Torvalds#else
4861da177e4SLinus Torvalds	/* If we are not a 64-bit kernel, then we don't
4871da177e4SLinus Torvalds	 * implement having 64-bit input registers
4881da177e4SLinus Torvalds	 */
4891da177e4SLinus Torvalds	b,n	lws_exit_nosys
4901da177e4SLinus Torvalds#endif
4911da177e4SLinus Torvalds
4921da177e4SLinus Torvalds	/* ELF32 Process entry path */
4931da177e4SLinus Torvaldslws_compare_and_swap32:
494413059f2SGrant Grundler#ifdef CONFIG_64BIT
4951da177e4SLinus Torvalds	/* Clip all the input registers */
4961da177e4SLinus Torvalds	depdi	0, 31, 32, %r26
4971da177e4SLinus Torvalds	depdi	0, 31, 32, %r25
4981da177e4SLinus Torvalds	depdi	0, 31, 32, %r24
4991da177e4SLinus Torvalds#endif
5001da177e4SLinus Torvalds
5011da177e4SLinus Torvaldslws_compare_and_swap:
5021da177e4SLinus Torvalds#ifdef CONFIG_SMP
5031da177e4SLinus Torvalds	/* Load start of lock table */
5041da177e4SLinus Torvalds	ldil	L%lws_lock_start, %r20
5051da177e4SLinus Torvalds	ldo	R%lws_lock_start(%r20), %r28
5061da177e4SLinus Torvalds
5071da177e4SLinus Torvalds	/* Extract four bits from r26 and hash lock (Bits 4-7) */
5081da177e4SLinus Torvalds	extru  %r26, 27, 4, %r20
5091da177e4SLinus Torvalds
5101da177e4SLinus Torvalds	/* Find lock to use, the hash is either one of 0 to
5111da177e4SLinus Torvalds	   15, multiplied by 16 (keep it 16-byte aligned)
5121da177e4SLinus Torvalds	   and add to the lock table offset. */
5131da177e4SLinus Torvalds	shlw	%r20, 4, %r20
5141da177e4SLinus Torvalds	add	%r20, %r28, %r20
5151da177e4SLinus Torvalds
5161da177e4SLinus Torvalds# ifdef ENABLE_LWS_DEBUG
5171da177e4SLinus Torvalds	/*
5181da177e4SLinus Torvalds		DEBUG, check for deadlock!
5191da177e4SLinus Torvalds		If the thread register values are the same
5201da177e4SLinus Torvalds		then we were the one that locked it last and
5211da177e4SLinus Torvalds		this is a recurisve call that will deadlock.
5221da177e4SLinus Torvalds		We *must* giveup this call and fail.
5231da177e4SLinus Torvalds	*/
5241da177e4SLinus Torvalds	ldw	4(%sr2,%r20), %r28			/* Load thread register */
525aa0eecb0SCarlos O'Donell	/* WARNING: If cr27 cycles to the same value we have problems */
5261da177e4SLinus Torvalds	mfctl	%cr27, %r21				/* Get current thread register */
5271da177e4SLinus Torvalds	cmpb,<>,n	%r21, %r28, cas_lock		/* Called recursive? */
5281da177e4SLinus Torvalds	b	lws_exit				/* Return error! */
5291da177e4SLinus Torvalds	ldo	-EDEADLOCK(%r0), %r21
5301da177e4SLinus Torvaldscas_lock:
5311da177e4SLinus Torvalds	cmpb,=,n	%r0, %r28, cas_nocontend	/* Is nobody using it? */
5321da177e4SLinus Torvalds	ldo	1(%r0), %r28				/* 1st case */
5331da177e4SLinus Torvalds	b	lws_exit				/* Contended... */
5341da177e4SLinus Torvalds	ldo	-EAGAIN(%r0), %r21			/* Spin in userspace */
5351da177e4SLinus Torvaldscas_nocontend:
5361da177e4SLinus Torvalds# endif
5371da177e4SLinus Torvalds/* ENABLE_LWS_DEBUG */
5381da177e4SLinus Torvalds
53964f49532SKyle McMartin	LDCW	0(%sr2,%r20), %r28			/* Try to acquire the lock */
5401da177e4SLinus Torvalds	cmpb,<>,n	%r0, %r28, cas_action		/* Did we get it? */
5411da177e4SLinus Torvaldscas_wouldblock:
5421da177e4SLinus Torvalds	ldo	2(%r0), %r28				/* 2nd case */
5431da177e4SLinus Torvalds	b	lws_exit				/* Contended... */
5441da177e4SLinus Torvalds	ldo	-EAGAIN(%r0), %r21			/* Spin in userspace */
5451da177e4SLinus Torvalds#endif
5461da177e4SLinus Torvalds/* CONFIG_SMP */
5471da177e4SLinus Torvalds
5481da177e4SLinus Torvalds	/*
5491da177e4SLinus Torvalds		prev = *addr;
5501da177e4SLinus Torvalds		if ( prev == old )
5511da177e4SLinus Torvalds		  *addr = new;
5521da177e4SLinus Torvalds		return prev;
5531da177e4SLinus Torvalds	*/
5541da177e4SLinus Torvalds
5551da177e4SLinus Torvalds	/* NOTES:
5561da177e4SLinus Torvalds		This all works becuse intr_do_signal
5571da177e4SLinus Torvalds		and schedule both check the return iasq
5581da177e4SLinus Torvalds		and see that we are on the kernel page
5591da177e4SLinus Torvalds		so this process is never scheduled off
5601da177e4SLinus Torvalds		or is ever sent any signal of any sort,
5611da177e4SLinus Torvalds		thus it is wholly atomic from usrspaces
5621da177e4SLinus Torvalds		perspective
5631da177e4SLinus Torvalds	*/
5641da177e4SLinus Torvaldscas_action:
5651da177e4SLinus Torvalds#if defined CONFIG_SMP && defined ENABLE_LWS_DEBUG
5661da177e4SLinus Torvalds	/* DEBUG */
5671da177e4SLinus Torvalds	mfctl	%cr27, %r1
5681da177e4SLinus Torvalds	stw	%r1, 4(%sr2,%r20)
5691da177e4SLinus Torvalds#endif
5701da177e4SLinus Torvalds	/* The load and store could fail */
5711da177e4SLinus Torvalds1:	ldw	0(%sr3,%r26), %r28
5721da177e4SLinus Torvalds	sub,<>	%r28, %r25, %r0
5731da177e4SLinus Torvalds2:	stw	%r24, 0(%sr3,%r26)
5741da177e4SLinus Torvalds#ifdef CONFIG_SMP
5751da177e4SLinus Torvalds	/* Free lock */
5761da177e4SLinus Torvalds	stw	%r20, 0(%sr2,%r20)
5771da177e4SLinus Torvalds# ifdef ENABLE_LWS_DEBUG
5781da177e4SLinus Torvalds	/* Clear thread register indicator */
5791da177e4SLinus Torvalds	stw	%r0, 4(%sr2,%r20)
5801da177e4SLinus Torvalds# endif
5811da177e4SLinus Torvalds#endif
5821da177e4SLinus Torvalds	/* Return to userspace, set no error */
5831da177e4SLinus Torvalds	b	lws_exit
5841da177e4SLinus Torvalds	copy	%r0, %r21
5851da177e4SLinus Torvalds
5861da177e4SLinus Torvalds3:
5871da177e4SLinus Torvalds	/* Error occured on load or store */
5881da177e4SLinus Torvalds#ifdef CONFIG_SMP
5891da177e4SLinus Torvalds	/* Free lock */
5901da177e4SLinus Torvalds	stw	%r20, 0(%sr2,%r20)
5911da177e4SLinus Torvalds# ifdef ENABLE_LWS_DEBUG
5921da177e4SLinus Torvalds	stw	%r0, 4(%sr2,%r20)
5931da177e4SLinus Torvalds# endif
5941da177e4SLinus Torvalds#endif
5951da177e4SLinus Torvalds	b	lws_exit
5961da177e4SLinus Torvalds	ldo	-EFAULT(%r0),%r21	/* set errno */
5971da177e4SLinus Torvalds	nop
5981da177e4SLinus Torvalds	nop
5991da177e4SLinus Torvalds	nop
6001da177e4SLinus Torvalds	nop
6011da177e4SLinus Torvalds
6021da177e4SLinus Torvalds	/* Two exception table entries, one for the load,
6031da177e4SLinus Torvalds	   the other for the store. Either return -EFAULT.
6041da177e4SLinus Torvalds	   Each of the entries must be relocated. */
6051da177e4SLinus Torvalds	.section __ex_table,"aw"
6068e9e9844SHelge Deller	ULONG_WORD(2b - linux_gateway_page)
6078e9e9844SHelge Deller	ULONG_WORD(3b - linux_gateway_page)
6081da177e4SLinus Torvalds	.previous
6091da177e4SLinus Torvalds
6101da177e4SLinus Torvalds	.section __ex_table,"aw"
6118e9e9844SHelge Deller	ULONG_WORD(1b - linux_gateway_page)
6128e9e9844SHelge Deller	ULONG_WORD(3b - linux_gateway_page)
6131da177e4SLinus Torvalds	.previous
6141da177e4SLinus Torvalds
6151da177e4SLinus Torvaldsend_compare_and_swap:
6161da177e4SLinus Torvalds
6171da177e4SLinus Torvalds	/* Make sure nothing else is placed on this page */
6182fd83038SHelge Deller	.align ASM_PAGE_SIZE
6198e9e9844SHelge DellerEND(linux_gateway_page)
6208e9e9844SHelge DellerENTRY(end_linux_gateway_page)
6211da177e4SLinus Torvalds
6221da177e4SLinus Torvalds	/* Relocate symbols assuming linux_gateway_page is mapped
6231da177e4SLinus Torvalds	   to virtual address 0x0 */
6248e9e9844SHelge Deller
6258e9e9844SHelge Deller#define LWS_ENTRY(_name_) ULONG_WORD(lws_##_name_ - linux_gateway_page)
6261da177e4SLinus Torvalds
6271bcdd854SHelge Deller	.section .rodata,"a"
6281bcdd854SHelge Deller
6292fd83038SHelge Deller	.align ASM_PAGE_SIZE
6301da177e4SLinus Torvalds	/* Light-weight-syscall table */
6311da177e4SLinus Torvalds	/* Start of lws table. */
6328e9e9844SHelge DellerENTRY(lws_table)
6331da177e4SLinus Torvalds	LWS_ENTRY(compare_and_swap32)	/* 0 - ELF32 Atomic compare and swap */
6341da177e4SLinus Torvalds	LWS_ENTRY(compare_and_swap64)	/* 1 - ELF64 Atomic compare and swap */
6358e9e9844SHelge DellerEND(lws_table)
6361da177e4SLinus Torvalds	/* End of lws table */
6371da177e4SLinus Torvalds
6382fd83038SHelge Deller	.align ASM_PAGE_SIZE
6398e9e9844SHelge DellerENTRY(sys_call_table)
6401da177e4SLinus Torvalds#include "syscall_table.S"
6418e9e9844SHelge DellerEND(sys_call_table)
6421da177e4SLinus Torvalds
643413059f2SGrant Grundler#ifdef CONFIG_64BIT
6442fd83038SHelge Deller	.align ASM_PAGE_SIZE
6458e9e9844SHelge DellerENTRY(sys_call_table64)
6461da177e4SLinus Torvalds#define SYSCALL_TABLE_64BIT
6471da177e4SLinus Torvalds#include "syscall_table.S"
6488e9e9844SHelge DellerEND(sys_call_table64)
6491da177e4SLinus Torvalds#endif
6501da177e4SLinus Torvalds
6511da177e4SLinus Torvalds#ifdef CONFIG_SMP
6521da177e4SLinus Torvalds	/*
6531da177e4SLinus Torvalds		All light-weight-syscall atomic operations
6541da177e4SLinus Torvalds		will use this set of locks
6551da177e4SLinus Torvalds	*/
6561da177e4SLinus Torvalds	.section .data
6571da177e4SLinus Torvalds	.align 4096
6588e9e9844SHelge DellerENTRY(lws_lock_start)
6591da177e4SLinus Torvalds	/* lws locks */
6601da177e4SLinus Torvalds	.align 16
6611da177e4SLinus Torvalds	.rept 16
6621da177e4SLinus Torvalds	/* Keep locks aligned at 16-bytes */
6631da177e4SLinus Torvalds	.word 1
6641da177e4SLinus Torvalds	.word 0
6651da177e4SLinus Torvalds	.word 0
6661da177e4SLinus Torvalds	.word 0
6671da177e4SLinus Torvalds	.endr
6688e9e9844SHelge DellerEND(lws_lock_start)
6691da177e4SLinus Torvalds	.previous
6701da177e4SLinus Torvalds#endif
6711da177e4SLinus Torvalds/* CONFIG_SMP for lws_lock_start */
6721da177e4SLinus Torvalds
6731da177e4SLinus Torvalds.end
6741da177e4SLinus Torvalds
6751da177e4SLinus Torvalds
676